Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(blog-service): extract helper methods for reusability and consistency #1360

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 83 additions & 67 deletions src/modules/blogs/blogs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ import { User } from '@modules/user/entities/user.entity';
@Injectable()
export class BlogService {
constructor(
@InjectRepository(Blog)
private blogRepository: Repository<Blog>,
@InjectRepository(User)
private userRepository: Repository<User>
@InjectRepository(Blog) private blogRepository: Repository<Blog>,
@InjectRepository(User) private userRepository: Repository<User>
) {}

private async fetchUserById(userId: string): Promise<User> {
Expand All @@ -27,31 +25,30 @@ export class BlogService {
if (!user) {
throw new CustomHttpException('User not found.', HttpStatus.NOT_FOUND);
}

return user;
}

private async findBlogById(id: string, relations: string[] = []): Promise<Blog> {
const blog = await this.blogRepository.findOne({ where: { id }, relations });
if (!blog) {
throw new CustomHttpException(SYS_MSG.BLOG_NOT_FOUND, HttpStatus.NOT_FOUND);
}
return blog;
}

async createBlog(createBlogDto: CreateBlogDto, user: User): Promise<BlogResponseDto> {
const fullUser = await this.fetchUserById(user.id);

const blog = this.blogRepository.create({
...createBlogDto,
author: fullUser,
});

const blog = this.blogRepository.create({ ...createBlogDto, author: fullUser });
const savedBlog = await this.blogRepository.save(blog);

return {
blog_id: savedBlog.id,
title: savedBlog.title,
content: savedBlog.content,
tags: savedBlog.tags,
image_urls: savedBlog.image_urls,
author: `${fullUser.first_name} ${fullUser.last_name}`,
created_at: savedBlog.created_at,
};
return this.formatBlogResponse(savedBlog);
}


async getSingleBlog(blogId: string): Promise<any> {
const blog = await this.findBlogById(blogId, ['author']);

async getSingleBlog(
blogId: string,
user: User
Expand All @@ -70,41 +67,40 @@ export class BlogService {
const { id, created_at, updated_at, ...rest } = singleBlog;
const author = `${fullName.first_name} ${fullName.last_name}`;


return {
status_code: 200,
status_code: HttpStatus.OK,
message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL,

data: this.formatBlogResponse(blog),

data: { blog_id: id, ...rest, author, published_date: created_at, created_at },

};
}

async updateBlog(id: string, updateBlogDto: UpdateBlogDto, user: User): Promise<BlogResponseDto> {
const blog = await this.blogRepository.findOne({
where: { id },
relations: ['author'],
});

if (!blog) {
throw new CustomHttpException('Blog post not found.', HttpStatus.NOT_FOUND);
}

const blog = await this.findBlogById(id, ['author']);
const fullUser = await this.fetchUserById(user.id);

Object.assign(blog, updateBlogDto, { author: fullUser });

const updatedBlog = await this.blogRepository.save(blog);

return {
blog_id: updatedBlog.id,
title: updatedBlog.title,
content: updatedBlog.content,
tags: updatedBlog.tags,
image_urls: updatedBlog.image_urls,
author: `${updatedBlog.author.first_name} ${updatedBlog.author.last_name}`,
created_at: updatedBlog.created_at,
};
return this.formatBlogResponse(updatedBlog);
}

async deleteBlogPost(id: string): Promise<void> {

const blog = await this.findBlogById(id);
await this.blogRepository.remove(blog);
}

async getAllBlogs(page: number, pageSize: number) {
const skip = (page - 1) * pageSize;
const [result, total] = await this.blogRepository.findAndCount({ skip, take: pageSize, relations: ['author'] });

return this.formatPaginatedResponse(result, total, page, pageSize, SYS_MSG.BLOG_FETCHED_SUCCESSFUL);

const blog = await this.blogRepository.findOne({ where: { id } });
if (!blog) {
throw new CustomHttpException('Blog post with this id does not exist.', HttpStatus.NOT_FOUND);
Expand Down Expand Up @@ -155,19 +151,15 @@ export class BlogService {
blogs: result.data.blogs,
},
};

}

async searchBlogs(query: any): Promise<{
status_code: number;
message: string;
data: { current_page: number; total_pages: number; total_results: number; blogs: BlogResponseDto[]; meta: any };
}> {
async searchBlogs(query: any) {
const { page = 1, page_size = 10 } = query;
const skip = (page - 1) * page_size;

this.validateEmptyValues(query);

const where: FindOptionsWhere<Blog> = this.buildWhereClause(query);
const where = this.buildWhereClause(query);

const [result, total] = await this.blogRepository.findAndCount({
where: Object.keys(where).length ? where : undefined,
Expand All @@ -176,41 +168,65 @@ export class BlogService {
relations: ['author'],
});

if (!result || result.length === 0) {
return {
status_code: HttpStatus.NOT_FOUND,
message: 'no_results_found_for_the_provided_search_criteria',
data: {
current_page: page,
total_pages: 0,
total_results: 0,
blogs: [],
meta: {
has_next: false,
total: 0,
next_page: null,
prev_page: null,
},
},
};
return this.formatPaginatedResponse(
result,
total,
page,
page_size,
result.length ? SYS_MSG.BLOG_FETCHED_SUCCESSFUL : 'No results found.'
);
}

private buildWhereClause(query: any): FindOptionsWhere<Blog> {
const where: FindOptionsWhere<Blog> = {};
if (query.author) where.author = { first_name: Like(`%${query.author}%`), last_name: Like(`%${query.author}%`) };
if (query.title) where.title = Like(`%${query.title}%`);
if (query.content) where.content = Like(`%${query.content}%`);
if (query.tags) where.tags = Like(`%${query.tags}%`);
if (query.created_date) where.created_at = MoreThanOrEqual(new Date(query.created_date));
return where;
}

private validateEmptyValues(query: any): void {
for (const key in query) {
if (query[key] !== undefined && typeof query[key] === 'string' && !query[key].trim()) {
throw new CustomHttpException(`${key.replace(/_/g, ' ')} value is empty`, HttpStatus.BAD_REQUEST);
}
}
}

const data = this.mapBlogResults(result);
const totalPages = Math.ceil(total / page_size);
private formatBlogResponse(blog: Blog): BlogResponseDto {
return {
blog_id: blog.id,
title: blog.title,
content: blog.content,
tags: blog.tags,
image_urls: blog.image_urls,
author: blog.author ? `${blog.author.first_name} ${blog.author.last_name}` : 'Unknown',
created_at: blog.created_at,
};
}

private formatPaginatedResponse(result: Blog[], total: number, page: number, pageSize: number, message: string) {
const totalPages = Math.ceil(total / pageSize);

return {
status_code: HttpStatus.OK,
message: SYS_MSG.BLOG_FETCHED_SUCCESSFUL,
status_code: total > 0 ? HttpStatus.OK : HttpStatus.NOT_FOUND,
message,
data: {
current_page: page,
total_pages: totalPages,
total_results: total,

blogs: result.map(blog => this.formatBlogResponse(blog)),

blogs: data
.filter(blog => !blog.deletedAt)
.map(blog => {
const { deletedAt, ...rest } = blog;
return rest;
}),

meta: {
has_next: page < totalPages,
total,
Expand Down
Loading
Loading