Created
March 10, 2024 22:53
-
-
Save eadortsu/a9b715369ace30d2cef1df547911da10 to your computer and use it in GitHub Desktop.
Blog Scaffold
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ObjectType, Field } from '@nestjs/graphql'; | |
import { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; | |
import { Comment } from './comment.entity'; | |
import { Like } from './likes.entity'; | |
import { Paginated } from '../../common/pagination'; | |
@ObjectType() | |
@Entity('blogs') | |
export class Blog { | |
@Field(() => String, { description: 'Id' }) | |
@PrimaryGeneratedColumn('uuid') | |
id: string; | |
@Field({ | |
nullable: false, | |
description: 'Title', | |
}) | |
@Column({ nullable: false }) | |
title: string; | |
@Field({ | |
nullable: false, | |
description: 'slug', | |
}) | |
@Index() | |
@Column({ nullable: false }) | |
slug: string; | |
@Field({ | |
nullable: true, | |
description: 'Image', | |
}) | |
@Column({ nullable: true }) | |
image: string; | |
@Field(() => [String], { | |
nullable: true, | |
description: 'tags', | |
}) | |
@Column({ type: 'jsonb', nullable: true, default: [] }) | |
tags: string[]; | |
@Field(() => [String], { | |
nullable: true, | |
description: 'internal tags', | |
}) | |
@Column({ type: 'jsonb', nullable: true, default: [] }) | |
internalTags: string[]; | |
@Field({ | |
nullable: false, | |
description: 'html content', | |
}) | |
@Column({ nullable: false }) | |
htmlContent: string; | |
@Field({ | |
nullable: false, | |
description: 'content', | |
}) | |
@Column({ nullable: false }) | |
content: string; | |
@Field({ | |
nullable: false, | |
description: 'short description', | |
}) | |
@Column({ nullable: false }) | |
shortDescription: string; | |
@Field(() => [String], { | |
nullable: true, | |
description: 'credits', | |
}) | |
@Column({ type: 'jsonb', nullable: true, default: [] }) | |
credits: string[]; | |
@Field(() => [Comment], { description: 'Blog', nullable: 'itemsAndList' }) | |
@OneToMany(() => Comment, (comment) => comment.blog) | |
comments: Comment[]; | |
@Field(() => [Like], { description: 'Blog', nullable: 'itemsAndList' }) | |
@OneToMany(() => Like, (like) => like.blog) | |
likes: Like[]; | |
@Field({ | |
nullable: false, | |
description: 'publish', | |
}) | |
@Column({ nullable: false, default: true }) | |
publish: boolean; | |
@Field({ | |
nullable: false, | |
description: 'status', | |
}) | |
@Column({ nullable: false, default: true }) | |
status: boolean; | |
@Field({ nullable: false, description: 'featured' }) | |
@Column({ nullable: false, default: false }) | |
featured: boolean; | |
@Field({ nullable: false, description: 'time to read' }) | |
@Column({ nullable: false, default: 0 }) | |
timeToRead: number; | |
@Field() | |
@Column() | |
@CreateDateColumn() | |
createdAt: Date; | |
@Field() | |
@Column() | |
@UpdateDateColumn() | |
updatedAt: Date; | |
@Field({ nullable: true }) | |
@Column() | |
@DeleteDateColumn() | |
deletedAt: Date; | |
} | |
@ObjectType('PaginatedBlog') | |
export class PaginatedBlog extends Paginated(Blog) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql'; | |
import { Blog, PaginatedBlog } from './entities/blog.entity'; | |
import { CreateBlogInput } from './dto/create-blog.input'; | |
import { UpdateBlogInput } from './dto/update-blog.input'; | |
import { BlogService } from './blog.service'; | |
import { PaginatedEntityInput } from '../common/pagination'; | |
@Resolver(() => Blog) | |
export class BlogResolver { | |
constructor(private readonly blogService: BlogService) {} | |
@Mutation(() => Blog, { name: 'CreateBlog' }) | |
async createBlog(@Args('createBlogInput') createBlogInput: CreateBlogInput) { | |
return await this.blogService.create(createBlogInput); | |
} | |
@Query(() => PaginatedBlog, { name: 'Blogs' }) | |
async findAll( | |
@Args('options') options?: PaginatedEntityInput, | |
): Promise<PaginatedBlog> { | |
return await this.blogService.findAll(options); | |
} | |
@Query(() => Blog, { name: 'Blog' }) | |
async findOne(@Args('id', { type: () => String }) id: string) { | |
return await this.blogService.findOne(id); | |
} | |
@Mutation(() => Blog, { name: 'UpdateBlog' }) | |
async updateBlog(@Args('updateBlogInput') updateBlogInput: UpdateBlogInput) { | |
return await this.blogService.update(updateBlogInput); | |
} | |
@Mutation(() => Blog, { name: 'RemoveBlog' }) | |
async removeBlog(@Args('id', { type: () => String }) id: string) { | |
return await this.blogService.remove(id); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@nestjs/common'; | |
import { CreateBlogInput } from './dto/create-blog.input'; | |
import { UpdateBlogInput } from './dto/update-blog.input'; | |
import { InjectRepository } from '@nestjs/typeorm'; | |
import { Blog } from './entities/blog.entity'; | |
import { Repository } from 'typeorm'; | |
import { utils } from '../utils'; | |
import { UploadService } from '../upload/upload.service'; | |
import { JSDOM } from 'jsdom'; | |
import { | |
calcSkippedItems, | |
PaginatedEntityInput, | |
Pagination, | |
} from '../common/pagination'; | |
@Injectable() | |
export class BlogService { | |
constructor( | |
@InjectRepository(Blog) | |
private blogRepository: Repository<Blog>, | |
private uploadService: UploadService, | |
) {} | |
async create(createBlogInput: CreateBlogInput) { | |
const preparedBlogInput = await this.prepareBlogInput(createBlogInput); | |
return await this.blogRepository.save(preparedBlogInput); | |
} | |
async update(updateBlogInput: UpdateBlogInput) { | |
const preparedBlogInput = await this.prepareBlogInput(updateBlogInput); | |
await this.blogRepository.save(preparedBlogInput); | |
return await this.findOne(updateBlogInput.id); | |
} | |
async findAll(options: PaginatedEntityInput) { | |
const { | |
pagination: { limit, page }, | |
sorting: { column, order } = { column: 'createdAt', order: 'DESC' }, | |
filters = [], | |
} = options; | |
const skippedItemsCount = calcSkippedItems(page, limit); | |
const [results, total] = await this.blogRepository.findAndCount({ | |
where: { ...utils.getFilters(filters) }, | |
skip: skippedItemsCount, | |
take: limit, | |
order: { [column]: order }, | |
}); | |
return new Pagination<Blog>({ | |
page, | |
limit, | |
results, | |
total, | |
}); | |
} | |
async findOne(id: string) { | |
return await this.blogRepository.findOneOrFail({ | |
where: { id }, | |
relations: ['cities', 'countries', 'places'], | |
}); | |
} | |
async remove(id: string) { | |
return await this.blogRepository.softDelete({ id }); | |
} | |
calculateReadTime(content: string): number { | |
const words = content.split(' ').length; | |
return Math.ceil(words / 200); | |
} | |
async prepareBlogInput(blogPayload: CreateBlogInput | UpdateBlogInput) { | |
const { userId, cityIds, countryIds, placeIds, imageFile, ...blogInput } = | |
blogPayload; | |
blogInput.slug = utils.slugify(blogInput.title); | |
if (blogInput.htmlContent) { | |
const dom = new JSDOM(); | |
const parser = new dom.window.DOMParser(); | |
const doc = parser.parseFromString(blogInput.htmlContent, 'text/html'); | |
blogInput.content = doc.body.textContent || ''; | |
} | |
if (!blogInput.shortDescription) { | |
blogInput.shortDescription = blogInput.content.slice(0, 300); | |
} | |
if (imageFile) { | |
const image = await imageFile; | |
blogInput.image = await this.uploadService.uploadToFirebase({ | |
file: image, | |
path: `blog`, | |
fileName: blogInput.slug, | |
}); | |
} | |
blogInput.timeToRead = this.calculateReadTime(blogInput.content); | |
return blogInput; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment