Skip to content

Commit

Permalink
Merge pull request #401 from rusq/canvas
Browse files Browse the repository at this point in the history
Add support for channel canvases
  • Loading branch information
rusq authored Jan 10, 2025
2 parents 0e02dcb + 2907a7c commit 0e26de4
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 3 deletions.
34 changes: 34 additions & 0 deletions clienter_mock_test.go

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

1 change: 1 addition & 0 deletions internal/chunk/dirproc/conversations.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"runtime/trace"

"github.com/rusq/slack"

"github.com/rusq/slackdump/v3/internal/chunk"
"github.com/rusq/slackdump/v3/processor"
)
Expand Down
2 changes: 1 addition & 1 deletion internal/chunk/transform/fileproc/fileproc.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func NewSubprocessor(dl Downloader, fp func(ci *slack.Channel, f *slack.File) st
}
}

func (b Subprocessor) Files(ctx context.Context, channel *slack.Channel, msg slack.Message, ff []slack.File) error {
func (b Subprocessor) Files(ctx context.Context, channel *slack.Channel, _ slack.Message, ff []slack.File) error {
for _, f := range ff {
if !IsValid(&f) {
continue
Expand Down
4 changes: 4 additions & 0 deletions internal/edge/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,7 @@ func (w *Wrapper) SearchMessagesContext(ctx context.Context, query string, param
func (w *Wrapper) SearchFilesContext(ctx context.Context, query string, params slack.SearchParameters) (*slack.SearchFiles, error) {
return w.cl.SearchFilesContext(ctx, query, params)
}

func (w *Wrapper) GetFileInfoContext(ctx context.Context, fileID string, count int, page int) (*slack.File, []slack.Comment, *slack.Paging, error) {
return w.cl.GetFileInfoContext(ctx, fileID, count, page)
}
2 changes: 2 additions & 0 deletions slackdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type Slacker interface {

SearchMessagesContext(ctx context.Context, query string, params slack.SearchParameters) (*slack.SearchMessages, error)
SearchFilesContext(ctx context.Context, query string, params slack.SearchParameters) (*slack.SearchFiles, error)

GetFileInfoContext(ctx context.Context, fileID string, count int, page int) (*slack.File, []slack.Comment, *slack.Paging, error)
}

// clienter is the interface with some functions of slack.Client with the sole
Expand Down
1 change: 1 addition & 0 deletions stream/conversation.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ func (we *Result) Unwrap() error {
return we.Err
}

// channel fetches the channel data as defined in req, calling callback function for each API response.
func (cs *Stream) channel(ctx context.Context, req request, callback func(mm []slack.Message, isLast bool) error) error {
ctx, task := trace.NewTask(ctx, "channel")
defer task.End()
Expand Down
17 changes: 17 additions & 0 deletions stream/mock_stream/mock_stream.go

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

1 change: 1 addition & 0 deletions stream/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type Slacker interface {

SearchMessagesContext(ctx context.Context, query string, params slack.SearchParameters) (*slack.SearchMessages, error)
SearchFilesContext(ctx context.Context, query string, params slack.SearchParameters) (*slack.SearchFiles, error)
GetFileInfoContext(ctx context.Context, fileID string, count int, page int) (*slack.File, []slack.Comment, *slack.Paging, error)
}

// Stream is used to fetch conversations from Slack. It is safe for concurrent
Expand Down
29 changes: 27 additions & 2 deletions stream/stream_workers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package stream

import (
"context"
"errors"
"fmt"
"log/slog"
"runtime/trace"

"github.com/rusq/slack"
Expand All @@ -28,6 +30,12 @@ func (cs *Stream) channelWorker(ctx context.Context, proc processor.Conversation
results <- Result{Type: RTChannel, ChannelID: req.sl.Channel, Err: err}
continue
}
if channel.Properties != nil && !channel.Properties.Canvas.IsEmpty {
if err := cs.canvas(ctx, proc, channel, channel.Properties.Canvas.FileId); err != nil {
// ignore canvas errors
slog.Warn("canvas error: %s", "err", err)
}
}
if err := cs.channel(ctx, req, func(mm []slack.Message, isLast bool) error {
n, err := procChanMsg(ctx, proc, threadC, channel, isLast, mm)
if err != nil {
Expand Down Expand Up @@ -114,7 +122,7 @@ func (cs *Stream) channelInfoWorker(ctx context.Context, proc processor.ChannelI

if _, err := infoFetcher(ctx, proc, id, ""); err != nil {
// if _, err := cs.procChannelInfo(ctx, proc, id, ""); err != nil {
srC <- Result{Type: RTChannelInfo, ChannelID: id, Err: fmt.Errorf("channelInfoWorker: %s: %s", id, err)}
srC <- Result{Type: RTChannelInfo, ChannelID: id, Err: fmt.Errorf("channelInfoWorker: %s: %w", id, err)}
}
seen[id] = struct{}{}
}
Expand Down Expand Up @@ -143,9 +151,26 @@ func (cs *Stream) channelUsersWorker(ctx context.Context, proc processor.Channel
}

if _, err := cs.procChannelUsers(ctx, proc, id, ""); err != nil {
srC <- Result{Type: RTChannelUsers, ChannelID: id, Err: fmt.Errorf("channelUsersWorker: %s: %s", id, err)}
srC <- Result{Type: RTChannelUsers, ChannelID: id, Err: fmt.Errorf("channelUsersWorker: %s: %w", id, err)}
}
seen[id] = struct{}{}
}
}
}

func (cs *Stream) canvas(ctx context.Context, proc processor.Conversations, channel *slack.Channel, fileId string) error {
if fileId == "" {
return nil
}
file, _, _, err := cs.client.GetFileInfoContext(ctx, fileId, 0, 1)
if err != nil {
return fmt.Errorf("canvas: %s: %w", fileId, err)
}
if file == nil {
return errors.New("canvas: file not found")
}
if err := proc.Files(ctx, channel, slack.Message{}, []slack.File{*file}); err != nil {
return fmt.Errorf("canvas: %s: %w", fileId, err)
}
return nil
}
118 changes: 118 additions & 0 deletions stream/stream_workers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package stream

import (
"context"
"errors"
"testing"

"github.com/rusq/slack"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"

"github.com/rusq/slackdump/v3/internal/fixtures"
"github.com/rusq/slackdump/v3/mocks/mock_processor"
"github.com/rusq/slackdump/v3/stream/mock_stream"
)

func TestStream_canvas(t *testing.T) {
testChannel := fixtures.Load[[]*slack.Channel](fixtures.TestChannels)[0]
type args struct {
ctx context.Context
// proc processor.Conversations
channel *slack.Channel
fileId string
}
tests := []struct {
name string
fields *Stream
args args
expectFn func(ms *mock_stream.MockSlacker, mc *mock_processor.MockConversations)
wantErr bool
}{
{
name: "file ID is empty",
fields: &Stream{},
args: args{
ctx: context.Background(),
channel: &slack.Channel{},
fileId: "",
},
wantErr: false,
},
{
name: "getfileinfocontext returns an error",
fields: &Stream{},
args: args{
ctx: context.Background(),
fileId: "F123456",
},
expectFn: func(ms *mock_stream.MockSlacker, mc *mock_processor.MockConversations) {
ms.EXPECT().GetFileInfoContext(gomock.Any(), "F123456", 0, 1).Return(nil, nil, nil, errors.New("getfileinfocontext error"))
},
wantErr: true,
},
{
name: "file not found",
fields: &Stream{},
args: args{
ctx: context.Background(),
fileId: "F123456",
},
expectFn: func(ms *mock_stream.MockSlacker, mc *mock_processor.MockConversations) {
ms.EXPECT().GetFileInfoContext(gomock.Any(), "F123456", 0, 1).Return(nil, nil, nil, nil)
},
wantErr: true,
},
{
name: "success",
fields: &Stream{},
args: args{
ctx: context.Background(),
channel: testChannel,
fileId: "F123456",
},
expectFn: func(ms *mock_stream.MockSlacker, mc *mock_processor.MockConversations) {
ms.EXPECT().
GetFileInfoContext(gomock.Any(), "F123456", 0, 1).
Return(&slack.File{ID: "F123456"}, nil, nil, nil)
mc.EXPECT().
Files(gomock.Any(), testChannel, slack.Message{}, []slack.File{{ID: "F123456"}}).
Return(nil)
},
wantErr: false,
},
{
name: "processor returns an error",
fields: &Stream{},
args: args{
ctx: context.Background(),
channel: testChannel,
fileId: "F123456",
},
expectFn: func(ms *mock_stream.MockSlacker, mc *mock_processor.MockConversations) {
ms.EXPECT().
GetFileInfoContext(gomock.Any(), "F123456", 0, 1).
Return(&slack.File{ID: "F123456"}, nil, nil, nil)
mc.EXPECT().
Files(gomock.Any(), testChannel, slack.Message{}, []slack.File{{ID: "F123456"}}).
Return(assert.AnError)
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
ms := mock_stream.NewMockSlacker(ctrl)
mc := mock_processor.NewMockConversations(ctrl)
if tt.expectFn != nil {
tt.expectFn(ms, mc)
}
cs := tt.fields
cs.client = ms
if err := cs.canvas(tt.args.ctx, mc, tt.args.channel, tt.args.fileId); (err != nil) != tt.wantErr {
t.Errorf("Stream.canvas() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

0 comments on commit 0e26de4

Please sign in to comment.