feat: add redis control in artcle feature

This commit is contained in:
pahmiudahgede 2025-01-22 12:55:54 +07:00
parent ef95e3bfbe
commit ef94470eda
4 changed files with 142 additions and 42 deletions

View File

@ -9,6 +9,6 @@ type Article struct {
Author string `gorm:"not null" json:"author"` Author string `gorm:"not null" json:"author"`
Heading string `gorm:"not null" json:"heading"` Heading string `gorm:"not null" json:"heading"`
Content string `gorm:"not null" json:"content"` Content string `gorm:"not null" json:"content"`
PublishedAt time.Time `gorm:"not null" json:"publishedAt"` PublishedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"not null" json:"updatedAt"` UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
} }

View File

@ -13,13 +13,12 @@ func CreateArticle(c *fiber.Ctx) error {
if err := c.BodyParser(&articleRequest); err != nil { if err := c.BodyParser(&articleRequest); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
fiber.StatusBadRequest, fiber.StatusBadRequest,
"Invalid input", "Input tidak valid",
nil, nil,
)) ))
} }
if err := articleRequest.ValidatePostArticle(); err != nil { if err := articleRequest.ValidatePostArticle(); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
fiber.StatusBadRequest, fiber.StatusBadRequest,
err.Error(), err.Error(),
@ -31,7 +30,7 @@ func CreateArticle(c *fiber.Ctx) error {
if err != nil { if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
fiber.StatusInternalServerError, fiber.StatusInternalServerError,
"Failed to create article", "Gagal membuat artikel",
nil, nil,
)) ))
} }
@ -52,7 +51,7 @@ func CreateArticle(c *fiber.Ctx) error {
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse( return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
fiber.StatusCreated, fiber.StatusCreated,
"Article created successfully", "Artikel berhasil dibuat",
response, response,
)) ))
} }
@ -63,7 +62,7 @@ func GetArticles(c *fiber.Ctx) error {
if err != nil { if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
fiber.StatusInternalServerError, fiber.StatusInternalServerError,
"Failed to fetch articles", "Gagal mengambil artikel",
nil, nil,
)) ))
} }
@ -71,14 +70,13 @@ func GetArticles(c *fiber.Ctx) error {
if len(articles) == 0 { if len(articles) == 0 {
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
fiber.StatusOK, fiber.StatusOK,
"Articles fetched successfully but data is empty", "Artikel berhasil diambil tetapi data kosong",
[]dto.FormattedResponse{}, []dto.FormattedResponse{},
)) ))
} }
var formattedArticles []dto.FormattedResponse var formattedArticles []dto.FormattedResponse
for _, article := range articles { for _, article := range articles {
article.PublishedAtFormatted = utils.FormatDateToIndonesianFormat(article.PublishedAt) article.PublishedAtFormatted = utils.FormatDateToIndonesianFormat(article.PublishedAt)
article.UpdatedAtFormatted = utils.FormatDateToIndonesianFormat(article.UpdatedAt) article.UpdatedAtFormatted = utils.FormatDateToIndonesianFormat(article.UpdatedAt)
@ -96,19 +94,20 @@ func GetArticles(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
fiber.StatusOK, fiber.StatusOK,
"Articles fetched successfully", "Artikel berhasil diambil",
formattedArticles, formattedArticles,
)) ))
} }
func GetArticleByID(c *fiber.Ctx) error { func GetArticleByID(c *fiber.Ctx) error {
id := c.Params("id") id := c.Params("id")
article, err := services.GetArticleByID(id) article, err := services.GetArticleByID(id)
if err != nil { if err != nil {
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse( return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
fiber.StatusNotFound, fiber.StatusNotFound,
"Article not found", "Artikel tidak ditemukan",
nil, nil,
)) ))
} }
@ -129,7 +128,7 @@ func GetArticleByID(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
fiber.StatusOK, fiber.StatusOK,
"Article fetched successfully", "Artikel berhasil diambil",
response, response,
)) ))
} }
@ -142,13 +141,12 @@ func UpdateArticle(c *fiber.Ctx) error {
if err := c.BodyParser(&articleUpdateRequest); err != nil { if err := c.BodyParser(&articleUpdateRequest); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
fiber.StatusBadRequest, fiber.StatusBadRequest,
"Invalid input", "Input tidak valid",
nil, nil,
)) ))
} }
if err := articleUpdateRequest.ValidateUpdateArticle(); err != nil { if err := articleUpdateRequest.ValidateUpdateArticle(); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
fiber.StatusBadRequest, fiber.StatusBadRequest,
err.Error(), err.Error(),
@ -158,9 +156,17 @@ func UpdateArticle(c *fiber.Ctx) error {
updatedArticle, err := services.UpdateArticle(id, &articleUpdateRequest) updatedArticle, err := services.UpdateArticle(id, &articleUpdateRequest)
if err != nil { if err != nil {
if err.Error() == "record not found" {
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
fiber.StatusNotFound,
"id article tidak diketahui",
nil,
))
}
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
fiber.StatusInternalServerError, fiber.StatusInternalServerError,
"Failed to update article", "Gagal memperbarui artikel",
nil, nil,
)) ))
} }
@ -181,7 +187,7 @@ func UpdateArticle(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
fiber.StatusOK, fiber.StatusOK,
"Article updated successfully", "Artikel berhasil diperbarui",
response, response,
)) ))
} }
@ -191,16 +197,24 @@ func DeleteArticle(c *fiber.Ctx) error {
err := services.DeleteArticle(id) err := services.DeleteArticle(id)
if err != nil { if err != nil {
if err.Error() == "record not found" {
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
fiber.StatusNotFound,
"id article tidak diketahui",
nil,
))
}
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse( return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
fiber.StatusInternalServerError, fiber.StatusInternalServerError,
"Failed to delete article", "Gagal menghapus artikel",
nil, nil,
)) ))
} }
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
fiber.StatusOK, fiber.StatusOK,
"Article deleted successfully", "Artikel berhasil dihapus",
nil, nil,
)) ))
} }

View File

@ -1,49 +1,87 @@
package repositories package repositories
import ( import (
"encoding/json"
"time"
"github.com/pahmiudahgede/senggoldong/config" "github.com/pahmiudahgede/senggoldong/config"
"github.com/pahmiudahgede/senggoldong/domain" "github.com/pahmiudahgede/senggoldong/domain"
"golang.org/x/net/context"
) )
var ctx = context.Background()
func CreateArticle(article *domain.Article) error { func CreateArticle(article *domain.Article) error {
err := config.DB.Create(article).Error err := config.DB.Create(article).Error
if err != nil { if err != nil {
return err return err
} }
config.RedisClient.Del(ctx, "articles")
return nil return nil
} }
func GetArticles() ([]domain.Article, error) { func GetArticles() ([]domain.Article, error) {
var articles []domain.Article var articles []domain.Article
err := config.DB.Find(&articles).Error
cachedArticles, err := config.RedisClient.Get(ctx, "articles").Result()
if err == nil {
err := json.Unmarshal([]byte(cachedArticles), &articles)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return articles, nil
}
err = config.DB.Find(&articles).Error
if err != nil {
return nil, err
}
articlesJSON, _ := json.Marshal(articles)
config.RedisClient.Set(ctx, "articles", articlesJSON, 10*time.Minute)
return articles, nil return articles, nil
} }
func GetArticleByID(id string) (domain.Article, error) { func GetArticleByID(id string) (domain.Article, error) {
var article domain.Article var article domain.Article
err := config.DB.Where("id = ?", id).First(&article).Error
cachedArticle, err := config.RedisClient.Get(ctx, "article:"+id).Result()
if err == nil {
err := json.Unmarshal([]byte(cachedArticle), &article)
if err != nil { if err != nil {
return article, err return article, err
} }
return article, nil
}
err = config.DB.Where("id = ?", id).First(&article).Error
if err != nil {
return article, err
}
articleJSON, _ := json.Marshal(article)
config.RedisClient.Set(ctx, "article:"+id, articleJSON, 10*time.Minute)
return article, nil return article, nil
} }
func UpdateArticle(article *domain.Article) error { func UpdateArticle(article *domain.Article) error {
err := config.DB.Save(article).Error err := config.DB.Save(article).Error
if err != nil { if err != nil {
return err return err
} }
config.RedisClient.Del(ctx, "article:"+article.ID)
config.RedisClient.Del(ctx, "articles")
return nil return nil
} }
func DeleteArticle(id string) error { func DeleteArticle(id string) error {
var article domain.Article var article domain.Article
err := config.DB.Where("id = ?", id).First(&article).Error err := config.DB.Where("id = ?", id).First(&article).Error
if err != nil { if err != nil {
return err return err
@ -53,5 +91,9 @@ func DeleteArticle(id string) error {
if err != nil { if err != nil {
return err return err
} }
config.RedisClient.Del(ctx, "article:"+id)
config.RedisClient.Del(ctx, "articles")
return nil return nil
} }

View File

@ -1,13 +1,18 @@
package services package services
import ( import (
"context"
"encoding/json"
"time" "time"
"github.com/pahmiudahgede/senggoldong/config"
"github.com/pahmiudahgede/senggoldong/domain" "github.com/pahmiudahgede/senggoldong/domain"
"github.com/pahmiudahgede/senggoldong/dto" "github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/repositories" "github.com/pahmiudahgede/senggoldong/internal/repositories"
) )
var ctx = context.Background()
func CreateArticle(articleRequest *dto.ArticleRequest) (dto.ArticleResponse, error) { func CreateArticle(articleRequest *dto.ArticleRequest) (dto.ArticleResponse, error) {
article := domain.Article{ article := domain.Article{
Title: articleRequest.Title, Title: articleRequest.Title,
@ -15,8 +20,6 @@ func CreateArticle(articleRequest *dto.ArticleRequest) (dto.ArticleResponse, err
Author: articleRequest.Author, Author: articleRequest.Author,
Heading: articleRequest.Heading, Heading: articleRequest.Heading,
Content: articleRequest.Content, Content: articleRequest.Content,
PublishedAt: time.Now(),
UpdatedAt: time.Now(),
} }
err := repositories.CreateArticle(&article) err := repositories.CreateArticle(&article)
@ -24,6 +27,8 @@ func CreateArticle(articleRequest *dto.ArticleRequest) (dto.ArticleResponse, err
return dto.ArticleResponse{}, err return dto.ArticleResponse{}, err
} }
config.RedisClient.Del(ctx, "articles")
articleResponse := dto.ArticleResponse{ articleResponse := dto.ArticleResponse{
ID: article.ID, ID: article.ID,
Title: article.Title, Title: article.Title,
@ -39,12 +44,22 @@ func CreateArticle(articleRequest *dto.ArticleRequest) (dto.ArticleResponse, err
} }
func GetArticles() ([]dto.ArticleResponse, error) { func GetArticles() ([]dto.ArticleResponse, error) {
var response []dto.ArticleResponse
cachedArticles, err := config.RedisClient.Get(ctx, "articles").Result()
if err == nil {
err := json.Unmarshal([]byte(cachedArticles), &response)
if err != nil {
return nil, err
}
return response, nil
}
articles, err := repositories.GetArticles() articles, err := repositories.GetArticles()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var response []dto.ArticleResponse
for _, article := range articles { for _, article := range articles {
response = append(response, dto.ArticleResponse{ response = append(response, dto.ArticleResponse{
ID: article.ID, ID: article.ID,
@ -57,15 +72,30 @@ func GetArticles() ([]dto.ArticleResponse, error) {
UpdatedAt: article.UpdatedAt, UpdatedAt: article.UpdatedAt,
}) })
} }
articlesJSON, _ := json.Marshal(response)
config.RedisClient.Set(ctx, "articles", articlesJSON, 10*time.Minute)
return response, nil return response, nil
} }
func GetArticleByID(id string) (dto.ArticleResponse, error) { func GetArticleByID(id string) (dto.ArticleResponse, error) {
cachedArticle, err := config.RedisClient.Get(ctx, "article:"+id).Result()
if err == nil {
var article dto.ArticleResponse
err := json.Unmarshal([]byte(cachedArticle), &article)
if err != nil {
return article, err
}
return article, nil
}
article, err := repositories.GetArticleByID(id) article, err := repositories.GetArticleByID(id)
if err != nil { if err != nil {
return dto.ArticleResponse{}, err return dto.ArticleResponse{}, err
} }
return dto.ArticleResponse{
articleResponse := dto.ArticleResponse{
ID: article.ID, ID: article.ID,
Title: article.Title, Title: article.Title,
CoverImage: article.CoverImage, CoverImage: article.CoverImage,
@ -74,7 +104,12 @@ func GetArticleByID(id string) (dto.ArticleResponse, error) {
Content: article.Content, Content: article.Content,
PublishedAt: article.PublishedAt, PublishedAt: article.PublishedAt,
UpdatedAt: article.UpdatedAt, UpdatedAt: article.UpdatedAt,
}, nil }
articleJSON, _ := json.Marshal(articleResponse)
config.RedisClient.Set(ctx, "article:"+id, articleJSON, 10*time.Minute)
return articleResponse, nil
} }
func UpdateArticle(id string, articleUpdateRequest *dto.ArticleUpdateRequest) (*dto.ArticleResponse, error) { func UpdateArticle(id string, articleUpdateRequest *dto.ArticleUpdateRequest) (*dto.ArticleResponse, error) {
@ -96,7 +131,10 @@ func UpdateArticle(id string, articleUpdateRequest *dto.ArticleUpdateRequest) (*
return nil, err return nil, err
} }
return &dto.ArticleResponse{ config.RedisClient.Del(ctx, "article:"+id)
config.RedisClient.Del(ctx, "articles")
updatedArticleResponse := dto.ArticleResponse{
ID: article.ID, ID: article.ID,
Title: article.Title, Title: article.Title,
CoverImage: article.CoverImage, CoverImage: article.CoverImage,
@ -105,7 +143,9 @@ func UpdateArticle(id string, articleUpdateRequest *dto.ArticleUpdateRequest) (*
Content: article.Content, Content: article.Content,
PublishedAt: article.PublishedAt, PublishedAt: article.PublishedAt,
UpdatedAt: article.UpdatedAt, UpdatedAt: article.UpdatedAt,
}, nil }
return &updatedArticleResponse, nil
} }
func DeleteArticle(id string) error { func DeleteArticle(id string) error {
@ -114,5 +154,9 @@ func DeleteArticle(id string) error {
if err != nil { if err != nil {
return err return err
} }
config.RedisClient.Del(ctx, "article:"+id)
config.RedisClient.Del(ctx, "articles")
return nil return nil
} }