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

✨ Use ticker title and description for signal group details #328

Merged
merged 2 commits into from
Jul 7, 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
1 change: 1 addition & 0 deletions internal/api/response/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
FormError ErrorMessage = "invalid form values"
StorageError ErrorMessage = "failed to save"
UploadsNotFound ErrorMessage = "uploads not found"
BridgeError ErrorMessage = "unable to update ticker in bridges"
MastodonError ErrorMessage = "unable to connect to mastodon"
BlueskyError ErrorMessage = "unable to connect to bluesky"
SignalGroupError ErrorMessage = "unable to connect to signal"
Expand Down
20 changes: 8 additions & 12 deletions internal/api/response/ticker.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,10 @@ type Bluesky struct {
}

type SignalGroup struct {
Active bool `json:"active"`
Connected bool `json:"connected"`
GroupID string `json:"groupID"`
GroupName string `json:"groupName"`
GroupDescription string `json:"groupDescription"`
GroupInviteLink string `json:"groupInviteLink"`
Active bool `json:"active"`
Connected bool `json:"connected"`
GroupID string `json:"groupID"`
GroupInviteLink string `json:"groupInviteLink"`
}

type Location struct {
Expand Down Expand Up @@ -108,12 +106,10 @@ func TickerResponse(t storage.Ticker, config config.Config) Ticker {
Handle: t.Bluesky.Handle,
},
SignalGroup: SignalGroup{
Active: t.SignalGroup.Active,
Connected: t.SignalGroup.Connected(),
GroupID: t.SignalGroup.GroupID,
GroupName: t.SignalGroup.GroupName,
GroupDescription: t.SignalGroup.GroupDescription,
GroupInviteLink: t.SignalGroup.GroupInviteLink,
Active: t.SignalGroup.Active,
Connected: t.SignalGroup.Connected(),
GroupID: t.SignalGroup.GroupID,
GroupInviteLink: t.SignalGroup.GroupInviteLink,
},
Location: Location{
Lat: t.Location.Lat,
Expand Down
10 changes: 3 additions & 7 deletions internal/api/response/ticker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,9 @@ func (s *TickersResponseTestSuite) TestTickersResponse() {
},
},
SignalGroup: storage.TickerSignalGroup{
Active: true,
GroupID: "example",
GroupName: "Example",
GroupDescription: "Example",
GroupInviteLink: "https://signal.group/#example",
Active: true,
GroupID: "example",
GroupInviteLink: "https://signal.group/#example",
},
Location: storage.TickerLocation{
Lat: 0.0,
Expand Down Expand Up @@ -96,8 +94,6 @@ func (s *TickersResponseTestSuite) TestTickersResponse() {
s.Equal(ticker.SignalGroup.Active, tickerResponse[0].SignalGroup.Active)
s.Equal(ticker.SignalGroup.Connected(), tickerResponse[0].SignalGroup.Connected)
s.Equal(ticker.SignalGroup.GroupID, tickerResponse[0].SignalGroup.GroupID)
s.Equal(ticker.SignalGroup.GroupName, tickerResponse[0].SignalGroup.GroupName)
s.Equal(ticker.SignalGroup.GroupDescription, tickerResponse[0].SignalGroup.GroupDescription)
s.Equal(ticker.Location.Lat, tickerResponse[0].Location.Lat)
s.Equal(ticker.Location.Lon, tickerResponse[0].Location.Lon)
}
Expand Down
23 changes: 13 additions & 10 deletions internal/api/tickers.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@
return
}

err = h.bridges.Update(ticker)
if err != nil {
log.WithError(err).Error("failed to update ticker in bridges")
c.JSON(http.StatusBadRequest, response.ErrorResponse(response.CodeDefault, response.BridgeError))
return

Check warning on line 91 in internal/api/tickers.go

View check run for this annotation

Codecov / codecov/patch

internal/api/tickers.go#L89-L91

Added lines #L89 - L91 were not covered by tests
}

err = h.storage.SaveTicker(&ticker)
if err != nil {
c.JSON(http.StatusBadRequest, response.ErrorResponse(response.CodeDefault, response.StorageError))
Expand Down Expand Up @@ -304,16 +311,12 @@
return
}

if body.GroupName != "" && body.GroupDescription != "" {
ticker.SignalGroup.GroupName = body.GroupName
ticker.SignalGroup.GroupDescription = body.GroupDescription
groupClient := signal.NewGroupClient(h.config)
err = groupClient.CreateOrUpdateGroup(&ticker.SignalGroup)
if err != nil {
log.WithError(err).Error("failed to create or update group")
c.JSON(http.StatusBadRequest, response.ErrorResponse(response.CodeDefault, response.SignalGroupError))
return
}
groupClient := signal.NewGroupClient(h.config)
err = groupClient.CreateOrUpdateGroup(&ticker)
if err != nil {
log.WithError(err).Error("failed to create or update group")
c.JSON(http.StatusBadRequest, response.ErrorResponse(response.CodeDefault, response.SignalGroupError))
return
}
ticker.SignalGroup.Active = body.Active

Expand Down
173 changes: 153 additions & 20 deletions internal/api/tickers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,44 +594,75 @@ func (s *TickerTestSuite) TestPutTickerSignalGroup() {
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns error", func() {
s.ctx.Set("ticker", storage.Ticker{})
s.Run("when signal-cli API call updateGroup returns error", func() {
// updateGroup
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(500)

s.ctx.Set("ticker", storage.Ticker{Title: "Example", Description: "Example"})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
s.store.On("SaveTicker", mock.Anything).Return(errors.New("storage error")).Once()

h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusBadRequest, s.w.Code)
s.True(gock.IsDone())
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns ticker", func() {
s.ctx.Set("ticker", storage.Ticker{})
s.Run("when signal-cli API call updateGroup returns no groupId", func() {
// updateGroup
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": map[string]interface{}{
"timestamp": 1,
},
"id": 1,
})

s.ctx.Set("ticker", storage.Ticker{Title: "Example", Description: "Example"})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
s.store.On("SaveTicker", mock.Anything).Return(nil).Once()

h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusOK, s.w.Code)
s.Equal(gock.IsDone(), true)
s.Equal(http.StatusBadRequest, s.w.Code)
s.True(gock.IsDone())
s.store.AssertExpectations(s.T())
})

