feat: add a new command - monitor list #28

Merged
Kichiyaki merged 1 commits from feat/monitor-list into master 2022-10-27 13:18:59 +00:00
9 changed files with 209 additions and 1 deletions

1
go.mod
View File

@ -5,6 +5,7 @@ go 1.19
require (
github.com/bwmarrin/discordgo v0.26.1
github.com/cenkalti/backoff/v4 v4.1.3
github.com/google/go-cmp v0.5.9
github.com/google/uuid v1.3.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/ory/dockertest/v3 v3.9.1

3
go.sum
View File

@ -49,7 +49,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=

View File

@ -20,6 +20,7 @@ type GroupService interface {
type MonitorService interface {
Create(ctx context.Context, groupID, serverID, tribeTag string) (domain.Monitor, error)
List(ctx context.Context, groupID, serverID string) ([]domain.MonitorWithTribe, error)
Execute(ctx context.Context) ([]domain.EnnoblementNotification, error)
Delete(ctx context.Context, id, serverID string) error
}

View File

@ -3,7 +3,9 @@ package discord
import (
"context"
"fmt"
"time"
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
"github.com/bwmarrin/discordgo"
)
@ -57,6 +59,19 @@ func (c *monitorCommand) create(s *discordgo.Session) error {
},
},
},
{
Name: "list",
Description: "Lists all monitors associated with a particular group",
Type: discordgo.ApplicationCommandOptionSubCommand,
Options: []*discordgo.ApplicationCommandOption{
{
Name: "group",
Description: "Group ID",
Type: discordgo.ApplicationCommandOptionString,
Required: true,
},
},
},
{
Name: "delete",
Description: "Deletes a monitor",
@ -90,6 +105,9 @@ func (c *monitorCommand) handle(s *discordgo.Session, i *discordgo.InteractionCr
case "create":
c.handleCreate(s, i)
return
case "list":
c.handleList(s, i)
return
case "delete":
c.handleDelete(s, i)
return
@ -133,6 +151,36 @@ func (c *monitorCommand) handleCreate(s *discordgo.Session, i *discordgo.Interac
})
}
func (c *monitorCommand) handleList(s *discordgo.Session, i *discordgo.InteractionCreate) {
ctx := context.Background()
group := i.ApplicationCommandData().Options[0].Options[0].StringValue()
monitors, err := c.svc.List(ctx, group, i.GuildID)
if err != nil {
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: messageFromError(err),
},
})
return
}
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Embeds: []*discordgo.MessageEmbed{
{
Type: discordgo.EmbedTypeRich,
Title: "Monitor list",
Description: buildMonitorListDescription(monitors),
Timestamp: formatTimestamp(time.Now()),
},
},
},
})
}
func (c *monitorCommand) handleDelete(s *discordgo.Session, i *discordgo.InteractionCreate) {
ctx := context.Background()
@ -154,3 +202,11 @@ func (c *monitorCommand) handleDelete(s *discordgo.Session, i *discordgo.Interac
},
})
}
func buildMonitorListDescription(monitors []domain.MonitorWithTribe) string {
description := "**ID** - **Tribe**"
for i, m := range monitors {
description += fmt.Sprintf("\n%d. %s - %s", i+1, m.ID, buildLink(m.Tribe.Tag, m.Tribe.ProfileURL))
}
return description
}

View File

