Skip to content

Commit 3c55fb1

Browse files
authored
Merge pull request #19 from hammercode-dev/api/blog-posts
Api/blog posts
2 parents 37c574a + 76b8c2f commit 3c55fb1

File tree

12 files changed

+790
-9
lines changed

12 files changed

+790
-9
lines changed

app/app.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package app
22

33
import (
4+
blogPost "github.com/hammer-code/lms-be/app/blog_post"
45
"github.com/hammer-code/lms-be/app/middlewares"
56
newsletters "github.com/hammer-code/lms-be/app/newsletters"
67
users "github.com/hammer-code/lms-be/app/users"
@@ -20,6 +21,7 @@ type App struct {
2021
NewLetterHandler domain.NewslettterHandler
2122
EventHandler domain.EventHandler
2223
ImageHandler domain.ImageHandler
24+
BlogPostHandler domain.BlogPostHandler
2325
}
2426

2527
func InitApp(
@@ -39,6 +41,7 @@ func InitApp(
3941
newsletterRepo := newsletters.InitRepository(dbTx)
4042
eventRepo := events.InitRepository(dbTx)
4143
imgRepo := images.InitRepository(dbTx)
44+
blogPostRepo := blogPost.InitRepository(dbTx)
4245

4346
// Middlewares
4447
middleware := middlewares.InitMiddleware(jwtInstance, userRepo)
@@ -48,18 +51,21 @@ func InitApp(
4851
newsletterUC := newsletters.InitUsecase(cfg, newsletterRepo, dbTx, jwt.NewJwt(cfg.JWT_SECRET_KEY))
4952
eventUC := events.InitUsecase(eventRepo, imgRepo, dbTx)
5053
imgUc := images.InitUsecase(imgRepo, dbTx)
54+
blogPostUc := blogPost.InitUseCase(blogPostRepo, jwtInstance)
5155

5256
// handler
5357
userHandler := users.InitHandler(userUsecase)
5458
newsletterHandler := newsletters.InitHandler(newsletterUC, middleware)
5559
eventHandler := events.InitHandler(eventUC)
5660
ImageHandler := images.InitHandler(imgUc)
61+
blogPostHandler := blogPost.InitHandler(blogPostUc)
5762

5863
return App{
5964
UserHandler: userHandler,
6065
NewLetterHandler: newsletterHandler,
6166
Middleware: middleware,
6267
EventHandler: eventHandler,
6368
ImageHandler: ImageHandler,
69+
BlogPostHandler: blogPostHandler,
6470
}
6571
}

app/blog_post/blogPost.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package blog_post
2+
3+
import (
4+
blog_post_handler "github.com/hammer-code/lms-be/app/blog_post/delivery/http"
5+
blog_post_repo "github.com/hammer-code/lms-be/app/blog_post/repository"
6+
blog_post_usecase "github.com/hammer-code/lms-be/app/blog_post/usecase"
7+
"github.com/hammer-code/lms-be/domain"
8+
"github.com/hammer-code/lms-be/pkg/db"
9+
"github.com/hammer-code/lms-be/pkg/jwt"
10+
)
11+
12+
func InitRepository(db db.DatabaseTransaction) domain.BlogPostRepository {
13+
return blog_post_repo.NewRepository(db)
14+
}
15+
16+
func InitUseCase(repository domain.BlogPostRepository, jwt jwt.JWT) domain.BlogPostUsecase {
17+
return blog_post_usecase.NewUsecase(repository, jwt)
18+
}
19+
20+
func InitHandler(usecase domain.BlogPostUsecase) domain.BlogPostHandler {
21+
return blog_post_handler.NewHandler(usecase)
22+
}
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
package http
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"net/http"
7+
"strconv"
8+
"strings"
9+
"time"
10+
11+
"github.com/gorilla/mux"
12+
"github.com/hammer-code/lms-be/domain"
13+
"github.com/hammer-code/lms-be/utils"
14+
"github.com/sirupsen/logrus"
15+
)
16+
17+
type Handler struct {
18+
usecase domain.BlogPostUsecase
19+
}
20+
21+
// CreateBlogPost implements domain.BlogPostHandler.
22+
func (h Handler) CreateBlogPost(w http.ResponseWriter, r *http.Request) {
23+
bodyBytes, err := io.ReadAll(r.Body)
24+
if err != nil {
25+
resp := utils.CustomErrorResponse(err)
26+
utils.Response(resp, w)
27+
return
28+
}
29+
30+
var token string
31+
if hasToken := strings.HasPrefix(r.Header.Get("Authorization"), "Bearer "); hasToken {
32+
token = strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
33+
if token == "" {
34+
resp := utils.CustomErrorResponse(utils.NewUnauthorizedError(r.Context(), "Authorization token is required", nil))
35+
utils.Response(resp, w)
36+
return
37+
}
38+
}
39+
40+
BlogPost := domain.BlogPost{}
41+
if err = json.Unmarshal(bodyBytes, &BlogPost); err != nil {
42+
resp := utils.CustomErrorResponse(err)
43+
utils.Response(resp, w)
44+
return
45+
}
46+
47+
data, err := h.usecase.CreateBlogPost(r.Context(), BlogPost, token)
48+
if err != nil {
49+
resp := utils.CustomErrorResponse(err)
50+
utils.Response(resp, w)
51+
return
52+
}
53+
54+
utils.Response(domain.HttpResponse{
55+
Code: http.StatusCreated,
56+
Message: "Blog post created successfully",
57+
Data: data,
58+
}, w)
59+
60+
}
61+
62+
// DeleteBlogPost implements domain.BlogPostHandler.
63+
func (h Handler) DeleteBlogPost(w http.ResponseWriter, r *http.Request) {
64+
vars := mux.Vars(r)
65+
idString := vars["id"]
66+
67+
value, err := strconv.ParseUint(idString, 10, 32)
68+
if err != nil {
69+
logrus.Error("failed to convert string to uint: ", err)
70+
utils.Response(domain.HttpResponse{
71+
Code: 500,
72+
Message: err.Error(),
73+
}, w)
74+
return
75+
}
76+
77+
err = h.usecase.DeleteBlogPost(r.Context(), uint(value))
78+
if err != nil {
79+
logrus.Error("failed to delete event : ", err)
80+
utils.Response(domain.HttpResponse{
81+
Code: 500,
82+
Message: err.Error(),
83+
}, w)
84+
return
85+
}
86+
87+
utils.Response(domain.HttpResponse{
88+
Code: 200,
89+
Message: "success",
90+
Data: nil,
91+
}, w)
92+
}
93+
94+
// GetAllBlogPosts implements domain.BlogPostHandler.
95+
func (h Handler) GetAllBlogPosts(w http.ResponseWriter, r *http.Request) {
96+
resp, err := h.usecase.GetAllBlogPosts(r.Context())
97+
if err != nil {
98+
resp := utils.CustomErrorResponse(err)
99+
utils.Response(resp, w)
100+
return
101+
}
102+
103+
utils.Response(domain.HttpResponse{
104+
Code: http.StatusOK,
105+
Message: "Blog posts retrieved successfully",
106+
Data: resp,
107+
}, w)
108+
109+
}
110+
111+
// GetDetailBlogPost implements domain.BlogPostHandler.
112+
func (h Handler) GetDetailBlogPost(w http.ResponseWriter, r *http.Request) {
113+
vars := mux.Vars(r)
114+
slug := vars["slug"]
115+
116+
if slug == "" {
117+
resp := utils.CustomErrorResponse(utils.NewBadRequestError(r.Context(), "Slug is required", nil))
118+
utils.Response(resp, w)
119+
return
120+
}
121+
122+
resp, err := h.usecase.GetDetailBlogPost(r.Context(), slug, 0)
123+
if err != nil {
124+
resp := utils.CustomErrorResponse(err)
125+
utils.Response(resp, w)
126+
return
127+
}
128+
129+
utils.Response(domain.HttpResponse{
130+
Code: http.StatusOK,
131+
Message: "Blog post retrieved successfully",
132+
Data: resp,
133+
}, w)
134+
}
135+
136+
// UpdateBlogPost implements domain.BlogPostHandler.
137+
func (h Handler) UpdateBlogPost(w http.ResponseWriter, r *http.Request) {
138+
idS := mux.Vars(r)["id"]
139+
id, err := strconv.ParseUint(idS, 10, 32)
140+
if err != nil {
141+
logrus.Error("failed to convert string to uint: ", err)
142+
utils.Response(domain.HttpResponse{
143+
Code: http.StatusBadRequest,
144+
Message: "Invalid ID format",
145+
}, w)
146+
return
147+
}
148+
149+
existingPost, err := h.usecase.GetDetailBlogPost(r.Context(), "", uint(id))
150+
if err != nil {
151+
logrus.Error("failed to get existing blog post: ", err)
152+
utils.Response(domain.HttpResponse{
153+
Code: http.StatusNotFound,
154+
Message: "Blog post not found",
155+
}, w)
156+
return
157+
}
158+
159+
body, err := io.ReadAll(r.Body)
160+
if err != nil {
161+
logrus.Error("failed to read body: ", err)
162+
utils.Response(domain.HttpResponse{
163+
Code: http.StatusBadRequest,
164+
Message: "Invalid request body",
165+
}, w)
166+
return
167+
}
168+
169+
var patchData map[string]interface{}
170+
if err := json.Unmarshal(body, &patchData); err != nil {
171+
logrus.Error("failed to unmarshal: ", err)
172+
utils.Response(domain.HttpResponse{
173+
Code: http.StatusBadRequest,
174+
Message: "Invalid request format",
175+
}, w)
176+
return
177+
}
178+
179+
updatedPost := existingPost
180+
if title, ok := patchData["title"].(string); ok {
181+
updatedPost.Title = title
182+
}
183+
if content, ok := patchData["content"].(string); ok {
184+
updatedPost.Content = content
185+
}
186+
if excerpt, ok := patchData["excerpt"].(string); ok {
187+
updatedPost.Excerpt = excerpt
188+
}
189+
if category, ok := patchData["category"].(string); ok {
190+
updatedPost.Category = category
191+
}
192+
if status, ok := patchData["status"].(string); ok {
193+
updatedPost.Status = status
194+
}
195+
if tags, ok := patchData["tags"].([]interface{}); ok {
196+
updatedPost.Tags = make([]string, len(tags))
197+
for i, tag := range tags {
198+
updatedPost.Tags[i] = tag.(string)
199+
}
200+
}
201+
202+
if authorData, ok := patchData["author"].(map[string]interface{}); ok {
203+
if avatar, ok := authorData["avatar"].(string); ok && avatar != "" {
204+
updatedPost.Author.Avatar = avatar
205+
}
206+
}
207+
208+
updatedPost.UpdatedAt = time.Now()
209+
210+
err = h.usecase.UpdateBlogPost(r.Context(), updatedPost, uint(id))
211+
if err != nil {
212+
logrus.Error("failed to update blog post: ", err)
213+
utils.Response(domain.HttpResponse{
214+
Code: http.StatusInternalServerError,
215+
Message: "Failed to update blog post",
216+
}, w)
217+
return
218+
}
219+
220+
utils.Response(domain.HttpResponse{
221+
Code: http.StatusOK,
222+
Message: "Blog post updated successfully",
223+
Data: updatedPost,
224+
}, w)
225+
}
226+
227+
var (
228+
handlr *Handler
229+
)
230+
231+
func NewHandler(usecase domain.BlogPostUsecase) domain.BlogPostHandler {
232+
if handlr == nil {
233+
handlr = &Handler{
234+
usecase: usecase,
235+
}
236+
237+
}
238+
return *handlr
239+
}

0 commit comments

Comments
 (0)