s.Run("when signal-cli API call updateGroup returns error", func() {
s.Run("when signal-cli API call listGroups returns error", func() {
// updateGroup
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": map[string]interface{}{
"groupId": "sample-group-id",
"timestamp": 1,
},
"id": 1,
})
// listGroups
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(500)

s.ctx.Set("ticker", storage.Ticker{})
body := `{"active":true,"GroupName":"Example","GroupDescription":"Example"}`
s.ctx.Set("ticker", storage.Ticker{Title: "Example", Description: "Example"})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")

Expand All @@ -643,7 +674,7 @@ func (s *TickerTestSuite) TestPutTickerSignalGroup() {
s.store.AssertExpectations(s.T())
})

s.Run("when signal-cli API call getGroups returns error", func() {
s.Run("when signal-cli API call listGroups returns no group link", func() {
// updateGroup
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
Expand All @@ -661,10 +692,21 @@ func (s *TickerTestSuite) TestPutTickerSignalGroup() {
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(500)
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": []map[string]interface{}{
{
"id": "sample-group-id",
"name": "Example",
"description": "Example",
},
},
"id": 1,
})

s.ctx.Set("ticker", storage.Ticker{})
body := `{"active":true,"GroupName":"Example","GroupDescription":"Example"}`
s.ctx.Set("ticker", storage.Ticker{Title: "Example", Description: "Example"})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")

Expand All @@ -676,7 +718,7 @@ func (s *TickerTestSuite) TestPutTickerSignalGroup() {
s.store.AssertExpectations(s.T())
})

s.Run("when enabling signal group successfully", func() {
s.Run("when storage returns error", func() {
// updateGroup
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
Expand Down Expand Up @@ -708,8 +750,99 @@ func (s *TickerTestSuite) TestPutTickerSignalGroup() {
"id": 1,
})

s.ctx.Set("ticker", storage.Ticker{})
body := `{"active":true,"GroupName":"Example","GroupDescription":"Example"}`
s.ctx.Set("ticker", storage.Ticker{Title: "Example", Description: "Example"})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
s.store.On("SaveTicker", mock.Anything).Return(errors.New("storage error")).Once()

h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusBadRequest, s.w.Code)
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns ticker", func() {
// updateGroup
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": map[string]interface{}{
"groupId": "sample-group-id",
"timestamp": 1,
},
"id": 1,
})
// listGroups
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": []map[string]interface{}{
{
"id": "sample-group-id",
"name": "Example",
"description": "Example",
"groupInviteLink": "https://signal.group/#example",
},
},
"id": 1,
})

