Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go chi 버전 업데이트 및 돌봄급구 관련 API를 추가합니다. #22

Merged
merged 11 commits into from
Dec 15, 2023
Merged
200 changes: 200 additions & 0 deletions cmd/server/handler/sos_post_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package handler

import (
"encoding/json"
"net/http"
"strconv"

"github.com/go-playground/validator"
"github.com/pet-sitter/pets-next-door-api/api/commonviews"
webutils "github.com/pet-sitter/pets-next-door-api/internal/common"
"github.com/pet-sitter/pets-next-door-api/internal/domain/auth"
"github.com/pet-sitter/pets-next-door-api/internal/domain/sos_post"
)

type SosPostHandler struct {
sosPostService sos_post.SosPostService
authService auth.AuthService
}

func NewSosPostHandler(sosPostService sos_post.SosPostService, authService auth.AuthService) *SosPostHandler {
return &SosPostHandler{
sosPostService: sosPostService,
authService: authService,
}
}

// writeSosPost godoc
// @Summary 돌봄급구 게시글을 업로드합니다.
// @Description
// @Tags posts
// @Accept json
// @Produce json
// @Param request body sos_post.WriteSosPostRequest true "돌봄급구 게시글 업로드 요청"
// @Security FirebaseAuth
// @Success 201 {object} sos_post.WriteSosPostResponse
// @Router /posts/sos [post]
func (h *SosPostHandler) WriteSosPost(w http.ResponseWriter, r *http.Request) {
foundUser, err := h.authService.VerifyAuthAndGetUser(r.Context(), r.Header.Get("Authorization"))
if err != nil {
commonviews.Unauthorized(w, nil, "unauthorized")
return
}

uid, _ := strconv.Atoi(foundUser.FirebaseUID)

var writeSosPostRequest sos_post.WriteSosPostRequest

if err := json.NewDecoder(r.Body).Decode(&writeSosPostRequest); err != nil {
commonviews.BadRequest(w, nil, err.Error())
return
}
if err := validator.New().Struct(writeSosPostRequest); err != nil {
commonviews.BadRequest(w, nil, err.Error())
return
}

res, err := h.sosPostService.WriteSosPost(uid, &writeSosPostRequest)
if err != nil {
commonviews.InternalServerError(w, nil, err.Error())
return
}

commonviews.Created(w, nil, res)
}

// findSosPosts godoc
// @Summary 돌봄급구 게시글을 조회합니다.
// @Description
// @Tags posts
// @Accept json
// @Produce json
// @Param author_id query int false "작성자 ID"
// @Param page query int false "페이지 번호" default(1)
// @Param size query int false "페이지 사이즈" default(20)
// @Param sort_by query string false "정렬 기준" Enums(newest, deadline)
// @Security FirebaseAuth
// @Success 200 {object} commonviews.PaginatedView[sos_post.FindSosPostResponse]
// @Router /posts/sos [get]
func (h *SosPostHandler) FindSosPosts(w http.ResponseWriter, r *http.Request) {
authorIDQuery := r.URL.Query().Get("author_id")
pageQuery := r.URL.Query().Get("page")
sizeQuery := r.URL.Query().Get("size")
sortByQuery := r.URL.Query().Get("sort_by")

page := 1
size := 20

var err error
if pageQuery != "" {
page, err = strconv.Atoi(pageQuery)
if err != nil {
commonviews.BadRequest(w, nil, err.Error())
return
}
}

if sizeQuery != "" {
size, err = strconv.Atoi(sizeQuery)
if err != nil {
commonviews.BadRequest(w, nil, err.Error())
return
}
}

var res []sos_post.FindSosPostResponse

if authorIDQuery != "" {
var authorID int
authorID, err = strconv.Atoi(authorIDQuery)
if err != nil {
commonviews.BadRequest(w, nil, err.Error())
return
}

res, err = h.sosPostService.FindSosPostsByAuthorID(authorID, page, size)
if err != nil {
commonviews.InternalServerError(w, nil, err.Error())
return
}
} else {
var sortBy string
if sortByQuery == "" {
sortBy = "newest"
} else {
sortBy = sortByQuery
}

res, err = h.sosPostService.FindSosPosts(page, size, sortBy)
if err != nil {
commonviews.InternalServerError(w, nil, err.Error())
return
}
}

commonviews.OK(w, nil, commonviews.NewPaginatedView(page, size, res))
}

// findSosPostsByID godoc
// @Summary 게시글 ID로 돌봄급구 게시글을 조회합니다.
// @Description
// @Tags posts
// @Accept json
// @Produce json
// @Security FirebaseAuth
// @Param id path string true "게시글 ID"
// @Success 200 {object} sos_post.FindSosPostResponse
// @Router /posts/sos/{id} [get]
func (h *SosPostHandler) FindSosPostByID(w http.ResponseWriter, r *http.Request) {
SosPostID, err := webutils.ParseIdFromPath(r, "id")
if err != nil || SosPostID <= 0 {
commonviews.NotFound(w, nil, "invalid sos_post ID")
return
}
res, err := h.sosPostService.FindSosPostByID(SosPostID)
if err != nil {
commonviews.InternalServerError(w, nil, err.Error())
return
}

commonviews.OK(w, nil, res)
}

