Skip to content

Commit

Permalink
feat: add POST Apply subscription API
Browse files Browse the repository at this point in the history
  • Loading branch information
slowhigh committed May 29, 2024
1 parent 107e4a7 commit 1c6c8d4
Show file tree
Hide file tree
Showing 24 changed files with 898 additions and 18 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
```bash
go run ./cmd/server .

# "google/wire" module
go install github.com/google/wire/cmd/wire@latest
wire ./cmd/server

# "swaggo/swag" module
go install github.com/swaggo/swag/cmd/swag@latest
swag init -pd -g ./cmd/server/main.go -o ./third_party/docs/

# "golang.org/x/tools" module
go install golang.org/x/tools/cmd/goimports@latest
goimports -w .
gofmt -s -w .
```
6 changes: 5 additions & 1 deletion cmd/server/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/gin-contrib/cache v1.3.0
github.com/gin-contrib/cors v1.7.2
github.com/gin-gonic/gin v1.10.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/wire v0.6.0
github.com/lib/pq v1.10.9
github.com/spf13/viper v1.18.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBEx
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
Expand Down
13 changes: 13 additions & 0 deletions infra/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ type Config struct {
Rest Rest `mapstructure:"REST"`
Database Database `mapstructure:"DATABASE"`
Swagger Swagger `mapstructure:"SWAGGER"`
Jwt Jwt `mapstructure:"JWT"`
Smtp Smtp `mapstructure:"SMTP"`
}

type App struct {
Expand All @@ -38,6 +40,17 @@ type Swagger struct {
BasePath string `mapstructure:"BASE_PATH"`
}

type Jwt struct {
SecretKey string `mapstructure:"SECRET_KEY"`
}

type Smtp struct {
Host string `mapstructure:"HOST"`
Port int `mapstructure:"PORT"`
UserName string `mapstructure:"USER_NAME"`
Password string `mapstructure:"PASSWORD"`
}

func NewConfig() (*Config, error) {
_, b, _, _ := runtime.Caller(0)
configDirPath := path.Join(path.Dir(b))
Expand Down
2 changes: 1 addition & 1 deletion infra/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"LOG_LEVEL": 4
},
"SWAGGER": {
"HOST": "localhost",
"HOST": "localhost:5000",
"BASE_PATH": "/"
}
}
72 changes: 72 additions & 0 deletions infra/database/repository/subscription_repo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package repository

import (
"errors"

"github.com/team-nerd-planet/api-server/infra/database"
"github.com/team-nerd-planet/api-server/internal/entity"
"gorm.io/gorm"
)

type SubscriptionRepo struct {
db *database.Database
}

func NewSubscriptionRepo(db *database.Database) entity.SubscriptionRepo {
db.AutoMigrate(&entity.Subscription{})

return &SubscriptionRepo{
db: db,
}
}

// Create implements entity.SubscriptionRepo.
func (sr *SubscriptionRepo) Create(newSubscription entity.Subscription) (*entity.Subscription, error) {
err := sr.db.Create(&newSubscription).Error
if err != nil {
return nil, err
}

return &newSubscription, nil
}

// Delete implements entity.SubscriptionRepo.
func (sr *SubscriptionRepo) Delete(id int64) (*entity.Subscription, error) {
panic("unimplemented")
}

// ExistEmail implements entity.SubscriptionRepo.
func (sr *SubscriptionRepo) ExistEmail(email string) (*int64, error) {
data := entity.Subscription{}

err := sr.db.Select("id").Take(&data, "email = ?", email).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
} else if err != nil {
return nil, err
}

id := int64(data.ID)
return &id, nil
}