s.ctx.Set("ticker", storage.Ticker{Title: "Example", Description: "Example"})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
s.store.On("SaveTicker", mock.Anything).Return(nil).Once()

h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusOK, s.w.Code)
s.Equal(gock.IsDone(), true)
s.store.AssertExpectations(s.T())
})

s.Run("when creating signal group successfully", func() {
// updateGroup
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": map[string]interface{}{
"groupId": "sample-group-id",
"timestamp": 1,
},
"id": 1,
})
// listGroups
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": []map[string]interface{}{
{
"id": "sample-group-id",
"name": "Example",
"description": "Example",
"groupInviteLink": "https://signal.group/#example",
},
},
"id": 1,
})

s.ctx.Set("ticker", storage.Ticker{Title: "Example", Description: "Example"})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
s.store.On("SaveTicker", mock.Anything).Return(nil).Once()
Expand Down Expand Up @@ -754,8 +887,8 @@ func (s *TickerTestSuite) TestPutTickerSignalGroup() {
"id": 1,
})

s.ctx.Set("ticker", storage.Ticker{})
body := `{"active":true,"GroupId":"sample-group-id","GroupName":"Example","GroupDescription":"Example"}`
s.ctx.Set("ticker", storage.Ticker{Title: "Example", Description: "Example", SignalGroup: storage.TickerSignalGroup{GroupID: "sample-group-id"}})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
s.store.On("SaveTicker", mock.Anything).Return(nil).Once()
Expand Down
4 changes: 4 additions & 0 deletions internal/bridge/bluesky.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ type BlueskyBridge struct {
storage storage.Storage
}

func (bb *BlueskyBridge) Update(ticker storage.Ticker) error {
return nil
}

func (bb *BlueskyBridge) Send(ticker storage.Ticker, message *storage.Message) error {
if !ticker.Bluesky.Connected() || !ticker.Bluesky.Active {
return nil
Expand Down
9 changes: 9 additions & 0 deletions internal/bridge/bluesky_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import (
"github.com/systemli/ticker/internal/storage"
)

func (s *BridgeTestSuite) TestBlueskyUpdate() {
s.Run("does nothing", func() {
bridge := s.blueskyBridge(config.Config{}, &storage.MockStorage{})

err := bridge.Update(tickerWithBridges)
s.NoError(err)
})
}

func (s *BridgeTestSuite) TestBlueskySend() {
s.Run("when bluesky is inactive", func() {
bridge := s.blueskyBridge(config.Config{}, &storage.MockStorage{})
Expand Down
13 changes: 13 additions & 0 deletions internal/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
var log = logrus.WithField("package", "bridge")

type Bridge interface {
Update(ticker storage.Ticker) error
Send(ticker storage.Ticker, message *storage.Message) error
Delete(ticker storage.Ticker, message *storage.Message) error
}
Expand All @@ -24,6 +25,18 @@ func RegisterBridges(config config.Config, storage storage.Storage) Bridges {
return Bridges{"telegram": &telegram, "mastodon": &mastodon, "bluesky": &bluesky, "signalGroup": &signalGroup}
}

func (b *Bridges) Update(ticker storage.Ticker) error {
var err error
for name, bridge := range *b {
err := bridge.Update(ticker)
if err != nil {
log.WithError(err).WithField("bridge_name", name).Error("failed to update ticker")
}
}

return err
}

func (b *Bridges) Send(ticker storage.Ticker, message *storage.Message) error {
var err error
for name, bridge := range *b {
Expand Down
Loading
Loading