From 6f2073667169904c0d6aabeaa0fa991afc86db7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Wysoki=C5=84ski?= Date: Thu, 27 Oct 2022 15:16:37 +0200 Subject: [PATCH] feat: add a new command - monitor list --- go.mod | 1 + go.sum | 3 +- internal/discord/bot.go | 1 + internal/discord/command_monitor.go | 56 ++++++++++++++++++++++++ internal/domain/monitor.go | 5 +++ internal/service/monitor.go | 68 +++++++++++++++++++++++++++++ internal/service/monitor_test.go | 66 ++++++++++++++++++++++++++++ internal/service/service.go | 1 + internal/twhelp/client.go | 9 ++++ 9 files changed, 209 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 7b4edea..8deec56 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index af9e1bb..f4133f3 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/discord/bot.go b/internal/discord/bot.go index 4cb506c..b64efe0 100644 --- a/internal/discord/bot.go +++ b/internal/discord/bot.go @@ -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 } diff --git a/internal/discord/command_monitor.go b/internal/discord/command_monitor.go index b7bc3aa..9082337 100644 --- a/internal/discord/command_monitor.go +++ b/internal/discord/command_monitor.go @@ -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 +} diff --git a/internal/domain/monitor.go b/internal/domain/monitor.go index d02ec58..5432a7d 100644 --- a/internal/domain/monitor.go +++ b/internal/domain/monitor.go @@ -16,6 +16,11 @@ type Monitor struct { CreatedAt time.Time } +type MonitorWithTribe struct { + Monitor + Tribe TribeMeta +} + type CreateMonitorParams struct { tribeID int64 groupID string diff --git a/internal/service/monitor.go b/internal/service/monitor.go index e1d9c82..00f3386 100644 --- a/internal/service/monitor.go +++ b/internal/service/monitor.go @@ -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 { diff --git a/internal/service/monitor_test.go b/internal/service/monitor_test.go index 26a330e..a73d717 100644 --- a/internal/service/monitor_test.go +++ b/internal/service/monitor_test.go @@ -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() diff --git a/internal/service/service.go b/internal/service/service.go index be95e63..9d072ad 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -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, diff --git a/internal/twhelp/client.go b/internal/twhelp/client.go index bdba454..2ebb628 100644 --- a/internal/twhelp/client.go +++ b/internal/twhelp/client.go @@ -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 { -- 2.45.1