feat: wrap article and user service functions with TypedResult for improved error handling

This commit is contained in:
2026-04-11 01:48:09 -03:00
parent 94e8058880
commit af17b6dc5a
10 changed files with 363 additions and 240 deletions

View File

@@ -8,28 +8,35 @@ import {
} from '@/lib/feature/article/article.model';
import * as service from '@/lib/feature/article/article.service';
import { getSessionData } from '@/lib/session/session-storage';
import { TypedResult, wrap } from '@/utils/types/results';
import { UUIDv4 } from '@/utils/types/uuid';
export const getArticleByExternalId = async (
const _getArticleByExternalId = async (
externalId: UUIDv4
): Promise<ArticleModel | null> => {
return await service.getArticleByExternalId(externalId);
const result = await service.getArticleByExternalId(externalId);
if (!result.ok) throw result.error;
return result.value;
};
export const getArticleBySlug = async (
const _getArticleBySlug = async (
slug: string
): Promise<ArticleModel | null> => {
return await service.getArticleBySlug(slug);
const result = await service.getArticleBySlug(slug);
if (!result.ok) throw result.error;
return result.value;
};
export const getArticlesPaginated = async (
const _getArticlesPaginated = async (
page: number = 1,
pageSize: number = 10
): Promise<PaginatedArticlesResult> => {
return await service.getArticlesPaginated(page, pageSize);
const result = await service.getArticlesPaginated(page, pageSize);
if (!result.ok) throw result.error;
return result.value;
};
export const saveArticle = async (
const _saveArticle = async (
article: CreateArticleModel
): Promise<ArticleModel> => {
const session = await getSessionData();
@@ -38,10 +45,12 @@ export const saveArticle = async (
}
article.authorId = session.user.id;
return await service.saveArticle(article);
const result = await service.saveArticle(article);
if (!result.ok) throw result.error;
return result.value;
};
export const updateArticle = async (
const _updateArticle = async (
articleId: string,
article: UpdateArticleModel
): Promise<ArticleModel> => {
@@ -49,13 +58,45 @@ export const updateArticle = async (
if (!session || !session?.user || session?.user.role !== 'admin') {
throw new Error('Unauthorized: Only admin users can save articles.');
}
return await service.updateArticle(articleId, article);
const result = await service.updateArticle(articleId, article);
if (!result.ok) throw result.error;
return result.value;
};
export const deleteArticle = async (articleId: string): Promise<void> => {
const _deleteArticle = async (articleId: string): Promise<void> => {
const session = await getSessionData();
if (!session || !session?.user || session?.user.role !== 'admin') {
throw new Error('Unauthorized: Only admin users can delete articles.');
}
await service.deleteArticle(articleId);
const result = await service.deleteArticle(articleId);
if (!result.ok) throw result.error;
};
export const getArticleByExternalId: (
externalId: UUIDv4
) => Promise<TypedResult<ArticleModel | null>> = wrap(_getArticleByExternalId);
export const getArticleBySlug: (
slug: string
) => Promise<TypedResult<ArticleModel | null>> = wrap(_getArticleBySlug);
export const getArticlesPaginated: (
page?: number,
pageSize?: number
) => Promise<TypedResult<PaginatedArticlesResult>> = wrap(
_getArticlesPaginated
);
export const saveArticle: (
article: CreateArticleModel
) => Promise<TypedResult<ArticleModel>> = wrap(_saveArticle);
export const updateArticle: (
articleId: string,
article: UpdateArticleModel
) => Promise<TypedResult<ArticleModel>> = wrap(_updateArticle);
export const deleteArticle: (articleId: string) => Promise<TypedResult<void>> =
wrap(_deleteArticle);

View File

@@ -6,6 +6,7 @@ import {
PaginatedArticlesResult,
UpdateArticleModel,
} from '@/lib/feature/article/article.model';
import { TypedResult, wrap } from '@/utils/types/results';
import { UUIDv4 } from '@/utils/types/uuid';
export const articleEntityToModel = (
@@ -25,11 +26,7 @@ export const articleEntityToModel = (
};
};
/** Retrieves an artible by its external ID.
* @param externalId - The external ID of the article to retrieve.
* @returns {Promise<ArticleModel | null>} The article model if found, otherwise null.
* */
export const getArticleByExternalId = async (
const _getArticleByExternalId = async (
externalId: UUIDv4
): Promise<ArticleModel | null> => {
const articleRepository = await getRepository(ArticleEntity);
@@ -45,12 +42,7 @@ export const getArticleByExternalId = async (
return articleEntityToModel(articleEntity);
};
/**
* Retrieves an article by its slug.
* @param slug - The slug of the article to retrieve.
* @returns {Promise<ArticleModel | null>} The article model if found, otherwise null.
*/
export const getArticleBySlug = async (
const _getArticleBySlug = async (
slug: string
): Promise<ArticleModel | null> => {
const articleRepository = await getRepository(ArticleEntity);
@@ -64,12 +56,7 @@ export const getArticleBySlug = async (
return articleEntityToModel(articleEntity);
};
/**
* Retrieves all articles by a given author ID.
* @param authorId - The ID of the author.
* @returns {Promise<ArticleModel[]>} A list of article models.
*/
export const getArticlesByAuthorId = async (
const _getArticlesByAuthorId = async (
authorId: string
): Promise<ArticleModel[]> => {
const articleRepository = await getRepository(ArticleEntity);
@@ -79,13 +66,7 @@ export const getArticlesByAuthorId = async (
return articleEntities.map(articleEntityToModel);
};
/**
* Retrieves a paginated list of articles ordered by creation date descending.
* @param page - The page number (1-based).
* @param pageSize - The number of articles per page.
* @returns {Promise<PaginatedArticlesResult>} The paginated result.
*/
export const getArticlesPaginated = async (
const _getArticlesPaginated = async (
page: number = 1,
pageSize: number = 10
): Promise<PaginatedArticlesResult> => {
@@ -106,13 +87,7 @@ export const getArticlesPaginated = async (
};
};
/**
* Saves a new article to the database.
* @param article - The article data to save.
* @returns {Promise<ArticleModel>} The saved article model.
* @throws {Error} If an article with the same slug already exists.
*/
export const saveArticle = async (
const _saveArticle = async (
article: CreateArticleModel
): Promise<ArticleModel> => {
const articleRepository = await getRepository(ArticleEntity);
@@ -129,14 +104,7 @@ export const saveArticle = async (
return articleEntityToModel(await articleRepository.save(newArticle));
};
/**
* Updates an existing article in the database.
* @param articleId - The ID of the article to update.
* @param article - The new article data.
* @returns {Promise<ArticleModel>} The updated article model.
* @throws {Error} If the article with the given ID does not exist.
*/
export const updateArticle = async (
const _updateArticle = async (
articleId: string,
article: UpdateArticleModel
): Promise<ArticleModel> => {
@@ -160,12 +128,7 @@ export const updateArticle = async (
return articleEntityToModel(await articleRepository.save(existingArticle));
};
/**
* Deletes an article from the database.
* @param articleId - The ID of the article to delete.
* @throws {Error} If the article with the given ID does not exist.
*/
export const deleteArticle = async (articleId: string): Promise<void> => {
const _deleteArticle = async (articleId: string): Promise<void> => {
const articleRepository = await getRepository(ArticleEntity);
const existingArticle = await articleRepository.findOneBy({
@@ -177,3 +140,41 @@ export const deleteArticle = async (articleId: string): Promise<void> => {
await articleRepository.remove(existingArticle);
};
/** Retrieves an article by its external ID. */
export const getArticleByExternalId: (
externalId: UUIDv4
) => Promise<TypedResult<ArticleModel | null>> = wrap(_getArticleByExternalId);
/** Retrieves an article by its slug. */
export const getArticleBySlug: (
slug: string
) => Promise<TypedResult<ArticleModel | null>> = wrap(_getArticleBySlug);
/** Retrieves all articles by a given author ID. */
export const getArticlesByAuthorId: (
authorId: string
) => Promise<TypedResult<ArticleModel[]>> = wrap(_getArticlesByAuthorId);
/** Retrieves a paginated list of articles ordered by creation date descending. */
export const getArticlesPaginated: (
page?: number,
pageSize?: number
) => Promise<TypedResult<PaginatedArticlesResult>> = wrap(
_getArticlesPaginated
);
/** Saves a new article to the database. */
export const saveArticle: (
article: CreateArticleModel
) => Promise<TypedResult<ArticleModel>> = wrap(_saveArticle);
/** Updates an existing article in the database. */
export const updateArticle: (
articleId: string,
article: UpdateArticleModel
) => Promise<TypedResult<ArticleModel>> = wrap(_updateArticle);
/** Deletes an article from the database. */
export const deleteArticle: (articleId: string) => Promise<TypedResult<void>> =
wrap(_deleteArticle);