// updateSosPost godoc
// @Summary 돌봄급구 게시글을 수정합니다.
// @Description
// @Tags posts
// @Accept json
// @Produce json
// @Security FirebaseAuth
// @Param request body sos_post.UpdateSosPostRequest true "돌봄급구 수정 요청"
// @Success 200
// @Router /posts/sos [put]
func (h *SosPostHandler) UpdateSosPost(w http.ResponseWriter, r *http.Request) {
foundUser, err := h.authService.VerifyAuthAndGetUser(r.Context(), r.Header.Get("Authorization"))
if err != nil {
commonviews.Unauthorized(w, nil, "unauthorized")
return
}

uid, _ := strconv.Atoi(foundUser.FirebaseUID)

var updateSosPostRequest sos_post.UpdateSosPostRequest
if err := commonviews.ParseBody(w, r, &updateSosPostRequest); err != nil {
return
}

permission := h.sosPostService.CheckUpdatePermission(uid, updateSosPostRequest.ID)

if !permission {
commonviews.Forbidden(w, nil, "forbidden")
return
}

res, err := h.sosPostService.UpdateSosPost(&updateSosPostRequest)
if err != nil {
commonviews.InternalServerError(w, nil, err.Error())
}

commonviews.OK(w, nil, res)
}
21 changes: 18 additions & 3 deletions cmd/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package main
import (
"context"
"encoding/json"
"github.com/pet-sitter/pets-next-door-api/internal/domain/pet"
"log"
"net/http"

"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/pet-sitter/pets-next-door-api/internal/domain/pet"
"github.com/pet-sitter/pets-next-door-api/internal/domain/sos_post"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/pet-sitter/pets-next-door-api/cmd/server/handler"
"github.com/pet-sitter/pets-next-door-api/internal/configs"
"github.com/pet-sitter/pets-next-door-api/internal/domain/auth"
Expand Down Expand Up @@ -60,11 +62,18 @@ func NewRouter(app *firebaseinfra.FirebaseApp) *chi.Mux {
postgres.NewBreedPostgresStore(db),
)

sosPostService := sos_post.NewSosPostService(
postgres.NewSosPostPostgresStore(db),
postgres.NewResourceMediaPostgresStore(db),
postgres.NewUserPostgresStore(db),
)

// Initialize handlers
authHandler := handler.NewAuthHandler(authService, kakaoinfra.NewKakaoClient())
userHandler := handler.NewUserHandler(userService, authService)
mediaHandler := handler.NewMediaHandler(mediaService)
breedHandler := handler.NewBreedHandler(breedService)
sosPostHandler := handler.NewSosPostHandler(*sosPostService, authService)

// Register middlewares
r.Use(middleware.Logger)
Expand Down Expand Up @@ -101,6 +110,12 @@ func NewRouter(app *firebaseinfra.FirebaseApp) *chi.Mux {
r.Route("/breeds", func(r chi.Router) {
r.Get("/", breedHandler.FindBreeds)
})
r.Route("/posts", func(r chi.Router) {
r.Post("/sos", sosPostHandler.WriteSosPost)
r.Get("/sos/{id}", sosPostHandler.FindSosPostByID)
r.Get("/sos", sosPostHandler.FindSosPosts)
r.Put("/sos", sosPostHandler.UpdateSosPost)
})
})

return r
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3.7"

services:
db:
image: postgres:11.5-alpine
image: postgres:15-alpine
ports:
- "5454:5432"
environment:
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ go 1.20

require (
firebase.google.com/go v3.13.0+incompatible
github.com/go-chi/chi v1.5.4
github.com/joho/godotenv v1.5.1
github.com/swaggo/http-swagger/v2 v2.0.1
github.com/swaggo/swag v1.16.1
Expand All @@ -13,6 +12,7 @@ require (

require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/go-chi/chi/v5 v5.0.10 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
Expand Down
2 changes: 1 addition & 1 deletion internal/common/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"net/http"
"strconv"

"github.com/go-chi/chi"
"github.com/go-chi/chi/v5"
)

func ParseIdFromPath(r *http.Request, path string) (int, error) {
Expand Down
31 changes: 31 additions & 0 deletions internal/domain/media/resource_media.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package media

import "time"

type ResourceType string

const (
SosResourceType ResourceType = "sos_posts"
)

type ResourceMedia struct {
ID int `field:"id"`
ResourceType ResourceType `field:"resource_type"`
ResourceID int `field:"resource_id"`
MediaID int `field:"media_id"`
CreatedAt time.Time `field:"created_at"`
UpdatedAt time.Time `field:"updated_at"`
DeletedAt time.Time `field:"deleted_at"`
}

type ResourceMediaView struct {
ID int `field:"id"`
ResourceType ResourceType `field:"resource_type"`
ResourceID int `field:"resource_id"`
MediaID int `field:"media_id"`
}

type ResourceMediaStore interface {
CreateResourceMedia(resourceID int, mediaID int, resourceType string) (*ResourceMedia, error)
FindResourceMediaByResourceID(resourceID int, resourceType string) ([]Media, error)
}
5 changes: 5 additions & 0 deletions internal/domain/sos_post/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ type Condition struct {
DeletedAt string `filed:"deleted_at"`
}

type ConditionView struct {
ID int `filed:"id"`
Name string `filed:"name"`
}

type SosCondition string

const (
Expand Down
Loading