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 new api for uploading file #152

Merged
merged 4 commits into from
Apr 27, 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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Change Log

## [v0.5.0] -

### Breaking Changes

- **API Deprecation**: As per [Slack's latest API update](https://api.slack.com/changelog/2024-04-a-better-way-to-upload-files-is-here-to-stay), `files.upload` is now deprecated. We have updated our tool to use the new APIs, `files.getUploadURLExternal` and `files.completeUploadExternal`.
- **Channel Specification**: It is no longer possible to specify a `channel` for file uploads. You must now use `channel-id`.
- **Filetype Option Update**: The `-filetype` option has been modified. Please use `-snippet-type` instead for specifying the type of file when uploading to a snippet.

### Changed

* update Go version
* update dependent libraries
* update README

## [v0.4.14] - 2023-09-30

### Changed
Expand Down
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@ The Slack API allows you to specify the filetype of a file when posting it as a
config file name
-channel string
specify channel (unavailable for new Incoming Webhooks)
-channel-id string
specify channel id (for uploading a file)
-debug
debug mode (for developers)
-filename string
specify a file name (for uploading to snippet)
-filetype string
specify a filetype (for uploading to snippet)
[compatible] specify a filetype for uploading to snippet. This option is maintained for compatibility. Please use -snippet-type instead.
-icon-emoji string
specify icon emoji (unavailable for new Incoming Webhooks)
-interval duration
Expand All @@ -68,6 +72,8 @@ The Slack API allows you to specify the filetype of a file when posting it as a
slack url (Incoming Webhooks URL)
-snippet
switch to snippet uploading mode
-snippet-type string
specify a snippet_type (for uploading to snippet)
-token string
token (for uploading to snippet)
-username string
Expand All @@ -92,6 +98,7 @@ The toml file contains the following information.
url = "https://hooks.slack.com/services/**"
token = "xoxp-xxxxx"
channel = "#general"
channel_id = "C12345678"
username = "tester"
icon_emoji = ":rocket:"
interval = "1s"
Expand All @@ -103,13 +110,12 @@ Note:
* You can use the following options to customize your message when posting to Slack as text: `channel`, `username`, `icon_emoji`, and `interval`.
* Due to a recent change in the specification for Incoming Webhooks, it is currently not possible to override the `channel`, `username`, and `icon_emoji` options when posting to Slack. For more information, please refer to [this resource](https://api.slack.com/messaging/webhooks#advanced_message_formatting)
* You can create an Incoming Webhooks URL at https://slack.com/services/new/incoming-webhook
* To post a file as a snippet to Slack, you will need to provide both a `token` and a `channel`.
* To post a file as a snippet to Slack, you will need to provide both a `token` and a `channel_id`.
* The `username` and `icon_emoji` options will be ignored when posting a file as a snippet to Slack.
* For instructions on how to create a token, please see the next section.

Tips:

* If you want to use a different default channel for snippets, you can specify it using the `snippet_channel` option.
* You cannot specify a channel because the slack api support only the `channel_id`.
* If you don't specify `channel_id`, the file will be private. So, if you need to post a file public, you must specify `channel_id`.
* The Slack API can cause delays, so posting might take longer.

### How to create a token

Expand Down Expand Up @@ -149,7 +155,7 @@ Some settings for the Slack API can be provided using environment variables.
NOTIFY_SLACK_WEBHOOK_URL
NOTIFY_SLACK_TOKEN
NOTIFY_SLACK_CHANNEL
NOTIFY_SLACK_SNIPPET_CHANNEL
NOTIFY_SLACK_CHANNEL_ID
NOTIFY_SLACK_USERNAME
NOTIFY_SLACK_ICON_EMOJI
NOTIFY_SLACK_INTERVAL
Expand Down
35 changes: 12 additions & 23 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,17 @@ func (c *CLI) Run(args []string) int {
flags := flag.NewFlagSet("notify_slack", flag.ContinueOnError)
flags.SetOutput(c.errStream)

flags.StringVar(&c.conf.PrimaryChannel, "channel", "", "specify channel (unavailable for new Incoming Webhooks)")
flags.StringVar(&c.conf.Channel, "channel", "", "specify channel (unavailable for new Incoming Webhooks)")
flags.StringVar(&c.conf.ChannelID, "channel-id", "", "specify channel id (for uploading a file)")
flags.StringVar(&c.conf.SlackURL, "slack-url", "", "slack url (Incoming Webhooks URL)")
flags.StringVar(&c.conf.Token, "token", "", "token (for uploading to snippet)")
flags.StringVar(&c.conf.Username, "username", "", "specify username (unavailable for new Incoming Webhooks)")
flags.StringVar(&c.conf.IconEmoji, "icon-emoji", "", "specify icon emoji (unavailable for new Incoming Webhooks)")
flags.DurationVar(&c.conf.Duration, "interval", time.Second, "interval")
flags.StringVar(&tomlFile, "c", "", "config file name")
flags.StringVar(&uploadFilename, "filename", "", "specify a file name (for uploading to snippet)")
flags.StringVar(&filetype, "filetype", "", "specify a filetype (for uploading to snippet)")
flags.StringVar(&filetype, "filetype", "", "[compatible] specify a filetype for uploading to snippet. This option is maintained for compatibility. Please use -snippet-type instead.")
flags.StringVar(&filetype, "snippet-type", "", "specify a snippet_type (for uploading to snippet)")

flags.BoolVar(&snippetMode, "snippet", false, "switch to snippet uploading mode")

Expand Down Expand Up @@ -178,10 +180,7 @@ func (c *CLI) Run(args []string) int {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
defer stop()

channel := c.conf.PrimaryChannel
if channel == "" {
channel = c.conf.Channel
}
channel := c.conf.Channel

param := &slack.PostTextParam{
Channel: channel,
Expand Down Expand Up @@ -216,18 +215,8 @@ func (c *CLI) Run(args []string) int {
return ExitCodeOK
}

func (c *CLI) uploadSnippet(ctx context.Context, filename, uploadFilename, filetype string) error {
channel := c.conf.PrimaryChannel
if channel == "" {
channel = c.conf.SnippetChannel
}
if channel == "" {
channel = c.conf.Channel
}

if channel == "" {
return fmt.Errorf("must specify channel for uploading to snippet")
}
func (c *CLI) uploadSnippet(ctx context.Context, filename, uploadFilename, snippetType string) error {
channelID := c.conf.ChannelID

var reader io.ReadCloser
if filename == "" {
Expand All @@ -254,12 +243,12 @@ func (c *CLI) uploadSnippet(ctx context.Context, filename, uploadFilename, filet
}

param := &slack.PostFileParam{
Channel: channel,
Filename: uploadFilename,
Content: string(content),
Filetype: filetype,
ChannelID: channelID,
Filename: uploadFilename,
SnippetType: snippetType,
}
err = c.sClient.PostFile(ctx, param)

err = c.sClient.PostFile(ctx, param, content)
if err != nil {
return err
}
Expand Down
111 changes: 22 additions & 89 deletions internal/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ import (

"github.com/catatsuy/notify_slack/internal/config"
"github.com/catatsuy/notify_slack/internal/slack"
"github.com/google/go-cmp/cmp"
)

type fakeSlackClient struct {
slack.Slack

FakePostFile func(ctx context.Context, param *slack.PostFileParam) error
FakePostFile func(ctx context.Context, param *slack.PostFileParam, content []byte) error
}

func (c *fakeSlackClient) PostFile(ctx context.Context, param *slack.PostFileParam) error {
return c.FakePostFile(ctx, param)
func (c *fakeSlackClient) PostFile(ctx context.Context, param *slack.PostFileParam, content []byte) error {
return c.FakePostFile(ctx, param, content)
}

func (c *fakeSlackClient) PostText(ctx context.Context, param *slack.PostTextParam) error {
Expand Down Expand Up @@ -48,33 +49,23 @@ func TestUploadSnippet(t *testing.T) {
conf: config.NewConfig(),
}

err := cl.uploadSnippet(context.Background(), "", "", "")
want := "must specify channel"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("error = %v; want %q", err, want)
}

cl.conf.Channel = "normal_channel"
err = cl.uploadSnippet(context.Background(), "testdata/nofile.txt", "", "")
want = "no such file or directory"
cl.conf.ChannelID = "C12345678"
err := cl.uploadSnippet(context.Background(), "testdata/nofile.txt", "", "")
want := "no such file or directory"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("error = %v; want %q", err, want)
}

cl.sClient = &fakeSlackClient{
FakePostFile: func(ctx context.Context, param *slack.PostFileParam) error {
if param.Channel != cl.conf.Channel {
t.Errorf("expected %s; got %s", cl.conf.Channel, param.Channel)
}

FakePostFile: func(ctx context.Context, param *slack.PostFileParam, content []byte) error {
expectedFilename := "testdata/upload.txt"
if param.Filename != expectedFilename {
t.Errorf("expected %s; got %s", expectedFilename, param.Filename)
}

expectedContent := "upload_test\n"
if param.Content != expectedContent {
t.Errorf("expected %q; got %q", expectedContent, param.Content)
if diff := cmp.Diff(expectedContent, string(content)); diff != "" {
t.Errorf("unexpected diff: (-want +got):\n%s", diff)
}

return nil
Expand All @@ -87,19 +78,15 @@ func TestUploadSnippet(t *testing.T) {
}

cl.sClient = &fakeSlackClient{
FakePostFile: func(ctx context.Context, param *slack.PostFileParam) error {
if param.Channel != cl.conf.Channel {
t.Errorf("expected %s; got %s", cl.conf.Channel, param.Channel)
}

FakePostFile: func(ctx context.Context, param *slack.PostFileParam, content []byte) error {
expectedFilename := "overwrite.txt"
if param.Filename != expectedFilename {
t.Errorf("expected %s; got %s", expectedFilename, param.Filename)
}

expectedContent := "upload_test\n"
if param.Content != expectedContent {
t.Errorf("expected %q; got %q", expectedContent, param.Content)
if diff := cmp.Diff(expectedContent, string(content)); diff != "" {
t.Errorf("unexpected diff: (-want +got):\n%s", diff)
}

return nil
Expand All @@ -112,85 +99,31 @@ func TestUploadSnippet(t *testing.T) {
}

cl.sClient = &fakeSlackClient{
FakePostFile: func(ctx context.Context, param *slack.PostFileParam) error {
if param.Channel != cl.conf.Channel {
t.Errorf("expected %s; got %s", cl.conf.Channel, param.Channel)
FakePostFile: func(ctx context.Context, param *slack.PostFileParam, content []byte) error {
if param.ChannelID != cl.conf.ChannelID {
t.Errorf("expected %s; got %s", cl.conf.ChannelID, param.ChannelID)
}

expectedFilename := "overwrite.txt"
if param.Filename != expectedFilename {
t.Errorf("expected %s; got %s", expectedFilename, param.Filename)
}

expectedContent := "upload_test\n"
if param.Content != expectedContent {
t.Errorf("expected %q; got %q", expectedContent, param.Content)
}

expectedFiletype := "diff"
if param.Filetype != expectedFiletype {
t.Errorf("expected %s; got %s", expectedFiletype, param.Filetype)
}

return nil
},
}

err = cl.uploadSnippet(context.Background(), "testdata/upload.txt", "overwrite.txt", "diff")
if err != nil {
t.Errorf("expected nil; got %v", err)
}

cl.conf.SnippetChannel = "snippet_channel"

cl.sClient = &fakeSlackClient{
FakePostFile: func(ctx context.Context, param *slack.PostFileParam) error {
if param.Channel != cl.conf.SnippetChannel {
t.Errorf("expected %s; got %s", cl.conf.SnippetChannel, param.Channel)
}

expectedFilename := "testdata/upload.txt"
if param.Filename != expectedFilename {
t.Errorf("expected %s; got %s", expectedFilename, param.Filename)
}

expectedContent := "upload_test\n"
if param.Content != expectedContent {
t.Errorf("expected %q; got %q", expectedContent, param.Content)
}

return nil
},
}

err = cl.uploadSnippet(context.Background(), "testdata/upload.txt", "", "")
if err != nil {
t.Errorf("expected nil; got %v", err)
}

cl.conf.PrimaryChannel = "primary_channel"

cl.sClient = &fakeSlackClient{
FakePostFile: func(ctx context.Context, param *slack.PostFileParam) error {
if param.Channel != cl.conf.PrimaryChannel {
t.Errorf("expected %s; got %s", cl.conf.PrimaryChannel, param.Channel)
}

expectedFilename := "testdata/upload.txt"
if param.Filename != expectedFilename {
t.Errorf("expected %s; got %s", expectedFilename, param.Filename)
expectedSnippetType := "diff"
if param.SnippetType != expectedSnippetType {
t.Errorf("expected %s; got %s", expectedSnippetType, param.SnippetType)
}

expectedContent := "upload_test\n"
if param.Content != expectedContent {
t.Errorf("expected %q; got %q", expectedContent, param.Content)
if diff := cmp.Diff(expectedContent, string(content)); diff != "" {
t.Errorf("unexpected diff: (-want +got):\n%s", diff)
}

return nil
},
}

err = cl.uploadSnippet(context.Background(), "testdata/upload.txt", "", "")
err = cl.uploadSnippet(context.Background(), "testdata/upload.txt", "overwrite.txt", "diff")
if err != nil {
t.Errorf("expected nil; got %v", err)
}
Expand Down
24 changes: 17 additions & 7 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ var (
type Config struct {
SlackURL string
Token string
PrimaryChannel string
Channel string
SnippetChannel string
ChannelID string
Username string
IconEmoji string
Duration time.Duration
Expand All @@ -42,7 +42,13 @@ func (c *Config) LoadEnv() error {
}

if c.SnippetChannel == "" {
c.SnippetChannel = os.Getenv("NOTIFY_SLACK_SNIPPET_CHANNEL")
if os.Getenv("NOTIFY_SLACK_SNIPPET_CHANNEL") != "" {
return fmt.Errorf("the NOTIFY_SLACK_SNIPPET_CHANNEL option is deprecated")
}
}

if c.ChannelID == "" {
c.ChannelID = os.Getenv("NOTIFY_SLACK_CHANNEL_ID")
}

if c.Username == "" {
Expand Down Expand Up @@ -70,6 +76,7 @@ type slackConfig struct {
Token string
Channel string
SnippetChannel string `toml:"snippet_channel"`
ChannelID string `toml:"channel_id"`
Username string
IconEmoji string `toml:"icon_emoji"`
Interval string
Expand Down Expand Up @@ -109,16 +116,19 @@ func (c *Config) LoadTOML(filename string) error {
c.Channel = slackConfig.Channel
}
}
if c.SnippetChannel == "" {
if slackConfig.SnippetChannel != "" {
c.SnippetChannel = slackConfig.SnippetChannel
}
}
if c.Username == "" {
if slackConfig.Username != "" {
c.Username = slackConfig.Username
}
}
if slackConfig.SnippetChannel != "" {
return fmt.Errorf("the snippet_channel option is deprecated")
}
if c.ChannelID == "" {
if slackConfig.ChannelID != "" {
c.ChannelID = slackConfig.ChannelID
}
}
if c.IconEmoji == "" {
if slackConfig.IconEmoji != "" {
c.IconEmoji = slackConfig.IconEmoji
Expand Down
Loading
Loading