diff --git a/command/commands.go b/command/commands.go index f84a2642..d435fbc5 100644 --- a/command/commands.go +++ b/command/commands.go @@ -41,6 +41,7 @@ func GetCommands(slackClient client.SlackClient, cfg config.Config) *bot.Command NewRandomCommand(base), NewHelpCommand(base, commands), newUserStatusCommand(base), + NewRequestCommand(base), weather.NewWeatherCommand(base, cfg.OpenWeather), diff --git a/command/request.go b/command/request.go new file mode 100644 index 00000000..5a7defbe --- /dev/null +++ b/command/request.go @@ -0,0 +1,78 @@ +package command + +import ( + "context" + "net/http" + + "github.com/innogames/slack-bot/v2/bot" + "github.com/innogames/slack-bot/v2/bot/matcher" + "github.com/innogames/slack-bot/v2/bot/msg" + "github.com/innogames/slack-bot/v2/client" + "github.com/pkg/errors" +) + +func NewRequestCommand(base bot.BaseCommand) bot.Command { + return &requestCommand{base} +} + +type requestCommand struct { + bot.BaseCommand +} + +func (c requestCommand) GetMatcher() matcher.Matcher { + return matcher.NewOptionMatcher( + "request", + []string{"method", "url"}, + c.doRequest, + c.SlackClient, + ) +} + +func (c requestCommand) doRequest(match matcher.Result, message msg.Message) { + method := match.GetString("method") + if method == "" { + method = "GET" + } + url := match.GetString("url") + if url == "" { + c.ReplyError(message, errors.New("please provide a valid url")) + return + } + + request, err := http.NewRequestWithContext(context.Background(), method, url, nil) + if err != nil { + c.ReplyError(message, errors.Wrap(err, "invalid request")) + return + } + + httpClient := client.GetHTTPClient() + response, err := httpClient.Do(request) + if err != nil { + c.AddReaction("❌", message) + c.ReplyError(message, errors.Wrap(err, "request failed")) + return + } + defer response.Body.Close() + + // check success status + if response.StatusCode >= 400 { + c.AddReaction("❌", message) + c.ReplyError(message, errors.New("request failed with status "+response.Status)) + return + } + + c.AddReaction("white_check_mark", message) +} + +func (c requestCommand) GetHelp() []bot.Help { + return []bot.Help{ + { + Command: "request --url= [--method=]", + Description: "send a request to the given url", + Examples: []string{ + "request GET https://example.com", + "request POST https://jenkins.exmaple.com/webhook?auth=1", + }, + }, + } +} diff --git a/command/request_test.go b/command/request_test.go new file mode 100644 index 00000000..92056bd5 --- /dev/null +++ b/command/request_test.go @@ -0,0 +1,81 @@ +package command + +import ( + "testing" + + "github.com/innogames/slack-bot/v2/bot" + "github.com/innogames/slack-bot/v2/bot/msg" + "github.com/innogames/slack-bot/v2/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestRequest(t *testing.T) { + slackClient := mocks.NewSlackClient(t) + base := bot.BaseCommand{SlackClient: slackClient} + + reaction := NewRequestCommand(base) + + command := bot.Commands{} + command.AddCommand(reaction) + + t.Run("invalid command", func(t *testing.T) { + message := msg.Message{} + message.Text = "i need a reaction" + + actual := command.Run(message) + assert.False(t, actual) + }) + + t.Run("without URL", func(t *testing.T) { + message := msg.Message{} + message.Text = "request --method=GET" + + mocks.AssertError(slackClient, message, "please provide a valid url") + + actual := command.Run(message) + assert.True(t, actual) + }) + + t.Run("invalid url schema", func(t *testing.T) { + message := msg.Message{} + message.Text = "request --url=://example" + + mocks.AssertError(slackClient, message, "invalid request: parse \"://example\": missing protocol scheme") + + actual := command.Run(message) + assert.True(t, actual) + }) + + t.Run("invalid destination", func(t *testing.T) { + message := msg.Message{} + message.Text = "request --method=GET --url=https://127.111.111.111" + + mocks.AssertReaction(slackClient, "❌", message) + slackClient.On("ReplyError", message, mock.Anything).Once().Return("") + + actual := command.Run(message) + assert.True(t, actual) + }) + + t.Run("test 200", func(t *testing.T) { + message := msg.Message{} + message.Text = "request --url=https://httpstat.us/200" + + mocks.AssertReaction(slackClient, "white_check_mark", message) + + actual := command.Run(message) + assert.True(t, actual) + }) + + t.Run("test 404", func(t *testing.T) { + message := msg.Message{} + message.Text = "request --url=https://httpstat.us/404" + + mocks.AssertReaction(slackClient, "❌", message) + mocks.AssertError(slackClient, message, "request failed with status 404 Not Found") + + actual := command.Run(message) + assert.True(t, actual) + }) +}