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

[WIP] Event API를 추가합니다. #95

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
30 changes: 15 additions & 15 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ linters-settings:
- switch
- map

funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 150
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
statements: 100

# Ignore comments when counting lines.
# Default false
ignore-comments: true
# funlen:
# # Checks the number of lines in a function.
# # If lower than 0, disable the check.
# # Default: 60
# lines: 150
# # Checks the number of statements in a function.
# # If lower than 0, disable the check.
# # Default: 40
# statements: 100
#
# # Ignore comments when counting lines.
# # Default false
# ignore-comments: true

gocognit:
# Minimal code complexity to report
Expand Down Expand Up @@ -470,7 +470,7 @@ linters:
- exhaustive # checks exhaustiveness of enum switch statements
- exportloopref # checks for pointers to enclosing loop variables
- forbidigo # forbids identifiers
- funlen # tool for detection of long functions
# - funlen # tool for detection of long functions
- gocheckcompilerdirectives
- gochecknoinits # checks that no init functions are present in Go code
- gocognit # computes and checks the cognitive complexity of functions
Expand Down Expand Up @@ -585,7 +585,7 @@ issues:
- govet
- bodyclose
- dupl
- funlen
# - funlen
- goconst
- gosec
- noctx
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ clean:
make docs:clean

compile:
go generate ./...
go build -o ${BUILD_DIR}/${SERVER_BINARY_NAME} ./cmd/server

build:
Expand Down
8 changes: 8 additions & 0 deletions api/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ package pnd
import (
"encoding/json"
"net/http"

"github.com/google/uuid"
)

type CursorPaginatedView[T interface{}] struct {
Items []T `json:"items"`
Prev uuid.NullUUID `json:"prev"`
Next uuid.NullUUID `json:"next"`
}