// Update implements entity.SubscriptionRepo.
func (sr *SubscriptionRepo) Update(id int64, newSubscription entity.Subscription) (*entity.Subscription, error) {
var (
subscription entity.Subscription
)

err := sr.db.First(&subscription, id).Error
if err != nil {
return nil, err
}

subscription = newSubscription
subscription.ID = uint(id)
err = sr.db.Save(&subscription).Error
if err != nil {
return nil, err
}

return &subscription, nil
}
1 change: 1 addition & 0 deletions infra/infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ var InfraSet = wire.NewSet(
repository.NewItemRepo,
repository.NewJobTagRepo,
repository.NewSkillTagRepo,
repository.NewSubscriptionRepo,
)
4 changes: 2 additions & 2 deletions infra/router/handler/item_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/team-nerd-planet/api-server/infra/router/util"
"github.com/team-nerd-planet/api-server/internal/controller/rest"
_ "github.com/team-nerd-planet/api-server/internal/controller/rest/dto"
"github.com/team-nerd-planet/api-server/internal/controller/rest/dto/item_dto"
_ "github.com/team-nerd-planet/api-server/internal/entity"
)
Expand All @@ -23,15 +24,14 @@ import (
// @Param job_tags query []int64 false "관련 직무 DB ID 배열" collectionFormat(multi)
// @Param skill_tags query []int64 false "관련 스킬 DB ID 배열" collectionFormat(multi)
// @Param page query int true "페이지"
// @Success 200 {object} item_dto.FindAllItemRes
// @Success 200 {object} dto.Paginated[[]item_dto.FindAllItemRes]
// @Failure 400 {object} util.HTTPError
// @Failure 404 {object} util.HTTPError
// @Failure 500 {object} util.HTTPError
// @Router /v1/item [get]
func ListItems(c *gin.Context, ctrl rest.ItemController) {
req, err := util.ValidateQuery[item_dto.FindAllItemReq](c)
if err != nil {
c.Status(http.StatusBadRequest)
util.NewError(c, http.StatusBadRequest, err)
return
}
Expand Down
59 changes: 59 additions & 0 deletions infra/router/handler/subscription_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package handler

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/team-nerd-planet/api-server/infra/router/util"
"github.com/team-nerd-planet/api-server/internal/controller/rest"
"github.com/team-nerd-planet/api-server/internal/controller/rest/dto/subscription_dto"
)

// Apply
//
// @Summary Apply subscription
// @Description apply for subscription
// @Tags subscription
// @Schemes http
// @Accept json
// @Produce json
// @Param request body subscription_dto.ApplyReq true "contents for applying for subscription."
// @Success 200 {object} subscription_dto.ApplyRes
// @Failure 400 {object} util.HTTPError
// @Failure 500 {object} util.HTTPError
// @Router /v1/subscription/apply [post]
func Apply(c *gin.Context, ctrl rest.SubscriptionController) {
req, err := util.ValidateBody[subscription_dto.ApplyReq](c)
if err != nil {
util.NewError(c, http.StatusBadRequest, err)
return
}

res, ok := ctrl.Apply(*req)
if !ok {
util.NewError(c, http.StatusInternalServerError)
return
}

c.JSON(http.StatusOK, res)
}

func Approve(c *gin.Context, ctrl rest.SubscriptionController) {
req, err := util.ValidateQuery[subscription_dto.ApproveReq](c)
if err != nil {
c.Redirect(http.StatusBadRequest, "https://www.nerdplanet.app")
return
}

res, ok := ctrl.Approve(*req)
if !ok {
c.Redirect(http.StatusInternalServerError, "https://www.nerdplanet.app")
return
}

if res.Ok {
c.Redirect(http.StatusFound, "https://www.nerdplanet.app")
} else {
c.Redirect(http.StatusBadRequest, "https://www.nerdplanet.app")
}
}
14 changes: 10 additions & 4 deletions infra/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
"github.com/gin-gonic/gin"
swaggerfiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
docs "github.com/team-nerd-planet/api-server/docs"
"github.com/team-nerd-planet/api-server/infra/config"
"github.com/team-nerd-planet/api-server/infra/router/handler"
"github.com/team-nerd-planet/api-server/infra/router/middleware"
"github.com/team-nerd-planet/api-server/internal/controller/rest"
docs "github.com/team-nerd-planet/api-server/third_party/docs"
)

