# Реалізація інформаційного та програмного забезпечення

# SQL-скрипт для створення початкового наповнення бази даних

-- MySQL Script generated by MySQL Workbench
-- Sun 05 Nov 2023 02:19:32 PM EET
-- Model: New Model    Version: 1.0
-- MySQL Workbench Forward Engineering

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';

-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------

-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 ;
USE `mydb` ;

-- -----------------------------------------------------
-- Table `mydb`.`action`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`action` ;

CREATE TABLE IF NOT EXISTS `mydb`.`action` (
  `craeted_at` DATETIME NOT NULL,
  `state_id` INT NOT NULL,
  `media_request_id` INT NOT NULL,
  `source_id` INT NOT NULL,
  `user_id` INT NOT NULL,
  PRIMARY KEY (`state_id`, `media_request_id`, `source_id`, `user_id`),
  INDEX `fk_action_media_request1_idx` (`media_request_id` ASC) VISIBLE,
  INDEX `fk_action_source1_idx` (`source_id` ASC) VISIBLE,
  INDEX `fk_action_user1_idx` (`user_id` ASC) VISIBLE,
  CONSTRAINT `fk_action_state1`
    FOREIGN KEY (`state_id`)
    REFERENCES `mydb`.`state` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_action_media_request1`
    FOREIGN KEY (`media_request_id`)
    REFERENCES `mydb`.`media_request` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_action_source1`
    FOREIGN KEY (`source_id`)
    REFERENCES `mydb`.`source` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_action_user1`
    FOREIGN KEY (`user_id`)
    REFERENCES `mydb`.`user` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`based_on`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`based_on` ;

CREATE TABLE IF NOT EXISTS `mydb`.`based_on` (
  `source_id` INT NOT NULL,
  `media_request_id` INT NOT NULL,
  PRIMARY KEY (`source_id`, `media_request_id`),
  INDEX `fk_source_has_media_request_media_request1_idx` (`media_request_id` ASC) VISIBLE,
  INDEX `fk_source_has_media_request_source1_idx` (`source_id` ASC) VISIBLE,
  CONSTRAINT `fk_source_has_media_request_source1`
    FOREIGN KEY (`source_id`)
    REFERENCES `mydb`.`source` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_source_has_media_request_media_request1`
    FOREIGN KEY (`media_request_id`)
    REFERENCES `mydb`.`media_request` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`feedback`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`feedback` ;

CREATE TABLE IF NOT EXISTS `mydb`.`feedback` (
  `id` INT NOT NULL,
  `body` VARCHAR(255) NULL,
  `rating` FLOAT NOT NULL,
  `created_at` DATETIME NOT NULL,
  `updated_at` DATETIME NOT NULL,
  `media_request_id` INT NOT NULL,
  `user_id` INT NOT NULL,
  PRIMARY KEY (`id`, `user_id`, `media_request_id`),
  INDEX `fk_Feedback_MediaRequest1_idx` (`media_request_id` ASC) VISIBLE,
  INDEX `fk_Feedback_User1_idx` (`user_id` ASC) VISIBLE,
  CONSTRAINT `fk_Feedback_MediaRequest1`
    FOREIGN KEY (`media_request_id`)
    REFERENCES `mydb`.`media_request` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_Feedback_User1`
    FOREIGN KEY (`user_id`)
    REFERENCES `mydb`.`user` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`label`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`label` ;

CREATE TABLE IF NOT EXISTS `mydb`.`label` (
  `tag_id` INT NOT NULL,
  `source_id` INT NOT NULL,
  PRIMARY KEY (`tag_id`, `source_id`),
  INDEX `fk_tag_has_source_source1_idx` (`source_id` ASC) VISIBLE,
  INDEX `fk_tag_has_source_tag1_idx` (`tag_id` ASC) VISIBLE,
  CONSTRAINT `fk_tag_has_source_tag1`
    FOREIGN KEY (`tag_id`)
    REFERENCES `mydb`.`tag` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_tag_has_source_source1`
    FOREIGN KEY (`source_id`)
    REFERENCES `mydb`.`source` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`media_request`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`media_request` ;

CREATE TABLE IF NOT EXISTS `mydb`.`media_request` (
  `id` INT NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  `description` VARCHAR(255) NULL,
  `keywords` VARCHAR(255) NULL,
  `type` VARCHAR(255) NOT NULL,
  `created_at` DATETIME NOT NULL,
  `updated_at` DATETIME NOT NULL,
  `user_id` INT NOT NULL,
  `source_id` INT NOT NULL,
  PRIMARY KEY (`id`, `user_id`, `source_id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`permission`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`permission` ;

CREATE TABLE IF NOT EXISTS `mydb`.`permission` (
  `id` INT NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`role`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`role` ;

CREATE TABLE IF NOT EXISTS `mydb`.`role` (
  `id` INT NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  `description` VARCHAR(255) NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`role_has_permission`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`role_has_permission` ;

CREATE TABLE IF NOT EXISTS `mydb`.`role_has_permission` (
  `role_id` INT NOT NULL,
  `permission_id` INT NOT NULL,
  PRIMARY KEY (`role_id`, `permission_id`),
  INDEX `fk_Role_has_Permission_Permission1_idx` (`permission_id` ASC) VISIBLE,
  INDEX `fk_Role_has_Permission_Role1_idx` (`role_id` ASC) VISIBLE,
  CONSTRAINT `fk_Role_has_Permission_Role1`
    FOREIGN KEY (`role_id`)
    REFERENCES `mydb`.`role` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_Role_has_Permission_Permission1`
    FOREIGN KEY (`permission_id`)
    REFERENCES `mydb`.`permission` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`source`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`source` ;

CREATE TABLE IF NOT EXISTS `mydb`.`source` (
  `id` INT NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  `url` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`state`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`state` ;

CREATE TABLE IF NOT EXISTS `mydb`.`state` (
  `id` INT NOT NULL,
  `display_name` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`tag`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`tag` ;

CREATE TABLE IF NOT EXISTS `mydb`.`tag` (
  `id` INT NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`user`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`user` ;

CREATE TABLE IF NOT EXISTS `mydb`.`user` (
  `id` INT NOT NULL,
  `first_name` VARCHAR(255) NOT NULL,
  `last_name` VARCHAR(255) NOT NULL,
  `username` VARCHAR(255) NOT NULL,
  `email` VARCHAR(255) NOT NULL,
  `password` VARCHAR(255) NOT NULL,
  `role_id` INT NOT NULL,
  PRIMARY KEY (`id`, `role_id`),
  INDEX `fk_User_Role1_idx` (`role_id` ASC) VISIBLE,
  UNIQUE INDEX `username_UNIQUE` (`username` ASC) VISIBLE,
  UNIQUE INDEX `email_UNIQUE` (`email` ASC) VISIBLE,
  CONSTRAINT `fk_User_Role1`
    FOREIGN KEY (`role_id`)
    REFERENCES `mydb`.`role` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

-- -----------------------------------------------------
-- Data for table `mydb`.`permission`
-- -----------------------------------------------------
START TRANSACTION;
USE `mydb`;
INSERT INTO `mydb`.`permission` (`id`, `name`) VALUES (1, 'user.delete');
INSERT INTO `mydb`.`permission` (`id`, `name`) VALUES (2, 'user.role.promote');
INSERT INTO `mydb`.`permission` (`id`, `name`) VALUES (3, 'media.find');
INSERT INTO `mydb`.`permission` (`id`, `name`) VALUES (4, 'media.create');
INSERT INTO `mydb`.`permission` (`id`, `name`) VALUES (5, 'media.delete');
INSERT INTO `mydb`.`permission` (`id`, `name`) VALUES (6, 'media.edit');
INSERT INTO `mydb`.`permission` (`id`, `name`) VALUES (7, 'media.feedback.add');

COMMIT;


-- -----------------------------------------------------
-- Data for table `mydb`.`role`
-- -----------------------------------------------------
START TRANSACTION;
USE `mydb`;
INSERT INTO `mydb`.`role` (`id`, `name`, `description`) VALUES (1, 'User', 'An ordinary user of the system');
INSERT INTO `mydb`.`role` (`id`, `name`, `description`) VALUES (2, 'TechnicalExpert', 'Specialist in technical issues');

COMMIT;


-- -----------------------------------------------------
-- Data for table `mydb`.`role_has_permission`
-- -----------------------------------------------------
START TRANSACTION;
USE `mydb`;
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (2, 1);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (2, 2);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (1, 3);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (2, 3);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (1, 4);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (2, 4);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (1, 5);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (2, 5);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (1, 6);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (2, 6);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (1, 7);
INSERT INTO `mydb`.`role_has_permission` (`role_id`, `permission_id`) VALUES (2, 7);

COMMIT;


-- -----------------------------------------------------
-- Data for table `mydb`.`state`
-- -----------------------------------------------------
START TRANSACTION;
USE `mydb`;
INSERT INTO `mydb`.`state` (`id`, `display_name`) VALUES (1, 'Subscribe');
INSERT INTO `mydb`.`state` (`id`, `display_name`) VALUES (2, 'Unsubscribe');
INSERT INTO `mydb`.`state` (`id`, `display_name`) VALUES (3, 'Quarantine');

COMMIT;


-- -----------------------------------------------------
-- Data for table `mydb`.`tag`
-- -----------------------------------------------------
START TRANSACTION;
USE `mydb`;
INSERT INTO `mydb`.`tag` (`id`, `name`) VALUES (1, 'Sport');
INSERT INTO `mydb`.`tag` (`id`, `name`) VALUES (2, 'Science and Technology');
INSERT INTO `mydb`.`tag` (`id`, `name`) VALUES (3, 'Entertainment');
INSERT INTO `mydb`.`tag` (`id`, `name`) VALUES (4, 'Fashion and Style');
INSERT INTO `mydb`.`tag` (`id`, `name`) VALUES (5, 'Music');
INSERT INTO `mydb`.`tag` (`id`, `name`) VALUES (6, 'Food and Cooking');
INSERT INTO `mydb`.`tag` (`id`, `name`) VALUES (7, 'Tourism');
INSERT INTO `mydb`.`tag` (`id`, `name`) VALUES (8, 'Movies and Television');

COMMIT;

# RESTfull сервіс для управління даними

# Схема бази даних (ORM Prisma)

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model User {
  id            Int            @id @default(autoincrement())
  firstName     String         @map("first_name")
  lastName      String         @map("last_name")
  username      String         @unique
  email         String         @unique
  password      String
  role          Role           @relation(fields: [roleId], references: [id], onDelete: Cascade)
  roleId        Int            @map("role_id")
  actions       Action[]
  feedbacks     Feedback[]
  mediaRequests MediaRequest[]

  @@map("users")
}

enum RoleName {
  USER
  TECHNICAL_EXPERT
}

model Role {
  id          Int                 @id @default(autoincrement())
  name        RoleName
  description String?
  users       User[]
  permissions RoleHasPermission[]

  @@map("roles")
}

model RoleHasPermission {
  role         Role       @relation(fields: [roleId], references: [id], onDelete: Cascade)
  roleId       Int        @map("role_id")
  permission   Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
  permissionId Int        @map("permission_id")

  @@id([roleId, permissionId])
  @@map("role_has_permission")
}

model Permission {
  id    Int                 @id @default(autoincrement())
  name  String
  roles RoleHasPermission[]

  @@map("permissions")
}

model Feedback {
  id             Int          @id @default(autoincrement())
  body           String
  rating         Float
  user           User         @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId         Int          @map("user_id")
  mediaRequest   MediaRequest @relation(fields: [mediaRequestId], references: [id], onDelete: Cascade)
  mediaRequestId Int          @map("media_request)id")
  createdAt      DateTime     @default(now()) @map("created_at")
  updatedAt      DateTime     @updatedAt @map("updated_at")

  @@map("feedbacks")
}

model MediaRequest {
  id          Int        @id @default(autoincrement())
  name        String
  description String?
  keywords    String?
  type        String
  user        User       @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId      Int        @map("user_id")
  feedbacks   Feedback[]
  sources     BasedOn[]
  actions     Action[]
  createdAt   DateTime   @default(now()) @map("created_at")
  updatedAt   DateTime   @updatedAt @map("updated_at")

  @@map("media_requests")
}

model BasedOn {
  source         Source       @relation(fields: [sourceId], references: [id], onDelete: Cascade)
  sourceId       Int          @map("source_id")
  mediaRequest   MediaRequest @relation(fields: [mediaRequestId], references: [id], onDelete: Cascade)
  mediaRequestId Int          @map("media_request_id")

  @@id([sourceId, mediaRequestId])
  @@map("based_on")
}

model Source {
  id            Int       @id @default(autoincrement())
  name          String
  url           String
  mediaRequests BasedOn[]
  labels        Label[]
  actions       Action[]

  @@map("sources")
}

model Label {
  source   Source @relation(fields: [sourceId], references: [id], onDelete: Cascade)
  sourceId Int    @map("source_id")
  tag      Tag    @relation(fields: [tagId], references: [id], onDelete: Cascade)
  tagId    Int    @map("tag_id")

  @@id([sourceId, tagId])
  @@map("labels")
}

enum TagName {
  SPORT
  SCIENCE_AND_TECHOLOGY
  ENTERTAINMENT
  FASHION_AND_STYLE
  MUSIC
  FOOD_AND_COOKING
  TOURISM
  MOVIES_AND_TELEVISION
}

model Tag {
  id     Int     @id @default(autoincrement())
  name   TagName
  labels Label[]

  @@map("tags")
}

model Action {
  mediaRequest   MediaRequest @relation(fields: [mediaRequestId], references: [id], onDelete: Cascade)
  mediaRequestId Int          @map("media_request_id")
  source         Source       @relation(fields: [sourceId], references: [id], onDelete: Cascade)
  sourceId       Int          @map("source_id")
  user           User         @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId         Int          @map("user_id")
  state          State        @relation(fields: [stateId], references: [id], onDelete: Cascade)
  stateId        Int          @map("state_id")

  @@id([mediaRequestId, sourceId, userId, stateId])
  @@map("actions")
}

enum StateName {
  SUBSCRIBE
  UNSUBSCRIBE
  QUARANTINE
}

model State {
  id          Int       @id @default(autoincrement())
  displayName StateName @map("display_name")
  actions     Action[]

  @@map("states")
}

# Модуль та сервіс підключення до бази даних

import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Module({
  providers: [PrismaService],
  exports: [PrismaService]
})
export class PrismaModule {}
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }
}

# Модуль та контролер для отримання запитів

import { Module } from '@nestjs/common';
import { FeedbackController } from './feedback.controller';
import { FeedbackService } from './feedback.service';
import { PrismaModule } from '../prisma/prisma.module';

@Module({
  controllers: [FeedbackController],
  providers: [FeedbackService],
  imports: [PrismaModule],
})
export class FeedbackModule {}
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common';
import { CreateFeedbackDTO } from './create.feedback.dto';
import { FeedbackService } from './feedback.service';
import { FeedbackResponse } from './feedback.response';
import { UpdateFeedbackDTO } from './update.feedback.dto';
import { FeedbackPipe } from '../pipes/feedback.pipe';
import { FeedbackBodyPipe } from '../pipes/feedback.body.pipe';
import { ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';

@ApiTags('Feedback')
@Controller('/feedbacks')
export class FeedbackController {
  constructor(
    private feedbackService: FeedbackService,
  ) {}

  @ApiOkResponse({
    type: FeedbackResponse,
  })
  @ApiNotFoundResponse({
    description: `
      User was not found
      Media Request was not found`
  })
  @ApiOperation({
    summary: 'Create new feedback',
    description: 'Endpoint for creating a new feedback to a media request by the user'
  })
  @Post()
  async create (
    @Body(FeedbackBodyPipe) body: CreateFeedbackDTO,
  ): Promise<FeedbackResponse> {
    return this.feedbackService.create(body);
  }

  @ApiOkResponse({
    type: FeedbackResponse,
  })
  @ApiNotFoundResponse({
    description: `
      Feedback was not found
      User was not found
      Media Request was not found`
  })
  @ApiParam({
    name: 'id',
    type: Number,
    required: true,
    description: 'Id of existing feedback',
  })
  @ApiOperation({
    summary: 'Update an existing feedback',
    description: 'Endpoint for updating existing feedback'
  })
  @Patch('/:id')
  async update (
    @Param('id', ParseIntPipe) id: number,
    @Body(FeedbackBodyPipe) body: UpdateFeedbackDTO,
  ): Promise<FeedbackResponse> {
    return this.feedbackService.update(id, body);
  }

  @ApiOkResponse({
    type: FeedbackResponse,
  })
  @ApiNotFoundResponse({
    description: `
      Feedback was not found`
  })
  @ApiParam({
    name: 'id',
    type: Number,
    required: true,
    description: 'Id of existing feedback',
  })
  @ApiOperation({
    summary: 'Get an existing feedback',
    description: 'Endpoint for getting existing feedback'
  })
  @Get('/:id')
  async get (
    @Param('id', ParseIntPipe, FeedbackPipe) id: number,
  ): Promise<FeedbackResponse> {
    return this.feedbackService.get(id);
  }

  @ApiOkResponse({
    type: FeedbackResponse,
  })
  @ApiNotFoundResponse({
    description: `
      Feedback was not found`
  })
  @ApiParam({
    name: 'id',
    type: Number,
    required: true,
    description: 'Id of existing feedback',
  })
  @ApiOperation({
    summary: 'Delete an existing feedback',
    description: 'Endpoint for deleting existing feedback'
  })
  @Delete('/:id')
  async delete (
    @Param('id', ParseIntPipe) id: number,
  ): Promise<FeedbackResponse> {
    return this.feedbackService.delete(id);
  }
}

# Сервіс для обробки запитів

import { Injectable } from '@nestjs/common';
import { CreateFeedbackDTO } from './create.feedback.dto';
import { PrismaService } from '../prisma/prisma.service';
import { UpdateFeedbackDTO } from './update.feedback.dto';
import { FeedbackResponse } from './feedback.response';

@Injectable()
export class FeedbackService {
  constructor(
    private prisma: PrismaService,
  ) {}

  async create (data: CreateFeedbackDTO): Promise<FeedbackResponse> {
    return this.prisma.feedback.create({ data });
  }

  async update (id: number, data: UpdateFeedbackDTO): Promise<FeedbackResponse> {
    return this.prisma.feedback.update({
      where: { id },
      data,
    })
  }

  async get (id: number): Promise<FeedbackResponse> {
    return this.prisma.feedback.findUnique({
      where: { id },
    })
  }

  async delete (id: number): Promise<FeedbackResponse> {
    return this.prisma.feedback.delete({
      where: { id },
    })
  }
}

# DTO для створення відгуків

import { IsNotEmpty } from 'class-validator';
import { Transform } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';

export class CreateFeedbackDTO {
  @ApiProperty({
    description: 'Main text of the feedback',
  })
  @IsNotEmpty()
    body: string;

  @ApiProperty({
    description: 'Rating left by the user',
  })
  @IsNotEmpty()
    rating: number;

  @ApiProperty({
    description: 'The id of the user who provided the feedback'
  })
  @Transform(({ value }) => parseInt(value))
  @IsNotEmpty()
    userId: number;

  @ApiProperty({
    description: 'Id of the media request to which feedback was given'
  })
  @Transform(({ value }) => parseInt(value))
  @IsNotEmpty()
    mediaRequestId: number;
}

# DTO для оновлення відгуків

import { IsOptional } from 'class-validator';
import { Transform } from 'class-transformer';
import { ApiPropertyOptional } from '@nestjs/swagger';

export class UpdateFeedbackDTO {
  @ApiPropertyOptional({
    description: 'Main text of the feedback',
  })
  @IsOptional()
    body?: string;

  @ApiPropertyOptional({
    description: 'Rating left by the user',
  })
  @Transform(({ value }) => parseInt(value))
  @IsOptional()
    rating?: number;

  @ApiPropertyOptional({
    description: 'The id of the user who provided the feedback',
  })
  @Transform(({ value }) => parseInt(value))
  @IsOptional()
    userId?: number;

  @ApiPropertyOptional({
    description: 'Id of the media request to which feedback was given',
  })
  @IsOptional()
    mediaRequestId?: number;
}

# Exception та Pipes для валідації даних і обробки помилок клієнта

import { HttpException, HttpStatus } from '@nestjs/common';

export class EntityException extends HttpException {
  constructor(entity: string) {
    super(`${entity} was not found`, HttpStatus.NOT_FOUND);
  }
}
import { Injectable, PipeTransform } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { EntityException } from '../exceptions/entity.exception';

@Injectable()
export class FeedbackPipe implements PipeTransform {
  constructor(
    private prisma: PrismaService,
  ) {}

  async transform (id: number) {
    const feedback = await this.prisma.feedback.findUnique({ where: { id } });
    if (!feedback) throw new EntityException('Feedback');
    return id;
  }
}
import { Injectable, PipeTransform } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { EntityException } from '../exceptions/entity.exception';
import { UpdateFeedbackDTO } from '../feedback/update.feedback.dto';

@Injectable()
export class FeedbackBodyPipe implements PipeTransform {
  constructor(
    private prisma: PrismaService,
  ) {}

  async transform (body: UpdateFeedbackDTO) {
    if (body.userId) {
      const user = await this.prisma.user.findUnique({
        where: {
          id: body.userId,
        }
      });
      if (!user) throw new EntityException('User');
    }

    if (body.mediaRequestId) {
      const mediaRequest = await this.prisma.mediaRequest.findUnique({
        where: {
          id: body.mediaRequestId,
        }
      });
      if (!mediaRequest) throw new EntityException('Media Request');
    }

    return body;
  }
}

# App модуль та головний модуль програми

import { Module } from '@nestjs/common';
import { FeedbackModule } from './feedback/feedback.module';

@Module({
  imports: [FeedbackModule],
})
export class AppModule {}
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as process from 'process';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

const port = process.env.PORT;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('Media content analysis system')
    .setDescription('Restful service for analyzing media content')
    .setVersion('1.0')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(port, () => console.log(`Server started on 127.0.0.1:${port}`));
}
bootstrap();

# Документація Swagger

Документація доступна за шляхом <BASE_URL>/api

# POST /feedback



# PATCH /feedback/:id



# GET /feedback/:id



# DELETE /feedback/:id



Останнє оновлення: 11/9/2023, 8:19:27 PM