diff --git a/src/modules/article/article.controller.ts b/src/modules/article/article.controller.ts index 8b5a218..47b72e5 100644 --- a/src/modules/article/article.controller.ts +++ b/src/modules/article/article.controller.ts @@ -1,7 +1,6 @@ import { Controller } from "@/common/controller"; import { createOrmContextMiddleware } from "@/common/middlewares/create-orm-context.middleware"; import { isAdminMiddleware } from "@/common/middlewares/is-admin.middleware"; -import { paginationQuerySchema } from "@/common/schemas"; import type { AbstractFileStorage } from "@/common/services/file-storage/abstract.file-storage"; import type { AbstractJwtService } from "@/common/services/jwt-service/abstract.jwt-service"; import type { @@ -16,9 +15,11 @@ import { orm } from "@/database/orm"; import type { ArticleMapper } from "@/modules/article/article.mapper"; import { articleParamsSchema, + articleQuerySchema, articleRequestSchema, } from "@/modules/article/article.schemas"; import type { ArticleResponse } from "@/modules/article/article.types"; +import type { FilterQuery } from "@mikro-orm/core"; import { Router, type Request, type Response } from "express"; import slugify from "slugify"; import { ulid } from "ulid"; @@ -83,7 +84,7 @@ export class ArticleController extends Controller { } async list(req: Request, res: Response) { - const parseQueryResult = paginationQuerySchema.safeParse(req.query); + const parseQueryResult = articleQuerySchema.safeParse(req.query); if (!parseQueryResult.success) { return this.handleZodError(parseQueryResult.error, res, "query"); } @@ -91,15 +92,16 @@ export class ArticleController extends Controller { const count = await orm.em.count(Article); - const articles = await orm.em.find( - Article, - {}, - { - limit: query.per_page, - offset: (query.page - 1) * query.per_page, - orderBy: { createdAt: "DESC" }, - }, - ); + const filter: FilterQuery
= {}; + if (query.tag_slug) { + filter.tags = { slug: query.tag_slug }; + } + + const articles = await orm.em.find(Article, filter, { + limit: query.per_page, + offset: (query.page - 1) * query.per_page, + orderBy: { createdAt: "DESC" }, + }); return res.status(200).json({ data: articles.map(this.mapper.mapEntityToResponse.bind(this.mapper)), diff --git a/src/modules/article/article.schemas.ts b/src/modules/article/article.schemas.ts index abb8f55..31e9cea 100644 --- a/src/modules/article/article.schemas.ts +++ b/src/modules/article/article.schemas.ts @@ -1,3 +1,4 @@ +import { paginationQuerySchema } from "@/common/schemas"; import z from "zod"; export const articleRequestSchema = z.object({ @@ -21,6 +22,17 @@ export const articleRequestSchema = z.object({ .max(100, "Max 100 characters."), }); +export const articleQuerySchema = z.intersection( + paginationQuerySchema, + z.object({ + tag_slug: z + .string("Must be string.") + .nonempty("Must not empty.") + .max(100, "Max 100 characters.") + .optional(), + }), +); + export const articleParamsSchema = z.object({ slug: z .string("Must be string.") diff --git a/src/modules/article/article.types.ts b/src/modules/article/article.types.ts index e7b36f5..dda8a2e 100644 --- a/src/modules/article/article.types.ts +++ b/src/modules/article/article.types.ts @@ -1,5 +1,6 @@ import type { articleParamsSchema, + articleQuerySchema, articleRequestSchema, } from "@/modules/article/article.schemas"; import type { TagResponse } from "@/modules/tag/tag.types"; @@ -7,6 +8,8 @@ import z from "zod"; export type ArticleRequest = z.infer; +export type ArticleQuery = z.infer; + export type ArticleParams = z.infer; export type ArticleResponse = { diff --git a/src/modules/tag/tag.controller.ts b/src/modules/tag/tag.controller.ts index 16a0092..9c56cdf 100644 --- a/src/modules/tag/tag.controller.ts +++ b/src/modules/tag/tag.controller.ts @@ -87,13 +87,13 @@ export class TagController extends Controller { } const params = parseParamsResult.data; - const tag = await orm.em.findOne(Tag, { id: params.id }); + const tag = await orm.em.findOne(Tag, { slug: params.slug }); if (!tag) { return res.status(404).json({ data: null, errors: [ { - path: "id", + path: "slug", location: "params", message: "Tag not found.", }, @@ -120,13 +120,13 @@ export class TagController extends Controller { } const body = parseBodyResult.data; - const tag = await orm.em.findOne(Tag, { id: params.id }); + const tag = await orm.em.findOne(Tag, { slug: params.slug }); if (!tag) { return res.status(404).json({ data: null, errors: [ { - path: "id", + path: "slug", location: "params", message: "Tag not found.", }, @@ -155,7 +155,7 @@ export class TagController extends Controller { const tag = await orm.em.findOne( Tag, - { id: params.id }, + { slug: params.slug }, { populate: ["*"], }, @@ -165,7 +165,7 @@ export class TagController extends Controller { data: null, errors: [ { - path: "id", + path: "slug", location: "params", message: "Tag not found.", }, diff --git a/src/modules/tag/tag.schemas.ts b/src/modules/tag/tag.schemas.ts index 1090d5c..30de4e2 100644 --- a/src/modules/tag/tag.schemas.ts +++ b/src/modules/tag/tag.schemas.ts @@ -8,8 +8,8 @@ export const tagRequestSchema = z.object({ }); export const tagParamsSchema = z.object({ - id: z - .ulid("Must be ulid string.") + slug: z + .string("Must be string.") .nonempty("Must not empty.") - .max(30, "Max 30 characters."), + .max(100, "Max 100 characters."), });