import { Injectable , BadRequestException , NotFoundException } from '@nestjs/common' ; import { InjectRepository } from '@nestjs/typeorm' ; import { Validator , validate } from 'class-validator' ; import { createReadStream } from 'fs' ; import { TitlesRepository } from 'src/shared/Repositories/title.repository' ; import { CreateTitleDto } from '../Dto/create-title.dto' ; import { TitlesEntity } from 'src/shared/Entities/titles.entity' ; import { CategoriesRepository } from 'src/shared/Repositories/categories.repository' ; import { UpdateTitleDto } from '../Dto/update-title.dto' ; import { serializerService , ISerializeResponse } from 'src/shared/Services/serializer.service' ; import { EntriesRepository } from 'src/shared/Repositories/entries.repository' ; import { UsersRepository } from 'src/shared/Repositories/users.repository' ; import { CategoriesEntity } from 'src/shared/Entities/categories.entity' ; import { AwsService } from 'src/shared/Services/aws.service' ; import { configService } from 'src/shared/Services/config.service' ; import { sitemapManipulationService } from 'src/shared/Services/sitemap.manipulation.service' ; import { StatusOk } from 'src/shared/Types' ; @ Injectable ( ) export class TitleService { private validator : Validator ; constructor ( @ InjectRepository ( TitlesRepository ) private readonly titlesRepository , @ InjectRepository ( CategoriesRepository ) private readonly categoriesRepository , @ InjectRepository ( EntriesRepository ) private readonly entriesRepository , @ InjectRepository ( UsersRepository ) private readonly usersRepository , private readonly awsService ) { this . validator = new Validator ( ) ; } async getTitle ( titleQueryData , isId ) { let title ; if ( isId ) { if ( ! this . validator . isMongoId ( titleQueryData ) ) throw new BadRequestException ( 'Id must be a type of MongoId' ) ; title = await this . titlesRepository . getTitleById ( titleQueryData ) ; } else { title = await this . titlesRepository . getTitleBySlug ( titleQueryData ) ; } return serializerService . serializeResponse ( 'title_detail' , title ) ; } async searchTitle ( { searchValue } ) { if ( searchValue . length < 3 ) throw new BadRequestException ( 'Search value must be greater than 2 characters' ) ; const result = await this . titlesRepository . searchTitle ( { searchValue } ) ; return serializerService . serializeResponse ( 'searched_title_list' , result ) ; } async getTitleList ( query ) { const result = await this . titlesRepository . getTitleList ( query ) ; return serializerService . serializeResponse ( 'title_list' , result ) ; } async createTitle ( openedBy , payload , buffer ) { payload . name = payload . name . replace ( /^\s+|\s+$/g , '' ) ; if ( payload . name . length === 0 ) throw new BadRequestException ( 'Title name can not be whitespace' ) ; const dto = new CreateTitleDto ( ) ; dto . name = payload . name ; dto . categoryId = payload . categoryId ; return await validate ( dto , { validationError : { target : false } } ) . then ( async ( errors ) => { if ( errors . length > 0 ) { throw new BadRequestException ( errors ) ; } let category ; try { category = await this . categoriesRepository . findOneOrFail ( dto . categoryId ) ; } catch ( err ) { throw new NotFoundException ( 'Category could not found by given id' ) ; } if ( ! category . is_leaf ) throw new BadRequestException ( 'Category that is not leaf can not have titles' ) ; const newTitle = await this . titlesRepository . createTitle ( openedBy , dto , category . ancestors ) ; if ( buffer ) this . awsService . uploadImage ( String ( newTitle . id ) , 'titles' , buffer ) ; if ( configService . isProduction ( ) ) sitemapManipulationService . addToIndexedSitemap ( newTitle . slug , new Date ( ) . toJSON ( ) . slice ( 0 , 10 ) ) ; return serializerService . serializeResponse ( 'title_detail' , newTitle ) ; } ) ; } async getTitleImage ( titleId ) { await this . titlesRepository . getTitleById ( titleId ) ; if ( configService . isProduction ( ) ) { return this . awsService . getImageBuffer ( titleId , 'titles' ) ; } return createReadStream ( `${ __dirname }/../../../../public/default-titles.jpg` ) ; } async updateTitleImage ( titleId , buffer ) { await this . titlesRepository . getTitleById ( titleId ) ; this . awsService . uploadImage ( titleId , 'titles' , buffer ) ; } async deleteTitleImage ( titleId ) { await this . titlesRepository . getTitleById ( titleId ) ; this . awsService . deleteImage ( titleId , 'titles' ) ; } async rateTitle ( ratedBy , titleId , rateValue ) { if ( ! this . validator . isMongoId ( titleId ) ) throw new BadRequestException ( 'Id must be a type of MongoId' ) ; try { this . usersRepository . findOneOrFail ( { username : ratedBy } ) ; } catch ( error ) { throw new NotFoundException ( 'User could not found by given username' ) ; } await this . titlesRepository . rateTitle ( ratedBy , titleId , rateValue ) ; return { status : 'ok' , message : 'Title has been rated' } ; } async getRateOfUser ( username , titleId ) { if ( ! this . validator . isMongoId ( titleId ) ) throw new BadRequestException ( 'Id must be a type of MongoId' ) ; try { this . usersRepository . findOneOrFail ( { username } ) ; } catch ( error ) { throw new NotFoundException ( 'User could not found by given username' ) ; } const rate = await this . titlesRepository . getRateOfUser ( username , titleId ) ; return serializerService . serializeResponse ( 'title_rate_of_user' , { title_id : titleId , rate } ) ; } async getAvarageRate ( titleId ) { if ( ! this . validator . isMongoId ( titleId ) ) throw new BadRequestException ( 'Id must be a type of MongoId' ) ; const averageRate = await this . titlesRepository . getAvarageRate ( titleId ) ; return serializerService . serializeResponse ( 'average_title_rate' , { title_id : titleId , rate : averageRate } ) ; } async updateTitle ( updatedBy , titleId , dto ) { dto . name = dto . name . replace ( /^\s+|\s+$/g , '' ) ; if ( dto . name . length === 0 ) throw new BadRequestException ( 'Title name can not be whitespace' ) ; if ( ! this . validator . isMongoId ( titleId ) ) throw new BadRequestException ( 'Id must be a type of MongoId' ) ; let title ; try { title = await this . titlesRepository . findOneOrFail ( titleId ) ; } catch { throw new NotFoundException ( 'Title could not found by given id' ) ; } if ( dto . categoryId && ! this . validator . isMongoId ( dto . categoryId ) ) { throw new BadRequestException ( 'Id must be a type of MongoId' ) ; } let category ; if ( dto . categoryId ) { try { category = await this . categoriesRepository . findOneOrFail ( dto . categoryId ) ; } catch ( err ) { throw new NotFoundException ( 'Category could not found by given id' ) ; } } const updatedTitle = await this . titlesRepository . updateTitle ( updatedBy , title , dto , category ?. ancestors ) ; if ( configService . isProduction ( ) ) sitemapManipulationService . addToIndexedSitemap ( updatedTitle . slug , new Date ( ) . toJSON ( ) . slice ( 0 , 10 ) ) ; return serializerService . serializeResponse ( 'title_detail' , updatedTitle ) ; } async deleteTitle ( titleId ) { if ( ! this . validator . isMongoId ( titleId ) ) throw new BadRequestException ( 'Id must be a type of MongoId' ) ; await this . titlesRepository . deleteTitle ( titleId ) ; await this . entriesRepository . deleteEntriesBelongsToTitle ( titleId ) ; this . awsService . deleteImage ( titleId , 'titles' ) ; return { status : 'ok' , message : 'Title has been deleted' } ; } }