From abe771642158f917e0ca48131a12b18d41470a93 Mon Sep 17 00:00:00 2001 From: ayocodingit Date: Mon, 16 Sep 2024 10:58:04 +0700 Subject: [PATCH] init module category --- src/database/mongo/schemas/category.schema.ts | 21 +++++++ src/main.ts | 2 + src/modules/category/category.ts | 32 ++++++++++ src/modules/category/delivery/http/handler.ts | 58 ++++++++++++++++++ src/modules/category/entity/interface.ts | 3 + src/modules/category/entity/schema.ts | 6 ++ .../category/repository/mongo/repository.ts | 60 +++++++++++++++++++ src/modules/category/usecase/usecase.ts | 25 ++++++++ src/modules/images/usecase/usecase.ts | 2 - 9 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 src/database/mongo/schemas/category.schema.ts create mode 100644 src/modules/category/category.ts create mode 100644 src/modules/category/delivery/http/handler.ts create mode 100644 src/modules/category/entity/interface.ts create mode 100644 src/modules/category/entity/schema.ts create mode 100644 src/modules/category/repository/mongo/repository.ts create mode 100644 src/modules/category/usecase/usecase.ts diff --git a/src/database/mongo/schemas/category.schema.ts b/src/database/mongo/schemas/category.schema.ts new file mode 100644 index 0000000..0e2e1ba --- /dev/null +++ b/src/database/mongo/schemas/category.schema.ts @@ -0,0 +1,21 @@ +import { Schema } from 'mongoose' +import Mongo from '../mongo' + +const schema = new Schema( + { + name: { + type: String, + index: true, + required: true, + }, + }, + { + timestamps: { + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + versionKey: false, + } +) + +export default Mongo.Model('categories', schema) diff --git a/src/main.ts b/src/main.ts index 3121c25..9c5a1cd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import config from './config/config' import Mongo from './database/mongo/mongo' +import Category from './modules/category/category' import Images from './modules/images/images' import Logger from './pkg/logger' import Http from './transport/http/http' @@ -11,6 +12,7 @@ const main = async () => { // Start Load Modules new Images(logger, http, config) + new Category(logger).loadHttp(http) // End Load Modules http.Run(config.app.port.http) diff --git a/src/modules/category/category.ts b/src/modules/category/category.ts new file mode 100644 index 0000000..2b524d4 --- /dev/null +++ b/src/modules/category/category.ts @@ -0,0 +1,32 @@ +import Http from '../../transport/http/http' +import Logger from '../../pkg/logger' +import Usecase from './usecase/usecase' +import Handler from './delivery/http/handler' +import Repository from './repository/mongo/repository' + +class Category { + private usecase: Usecase + constructor( + private logger: Logger, + ) { + const repository = new Repository(logger) + const usecase = new Usecase(logger, repository) + this.usecase = usecase + } + + public loadHttp(http: Http) { + const handler = new Handler(this.logger, http, this.usecase) + this.httpPrivate(http, handler) + } + + private httpPrivate(http: Http, handler: Handler) { + const Router = http.Router() + + Router.post('/', handler.Store()) + Router.get('/', handler.Fetch()) + + http.SetRouter('/v1/categories', Router) + } +} + +export default Category diff --git a/src/modules/category/delivery/http/handler.ts b/src/modules/category/delivery/http/handler.ts new file mode 100644 index 0000000..ea899a3 --- /dev/null +++ b/src/modules/category/delivery/http/handler.ts @@ -0,0 +1,58 @@ +import Http from '../../../../transport/http/http' +import Logger from '../../../../pkg/logger' +import Usecase from '../../usecase/usecase' +import { NextFunction, Request, Response } from 'express' +import statusCode from '../../../../pkg/statusCode' +import { GetMeta, GetRequestParams } from '../../../../helpers/requestParams' +import { ValidateFormRequest } from '../../../../helpers/validate' +import { Store } from '../../entity/schema' + +class Handler { + constructor( + private logger: Logger, + private http: Http, + private usecase: Usecase + ) {} + + public Fetch() { + return async (req: Request, res: Response, next: NextFunction) => { + try { + const request = GetRequestParams(req.query) + const { data, count } = await this.usecase.Fetch(request) + this.logger.Info(statusCode[statusCode.OK], { + additional_info: this.http.AdditionalInfo( + req, + statusCode.OK + ), + }) + + return res.json({ data, meta: GetMeta(request, count) }) + } catch (error) { + return next(error) + } + } + } + + public Store() { + return async (req: any, res: Response, next: NextFunction) => { + try { + const value = ValidateFormRequest(Store, req.body) + const result = await this.usecase.Store(value) + this.logger.Info(statusCode[statusCode.CREATED], { + additional_info: this.http.AdditionalInfo( + req, + statusCode.CREATED + ), + }) + + return res + .status(statusCode.CREATED) + .json({ data: result, message: 'CREATED' }) + } catch (error) { + return next(error) + } + } + } +} + +export default Handler diff --git a/src/modules/category/entity/interface.ts b/src/modules/category/entity/interface.ts new file mode 100644 index 0000000..2c53a0c --- /dev/null +++ b/src/modules/category/entity/interface.ts @@ -0,0 +1,3 @@ +export interface Store { + name: string +} diff --git a/src/modules/category/entity/schema.ts b/src/modules/category/entity/schema.ts new file mode 100644 index 0000000..af74e7e --- /dev/null +++ b/src/modules/category/entity/schema.ts @@ -0,0 +1,6 @@ +import Joi from 'joi' +import { RegexSubdomain } from '../../../helpers/regex' + +export const Store = Joi.object({ + name: Joi.string().regex(RegexSubdomain).required(), +}) diff --git a/src/modules/category/repository/mongo/repository.ts b/src/modules/category/repository/mongo/repository.ts new file mode 100644 index 0000000..cd264b9 --- /dev/null +++ b/src/modules/category/repository/mongo/repository.ts @@ -0,0 +1,60 @@ +import Logger from '../../../../pkg/logger' +import { RequestParams } from '../../../../helpers/requestParams' +import categorySchema from '../../../../database/mongo/schemas/category.schema' +import { Store } from '../../entity/interface' + +class Repository { + constructor(private logger: Logger) {} + + public async Fetch({ + offset, + limit, + keyword, + sort_order, + sort_by, + }: RequestParams) { + const filter = {} + const sort = {} + + if (keyword) + Object.assign(filter, { + name: { + $regex: new RegExp(keyword, 'i'), + }, + }) + + if (['created_at', 'name'].includes(sort_by)) { + Object.assign(sort, { + [sort_by]: sort_order, + }) + } + + const data = await categorySchema + .find(filter) + .sort(sort) + .skip(offset) + .limit(limit) + const count = await categorySchema.find(filter).count() + + return { + data, + count, + } + } + + public async Store(body: Store) { + const schemaNew = new categorySchema(body) + + return schemaNew.save() + } + + public async FindUnique(name: string) { + const result = await categorySchema.findOne({ + name, + }) + + return result + } +} + +export default Repository diff --git a/src/modules/category/usecase/usecase.ts b/src/modules/category/usecase/usecase.ts new file mode 100644 index 0000000..e0c8358 --- /dev/null +++ b/src/modules/category/usecase/usecase.ts @@ -0,0 +1,25 @@ +import { RequestParams } from '../../../helpers/requestParams' +import error from '../../../pkg/error' +import Logger from '../../../pkg/logger' +import statusCode from '../../../pkg/statusCode' +import { Store } from '../entity/interface' +import Repository from '../repository/mongo/repository' + +class Usecase { + constructor(private logger: Logger, private repository: Repository) {} + + public async Fetch(request: RequestParams) { + return this.repository.Fetch(request) + } + + public async Store(body: Store) { + const item = await this.repository.FindUnique(body.name) + if (item) + throw new error(statusCode.BAD_REQUEST, 'Category Already Exist') + const result = await this.repository.Store(body) + + return result + } +} + +export default Usecase diff --git a/src/modules/images/usecase/usecase.ts b/src/modules/images/usecase/usecase.ts index fe1d345..def47eb 100644 --- a/src/modules/images/usecase/usecase.ts +++ b/src/modules/images/usecase/usecase.ts @@ -11,8 +11,6 @@ import { RegexExtensionImage, } from '../../../helpers/regex' import FileGenerator from '../../../external/fileGenerator' -import statusCode from '../../../pkg/statusCode' -import error from '../../../pkg/error' class Usecase { constructor(