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

feat: api to get memo by discord id #743

Merged
merged 2 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ require (
github.com/jackc/pgtype v1.14.0
github.com/jinzhu/now v1.1.5
github.com/joho/godotenv v1.5.1
github.com/k0kubun/pp/v3 v3.2.0
github.com/k0kubun/pp v3.0.1+incompatible
github.com/lib/pq v1.10.9
github.com/matoous/go-nanoid v1.5.0
github.com/patrickmn/go-cache v2.1.0+incompatible
Expand Down Expand Up @@ -129,6 +129,7 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,10 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs=
github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
Expand Down
1 change: 1 addition & 0 deletions pkg/handler/memologs/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ type IHandler interface {
List(c *gin.Context)
Sync(c *gin.Context)
ListOpenPullRequest(c *gin.Context)
ListByDiscordID(c *gin.Context)
}
35 changes: 35 additions & 0 deletions pkg/handler/memologs/memo_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package memologs

import (
"context"
"errors"
"net/http"
"strconv"
"time"
Expand Down Expand Up @@ -234,3 +235,37 @@ func (h *handler) ListOpenPullRequest(c *gin.Context) {

c.JSON(http.StatusOK, view.CreateResponse[any](memoprs, nil, nil, nil, "ok"))
}

func (h *handler) ListByDiscordID(c *gin.Context) {
l := h.logger.Fields(
logger.Fields{
"handler": "memologs",
"method": "ListByDiscordID",
},
)

discordID := c.Query("discordID")
if discordID == "" {
l.Error(errors.New("discordID is required"), "discordID is required")
c.JSON(http.StatusBadRequest, view.CreateResponse[any](nil, nil, nil, nil, "discordID is required"))
return
}

memoLogs, err := h.store.MemoLog.List(h.repo.DB(), memolog.ListFilter{
DiscordID: discordID,
})
if err != nil {
l.Error(err, "failed to get memologs")
c.JSON(http.StatusInternalServerError, view.CreateResponse[any](nil, nil, err, nil, ""))
return
}

discordMemoRank, err := h.store.MemoLog.GetRankByDiscordID(h.repo.DB(), discordID)
if err != nil {
l.Error(err, "failed to get rank by discord id")
c.JSON(http.StatusInternalServerError, view.CreateResponse[any](nil, nil, err, nil, ""))
return
}

c.JSON(http.StatusOK, view.CreateResponse[any](view.ToMemoLogByDiscordID(memoLogs, discordMemoRank), nil, nil, nil, ""))
}
6 changes: 6 additions & 0 deletions pkg/model/memo_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ type MemoLog struct {
AuthorMemoUsernames []string `json:"-" gorm:"-"`
}

type DiscordAccountMemoRank struct {
DiscordID string
TotalMemos int
Rank int
}

func (MemoLog) BeforeCreate(db *gorm.DB) error {
return db.SetupJoinTable(&MemoLog{}, "Authors", &MemoAuthor{})
}
1 change: 1 addition & 0 deletions pkg/routes/v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ func loadV1Routes(r *gin.Engine, h *handler.Handler, repo store.DBRepo, s *store
memoGroup.POST("", amw.WithAuth, h.MemoLog.Create)
memoGroup.POST("/sync", amw.WithAuth, h.MemoLog.Sync)
memoGroup.GET("", amw.WithAuth, h.MemoLog.List)
memoGroup.GET("/discords", amw.WithAuth, h.MemoLog.ListByDiscordID)
memoGroup.GET("/prs", amw.WithAuth, h.MemoLog.ListOpenPullRequest)
}

Expand Down
6 changes: 6 additions & 0 deletions pkg/routes/v1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,12 @@ func Test_loadV1Routes(t *testing.T) {
Handler: "github.com/dwarvesf/fortress-api/pkg/handler/memologs.IHandler.ListOpenPullRequest-fm",
},
},
"/api/v1/memos/discords": {
"GET": {
Method: "GET",
Handler: "github.com/dwarvesf/fortress-api/pkg/handler/memologs.IHandler.ListByDiscordID-fm",
},
},
"/api/v1/public/community-nfts/:id": {
"GET": {
Method: "GET",
Expand Down
1 change: 1 addition & 0 deletions pkg/store/memolog/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ type IStore interface {
Create(db *gorm.DB, b []model.MemoLog) ([]model.MemoLog, error)
GetLimitByTimeRange(db *gorm.DB, start, end *time.Time, limit int) ([]model.MemoLog, error)
List(db *gorm.DB, filter ListFilter) ([]model.MemoLog, error)
GetRankByDiscordID(db *gorm.DB, discordID string) (*model.DiscordAccountMemoRank, error)
}
59 changes: 57 additions & 2 deletions pkg/store/memolog/memo_log.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package memolog

import (
"fmt"
"time"

"gorm.io/gorm"
Expand All @@ -27,8 +28,9 @@ func (s *store) GetLimitByTimeRange(db *gorm.DB, start, end *time.Time, limit in

// ListFilter is a filter for List function
type ListFilter struct {
From *time.Time
To *time.Time
From *time.Time
To *time.Time
DiscordID string
}

// List gets all memo logs
Expand All @@ -42,5 +44,58 @@ func (s *store) List(db *gorm.DB, filter ListFilter) ([]model.MemoLog, error) {
query = query.Where("published_at <= ?", *filter.To)
}

if filter.DiscordID != "" {
query = query.Joins("JOIN memo_authors ma ON ma.memo_log_id = memo_logs.id").
Joins("JOIN discord_accounts da ON da.id = ma.discord_account_id AND da.discord_id = ?", filter.DiscordID)
}

return logs, query.Find(&logs).Error
}

func (s *store) GetRankByDiscordID(db *gorm.DB, discordID string) (*model.DiscordAccountMemoRank, error) {
query := `
WITH memo_count AS (
SELECT
da.discord_id,
COUNT(ml.id) AS total_memos
FROM
public.memo_authors ma
JOIN
public.memo_logs ml ON ma.memo_log_id = ml.id
JOIN
public.discord_accounts da ON ma.discord_account_id = da.id
WHERE
ml.deleted_at IS NULL
GROUP BY
da.discord_id
),
ranked_memos AS (
SELECT
discord_id,
total_memos,
RANK() OVER (ORDER BY total_memos DESC) AS rank
FROM
memo_count
)
SELECT
rm.discord_id,
rm.total_memos,
rm.rank
FROM
ranked_memos rm
WHERE
rm.discord_id = ?
`
var memoRank model.DiscordAccountMemoRank
result := db.Raw(query, discordID).Scan(&memoRank)

if result.Error != nil {
return nil, result.Error
}

if result.RowsAffected == 0 {
return nil, fmt.Errorf("no records found for discord_id: %s", discordID)
}

return &memoRank, nil
}
72 changes: 72 additions & 0 deletions pkg/view/memo.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ type MemoLogAuthor struct {
MemoUsername string `json:"memoUsername"`
}

// AuthorRanking is the rank of the discord account
type AuthorRanking struct {
DiscordID string `json:"discordID"`
TotalMemos int `json:"totalMemos"`
Rank int `json:"rank"`
}

// MemoLogsResponse response for memo logs
type MemoLogsResponse struct {
Data []MemoLog `json:"data"`
Expand Down Expand Up @@ -67,3 +74,68 @@ func ToMemoLog(memoLogs []model.MemoLog) []MemoLog {

return rs
}

// MemoLogByDiscordIDResponse response for memo logs
type MemoLogByDiscordIDResponse struct {
Data MemoLogsByDiscordID `json:"data"`
} // @name MemoLogByDiscordIDResponse

type MemoLogsByDiscordID struct {
MemoLogs []MemoLog `json:"memoLogs"`
Rank AuthorRanking `json:"rank"`
} // @name MemoLogsByDiscordID

// ToMemoLogByDiscordID ...
func ToMemoLogByDiscordID(memoLogs []model.MemoLog, discordMemoRank *model.DiscordAccountMemoRank) MemoLogsByDiscordID {
rs := make([]MemoLog, 0)
for _, memoLog := range memoLogs {
authors := make([]MemoLogAuthor, 0)
for _, author := range memoLog.Authors {
var employeeID string
if author.Employee != nil {
employeeID = author.Employee.ID.String()
}

rank := &AuthorRanking{}
if discordMemoRank != nil {
rank.DiscordID = discordMemoRank.DiscordID
rank.TotalMemos = discordMemoRank.TotalMemos
rank.Rank = discordMemoRank.Rank
}

authors = append(authors, MemoLogAuthor{
EmployeeID: employeeID,
GithubUsername: author.GithubUsername,
DiscordID: author.DiscordID,
PersonalEmail: author.PersonalEmail,
DiscordUsername: author.DiscordUsername,
MemoUsername: author.MemoUsername,
})
}

rs = append(rs, MemoLog{
ID: memoLog.ID.String(),
Title: memoLog.Title,
URL: memoLog.URL,
Authors: authors,
Description: memoLog.Description,
PublishedAt: memoLog.PublishedAt,
Reward: memoLog.Reward,
Category: memoLog.Category,
})
}

authorRank := AuthorRanking{}
if discordMemoRank != nil {
authorRank = AuthorRanking{
DiscordID: discordMemoRank.DiscordID,
TotalMemos: discordMemoRank.TotalMemos,
Rank: discordMemoRank.Rank,
}
}

return MemoLogsByDiscordID{
MemoLogs: rs,
Rank: authorRank,
}
}
Loading