@ -16,6 +16,11 @@ type Monitor struct {
CreatedAt time.Time
}
type MonitorWithTribe struct {
Monitor
Tribe TribeMeta
}
type CreateMonitorParams struct {
tribeID int64
groupID string

View File

@ -103,6 +103,74 @@ func (m *Monitor) Create(ctx context.Context, groupID, serverID, tribeTag string
return monitor, nil
}
type getTribeResult struct {
monitor domain.Monitor
tribe twhelp.Tribe
err error
}
func (m *Monitor) List(ctx context.Context, groupID, serverID string) ([]domain.MonitorWithTribe, error) {
group, err := m.groupSvc.Get(ctx, groupID, serverID)
if err != nil {
return nil, fmt.Errorf("GroupService.Get: %w", err)
}
monitors, err := m.repo.List(ctx, group.ID)
if err != nil {
return nil, fmt.Errorf("MonitorRepository.Delete: %w", err)
}
var wg sync.WaitGroup
ch := make(chan getTribeResult)
for _, monitor := range monitors {
wg.Add(1)
go func(monitor domain.Monitor) {
res := getTribeResult{
monitor: monitor,
}
res.tribe, res.err = m.client.GetTribeByID(
ctx,
group.VersionCode,
group.ServerKey,
monitor.TribeID,
)
ch <- res
}(monitor)
}
go func() {
wg.Wait()
close(ch)
}()
var firstErr error
results := make([]domain.MonitorWithTribe, 0, len(monitors))
for res := range ch {
wg.Done()
if res.err != nil && firstErr == nil {
firstErr = fmt.Errorf("couldn't load tribe (monitorID=%s): %w", res.monitor.ID, res.err)
continue
}
results = append(results, domain.MonitorWithTribe{
Monitor: res.monitor,
Tribe: domain.TribeMeta{
ID: res.tribe.ID,
Name: res.tribe.Name,
Tag: res.tribe.Tag,
ProfileURL: res.tribe.ProfileURL,
},
})
}
if firstErr != nil {
return nil, firstErr
}
return results, nil
}
func (m *Monitor) Delete(ctx context.Context, id, serverID string) error {
monitor, err := m.repo.Get(ctx, id)
if err != nil {

View File

@ -10,6 +10,7 @@ import (
"gitea.dwysokinski.me/twhelp/dcbot/internal/service"
"gitea.dwysokinski.me/twhelp/dcbot/internal/service/internal/mock"
"gitea.dwysokinski.me/twhelp/dcbot/internal/twhelp"
"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
@ -195,6 +196,71 @@ func TestMonitor_Create(t *testing.T) {
})
}
func TestMonitor_List(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()
groupID := uuid.NewString()
groupSvc := &mock.FakeGroupReader{}
groupSvc.GetCalls(func(ctx context.Context, groupID, serverID string) (domain.Group, error) {
return domain.Group{
ID: groupID,
ServerID: serverID,
ChannelGains: "",
ChannelLosses: "",
ServerKey: "pl151",
VersionCode: "pl",
CreatedAt: time.Now(),
}, nil
})
repo := &mock.FakeMonitorRepository{}
monitors := []domain.Monitor{
{
ID: uuid.NewString(),
TribeID: 124,
GroupID: groupID,
CreatedAt: time.Now(),
},
{
ID: uuid.NewString(),
TribeID: 125,
GroupID: groupID,
CreatedAt: time.Now(),
},
}
repo.ListReturns(monitors, nil)
client := &mock.FakeTWHelpClient{}
client.GetTribeByIDCalls(func(ctx context.Context, _ string, _ string, id int64) (twhelp.Tribe, error) {
return twhelp.Tribe{
ID: id,
Tag: uuid.NewString(),
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
DeletedAt: time.Time{},
}, nil
})
res, err := service.NewMonitor(repo, groupSvc, client, 10).
List(context.Background(), groupID, uuid.NewString())
assert.NoError(t, err)
assert.Len(t, res, len(monitors))
for _, m1 := range monitors {
var exist bool
for _, m2 := range res {
if cmp.Equal(m1, m2.Monitor) && m1.TribeID == m2.Tribe.ID {
exist = true
break
}
}
assert.True(t, exist)
}
})
}
func TestMonitor_Execute(t *testing.T) {
t.Parallel()

View File

@ -12,6 +12,7 @@ import (
type TWHelpClient interface {
ListVersions(ctx context.Context) ([]twhelp.Version, error)
GetServer(ctx context.Context, version, server string) (twhelp.Server, error)
GetTribeByID(ctx context.Context, version, server string, id int64) (twhelp.Tribe, error)
GetTribeByTag(ctx context.Context, version, server, tag string) (twhelp.Tribe, error)
ListEnnoblements(
ctx context.Context,

View File

@ -17,6 +17,7 @@ const (
endpointListVersions = "/api/v1/versions"
endpointGetServer = "/api/v1/versions/%s/servers/%s"
endpointGetTribeByID = "/api/v1/versions/%s/servers/%s/tribes/%d"
endpointGetTribeByTag = "/api/v1/versions/%s/servers/%s/tribes/tag/%s"
endpointListEnnoblements = "/api/v1/versions/%s/servers/%s/ennoblements"
)
@ -71,6 +72,14 @@ func (c *Client) GetServer(ctx context.Context, version, server string) (Server,
return resp.Data, nil
}
func (c *Client) GetTribeByID(ctx context.Context, version, server string, id int64) (Tribe, error) {
var resp getTribeResp
if err := c.getJSON(ctx, fmt.Sprintf(endpointGetTribeByID, version, server, id), &resp); err != nil {
return Tribe{}, err
}
return resp.Data, nil
}
func (c *Client) GetTribeByTag(ctx context.Context, version, server, tag string) (Tribe, error) {
var resp getTribeResp
if err := c.getJSON(ctx, fmt.Sprintf(endpointGetTribeByTag, version, server, tag), &resp); err != nil {