type PaginatedView[T interface{}] struct {
Page int `json:"page"`
Size int `json:"size"`
Expand Down
193 changes: 193 additions & 0 deletions cmd/server/handler/event_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package handler

import (
"log"
"net/http"

"github.com/google/uuid"
"github.com/labstack/echo/v4"
pnd "github.com/pet-sitter/pets-next-door-api/api"
"github.com/pet-sitter/pets-next-door-api/internal/domain/event"
databasegen "github.com/pet-sitter/pets-next-door-api/internal/infra/database/gen"
"github.com/pet-sitter/pets-next-door-api/internal/service"
)

type EventHandler struct {
authService service.AuthService
eventService service.EventService
}

func NewEventHandler(
authService service.AuthService,
eventService service.EventService,
) *EventHandler {
return &EventHandler{
authService: authService,
eventService: eventService,
}
}

// FindEvents godoc
// @Summary 이벤트를 조회합니다.
// @Description
// @Tags events
// @Accept json
// @Produce json
// @Param author_id query string false "작성자 ID"
// @Param prev query int false "이전 페이지"
// @Param next query int false "다음 페이지"
// @Param size query int false "페이지 사이즈" default(20)
// @Success 200 {object} pnd.CursorPaginatedView[event.View]
// @Router /events [get]
func (h *EventHandler) FindEvents(c echo.Context) error {
prev, next, size, err := pnd.ParseCursorPaginationQueries(c, 20)
if err != nil {
return err
}
authorID, err := pnd.ParseOptionalUUIDQuery(c, "author_id")
if err != nil {
return err
}

ctx := c.Request().Context()
events, err := h.eventService.FindEvents(ctx, databasegen.FindEventsParams{
AuthorID: authorID,
Prev: prev,
Next: next,
Limit: int32(size),
})
if err != nil {
return err
}

items := make([]event.ShortTermView, len(events))
for i, e := range events {
items[i] = event.ToShortTermView(e)
}
return c.JSON(
http.StatusOK,
pnd.CursorPaginatedView[event.ShortTermView]{
Items: items,
},
)
}

// FindEventByID godoc
// @Summary ID로 이벤트를 조회합니다.
// @Description
// @Tags events
// @Produce json
// @Param id path string true "이벤트 ID"
// @Success 200 {object} event.View
// @Router /events/{id} [get]
func (h *EventHandler) FindEventByID(c echo.Context) error {
id, err := pnd.ParseIDFromPath(c, "id")
if err != nil {
return err
}

ctx := c.Request().Context()
found, err := h.eventService.FindEvent(
ctx,
databasegen.FindEventParams{ID: uuid.NullUUID{UUID: id, Valid: true}},
)
if err != nil {
return err
}

return c.JSON(http.StatusOK, event.ToShortTermView(found))
}

// CreateEvent godoc
// @Summary 이벤트를 생성합니다.
// @Description
// @Tags events
// @Accept json
// @Produce json
// @Param request body event.CreateRequest true "이벤트 생성 요청"
// @Security FirebaseAuth
// @Success 201 {object} event.View
// @Router /events [post]
func (h *EventHandler) CreateEvent(c echo.Context) error {
foundUser, err := h.authService.VerifyAuthAndGetUser(
c.Request().Context(),
c.Request().Header.Get("Authorization"),
)
if err != nil {
return err
}
authorID := foundUser.ID

var reqBody event.CreateRequest
if err := pnd.ParseBody(c, &reqBody); err != nil {
return err
}

ctx := c.Request().Context()
created, err := h.eventService.CreateEvent(ctx, authorID, reqBody)
if err != nil {
return err
}

return c.JSON(http.StatusCreated, event.ToShortTermView(created))
}

// UpdateEvent godoc
// @Summary 이벤트를 수정합니다.
// @Description
// @Tags events
// @Accept json
// @Produce json
// @Security FirebaseAuth
// @Param request body event.UpdateRequest true "이벤트 수정 요청"
// @Success 200
// @Router /events [put]
func (h *EventHandler) UpdateEvent(c echo.Context) error {
foundUser, err := h.authService.VerifyAuthAndGetUser(
c.Request().Context(),
c.Request().Header.Get("Authorization"),
)
if err != nil {
return err
}
uid := foundUser.FirebaseUID

var reqBody event.UpdateRequest
if err := pnd.ParseBody(c, &reqBody); err != nil {
return err
}

log.Printf("uid: %s, reqBody: %+v", uid, reqBody)
// TODO: Implement update event logic

return c.JSON(http.StatusOK, nil)
}

// DeleteEvent godoc
// @Summary 이벤트를 삭제합니다.
// @Description
// @Tags events
// @Security FirebaseAuth
// @Param id path string true "이벤트 ID"
// @Success 200
// @Router /events/{id} [delete]
func (h *EventHandler) DeleteEvent(c echo.Context) error {
foundUser, err := h.authService.VerifyAuthAndGetUser(
c.Request().Context(),
c.Request().Header.Get("Authorization"),
)
if err != nil {
return err
}
uid := foundUser.FirebaseUID

id, err := pnd.ParseIDFromPath(c, "id")
if err != nil {
return err
}

log.Printf("uid: %s, id: %s", uid, id)
// TODO: Implement delete event logic

return c.JSON(http.StatusOK, nil)
}
11 changes: 11 additions & 0 deletions cmd/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func NewRouter(app *firebaseinfra.FirebaseApp) (*echo.Echo, error) {
breedService := service.NewBreedService(db)
sosPostService := service.NewSOSPostService(db)
conditionService := service.NewSOSConditionService(db)
eventService := service.NewEventService(db, userService, mediaService)
chatService := service.NewChatService(db)

// Initialize handlers
Expand All @@ -68,6 +69,7 @@ func NewRouter(app *firebaseinfra.FirebaseApp) (*echo.Echo, error) {
sosPostHandler := handler.NewSOSPostHandler(*sosPostService, authService)
conditionHandler := handler.NewConditionHandler(*conditionService)
chatHandler := handler.NewChatHandler(authService, *chatService)
eventHandler := handler.NewEventHandler(authService, *eventService)

// // InMemoryStateManager는 클라이언트와 채팅방의 상태를 메모리에 저장하고 관리합니다.
// // 이 메서드는 단순하고 빠르며 테스트 목적으로 적합합니다.
Expand Down Expand Up @@ -170,6 +172,15 @@ func NewRouter(app *firebaseinfra.FirebaseApp) (*echo.Echo, error) {
postAPIGroup.GET("/sos/conditions", conditionHandler.FindConditions)
}

eventAPIGroup := apiRouteGroup.Group("/events")
{
eventAPIGroup.GET("", eventHandler.FindEvents)
eventAPIGroup.GET("/:id", eventHandler.FindEventByID)
eventAPIGroup.POST("", eventHandler.CreateEvent)
eventAPIGroup.PUT("/:id", eventHandler.UpdateEvent)
eventAPIGroup.DELETE("/:id", eventHandler.DeleteEvent)
}

upgrader := wschat.NewDefaultUpgrader()
wsServerV2 := wschat.NewWSServer(upgrader, authService, *mediaService)

Expand Down
1 change: 1 addition & 0 deletions db/migrations/000022_event.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE IF EXISTS events;
15 changes: 15 additions & 0 deletions db/migrations/000022_event.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS events (
id UUID PRIMARY KEY,
event_type VARCHAR NOT NULL,
author_id UUID NOT NULL,
name VARCHAR NOT NULL,
description TEXT NOT NULL,
media_id UUID,
topics TEXT[] NOT NULL,
max_participants INT,
fee INT NOT NULL,
start_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ
);
29 changes: 29 additions & 0 deletions internal/common/null.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ func StrToNullStr(val string) sql.NullString {
}
}

func NullInt32ToIntPtr(val sql.NullInt32) *int {
if val.Valid {
intValue := int(val.Int32)
return &intValue
}
return nil
}

func NullInt32ToInt32Ptr(val sql.NullInt32) *int32 {
if val.Valid {
return &val.Int32
}
return nil
}

func NullInt64ToInt64Ptr(val sql.NullInt64) *int64 {
if val.Valid {
return &val.Int64
Expand Down Expand Up @@ -118,3 +133,17 @@ func NullTimeToStr(val sql.NullTime) string {
}
return ""
}

func NullTimeToTimePtr(val sql.NullTime) *time.Time {
if val.Valid {
return &val.Time
}
return nil
}

func TimePtrToNullTime(val *time.Time) sql.NullTime {
return sql.NullTime{
Time: DerefOrEmpty(val),
Valid: IsNotNil(val),
}
}
25 changes: 25 additions & 0 deletions internal/domain/event/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package event

import (
"github.com/pet-sitter/pets-next-door-api/internal/domain/media"
"github.com/pet-sitter/pets-next-door-api/internal/domain/user"
databasegen "github.com/pet-sitter/pets-next-door-api/internal/infra/database/gen"
)

type Event struct {
Event databasegen.Event
Author user.WithoutPrivateInfo
Media *media.DetailView
}

func ToEvent(
eventData databasegen.Event,
authorData user.WithoutPrivateInfo,
mediaData *media.DetailView,
) *Event {
return &Event{
Event: eventData,
Author: authorData,
Media: mediaData,
}
}
Loading
Loading