type Router struct {
Expand All @@ -22,7 +22,7 @@ type Router struct {
conf *config.Config
}

func NewRouter(conf *config.Config, itemCtrl rest.ItemController, tabCtrl rest.TagController) Router {
func NewRouter(conf *config.Config, itemCtrl rest.ItemController, tagCtrl rest.TagController, subscriptionCtrl rest.SubscriptionController) Router {
if conf.Rest.Mode == "release" {
gin.SetMode(gin.ReleaseMode)
}
Expand All @@ -44,8 +44,14 @@ func NewRouter(conf *config.Config, itemCtrl rest.ItemController, tabCtrl rest.T

tag := v1.Group("/tag")
{
tag.GET("/job", cache.CachePage(s, time.Hour, func(c *gin.Context) { handler.ListJobTags(c, tabCtrl) }))
tag.GET("/skill", cache.CachePage(s, time.Hour, func(c *gin.Context) { handler.ListSkillTags(c, tabCtrl) }))
tag.GET("/job", cache.CachePage(s, time.Hour, func(c *gin.Context) { handler.ListJobTags(c, tagCtrl) }))
tag.GET("/skill", cache.CachePage(s, time.Hour, func(c *gin.Context) { handler.ListSkillTags(c, tagCtrl) }))
}

subscription := v1.Group("/subscription")
{
subscription.POST("/apply", func(c *gin.Context) { handler.Apply(c, subscriptionCtrl) })
subscription.GET("/approve", func(c *gin.Context) { handler.Approve(c, subscriptionCtrl) })
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import (
"github.com/team-nerd-planet/api-server/internal/controller/rest"
)

var ControllerSet = wire.NewSet(rest.NewItemController, rest.NewTagController)
var ControllerSet = wire.NewSet(rest.NewItemController, rest.NewTagController, rest.NewSubscriptionController)
34 changes: 34 additions & 0 deletions internal/controller/rest/dto/subscription_dto/apply_dto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package subscription_dto

import "github.com/team-nerd-planet/api-server/internal/entity"

type ApplyReq struct {
Email string `json:"email" binding:"required,email"` // 이메일
Name *string `json:"name" binding:"omitempty"` // 이름
Division *string `json:"division" binding:"omitempty"` // 소속
PreferredCompanyArr []int64 `json:"preferred_company_arr" binding:"required"` // 회사 DB ID 배열
PreferredCompanySizeArr []entity.CompanySizeType `json:"preferred_companySize_arr" binding:"required"` // 회사 규모 배열 (0:스타트업, 1:중소기업, 2:중견기업, 3:대기업, 4:외국계)
PreferredJobArr []int64 `json:"preferred_job_arr" binding:"required"` // 직무 DB ID 배열
PreferredSkillArr []int64 `json:"preferred_skill_arr" binding:"required"` // 스킬 DB ID 배열
}

type ApplyRes struct {
Ok bool `json:"ok"` // 구독 신청 메일 전송 결과
}

func (asr ApplyReq) NewSubscription() entity.Subscription {
companySizeArr := make([]int64, len(asr.PreferredCompanySizeArr))
for i, companySize := range asr.PreferredCompanySizeArr {
companySizeArr[i] = int64(companySize)
}

return entity.Subscription{
Email: asr.Email,
Name: asr.Name,
Division: asr.Division,
PreferredCompanyArr: asr.PreferredCompanyArr,
PreferredCompanySizeArr: companySizeArr,
PreferredJobArr: asr.PreferredJobArr,
PreferredSkillArr: asr.PreferredSkillArr,
}
}
9 changes: 9 additions & 0 deletions internal/controller/rest/dto/subscription_dto/approve_dto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package subscription_dto

type ApproveReq struct {
Token string `form:"token" binding:"required"`
}

type ApproveRes struct {
Ok bool `json:"ok"` // 구독 인증 결과
}
48 changes: 48 additions & 0 deletions internal/controller/rest/subscription_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package rest

import (
"github.com/team-nerd-planet/api-server/internal/controller/rest/dto/subscription_dto"
"github.com/team-nerd-planet/api-server/internal/usecase/subscription"
)

type SubscriptionController struct {
subscriptionUcase subscription.SubscriptionUsecase
}

func NewSubscriptionController(subscriptionUcase subscription.SubscriptionUsecase) SubscriptionController {
return SubscriptionController{
subscriptionUcase: subscriptionUcase,
}
}

func (sc SubscriptionController) Apply(req subscription_dto.ApplyReq) (*subscription_dto.ApplyRes, bool) {
subscription, ok := sc.subscriptionUcase.ApplySubscription(req.NewSubscription())
if !ok {
return nil, false
}

result := false
if subscription != nil {
result = true
}

return &subscription_dto.ApplyRes{
Ok: result,
}, true
}

func (sc SubscriptionController) Approve(req subscription_dto.ApproveReq) (*subscription_dto.ApproveRes, bool) {
subscription, ok := sc.subscriptionUcase.Subscribe(req.Token)
if !ok {
return nil, false
}

result := false
if subscription != nil {
result = true
}

return &subscription_dto.ApproveRes{
Ok: result,
}, true
}
Loading

0 comments on commit 1c6c8d4

Please sign in to comment.