keel/bot/approvals.go

179 lines
4.6 KiB
Go

package bot
import (
"bytes"
"context"
"fmt"
"strings"
"github.com/keel-hq/keel/approvals"
"github.com/keel-hq/keel/bot/formatter"
"github.com/keel-hq/keel/types"
log "github.com/sirupsen/logrus"
)
type BotRequestApproval func(req *types.Approval) error
type BotReplyApproval func(approval *types.Approval) error
func (bm *BotManager) SubscribeForApprovals(ctx context.Context, approval BotRequestApproval) error {
approvalsCh, err := bm.approvalsManager.Subscribe(ctx)
if err != nil {
log.Errorf("bot.subscribeForApprovals(): %s", err.Error())
return err
}
for {
select {
case <-ctx.Done():
return nil
case a := <-approvalsCh:
err = approval(a)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"approval": a.Identifier,
}).Error("bot.subscribeForApprovals: approval request failed")
}
}
}
}
func (bm *BotManager) ProcessApprovalResponses(ctx context.Context, reply BotReplyApproval) error {
for {
select {
case <-ctx.Done():
return nil
case resp := <-bm.approvalsRespCh:
switch resp.Status {
case types.ApprovalStatusApproved:
err := bm.processApprovedResponse(resp, reply)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("bot.processApprovalResponses: failed to process approval response message")
}
case types.ApprovalStatusRejected:
err := bm.processRejectedResponse(resp, reply)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("bot.processApprovalResponses: failed to process approval reject response message")
}
}
}
}
}
func (bm *BotManager) processApprovedResponse(approvalResponse *ApprovalResponse, reply BotReplyApproval) error {
trimmed := strings.TrimPrefix(approvalResponse.Text, ApprovalResponseKeyword)
identifiers := strings.Split(trimmed, " ")
if len(identifiers) == 0 {
return nil
}
for _, identifier := range identifiers {
if identifier == "" {
continue
}
approval, err := bm.approvalsManager.Approve(identifier, approvalResponse.User)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"identifier": identifier,
}).Error("bot.processApprovedResponse: failed to approve")
continue
}
err = reply(approval)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"identifier": identifier,
}).Error("bot.processApprovedResponse: got error while replying after processing approved approval")
}
}
return nil
}
func (bm *BotManager) processRejectedResponse(approvalResponse *ApprovalResponse, reply BotReplyApproval) error {
trimmed := strings.TrimPrefix(approvalResponse.Text, RejectResponseKeyword)
identifiers := strings.Split(trimmed, " ")
if len(identifiers) == 0 {
return nil
}
for _, identifier := range identifiers {
approval, err := bm.approvalsManager.Reject(identifier)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"identifier": identifier,
}).Error("bot.processApprovedResponse: failed to reject")
continue
}
err = reply(approval)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"identifier": identifier,
}).Error("bot.processApprovedResponse: got error while replying after processing rejected approval")
}
}
return nil
}
func ApprovalsResponse(approvalsManager approvals.Manager) string {
approvals, err := approvalsManager.List()
if err != nil {
return fmt.Sprintf("got error while fetching approvals: %s", err)
}
if len(approvals) == 0 {
return fmt.Sprintf("there are currently no request waiting to be approved.")
}
buf := &bytes.Buffer{}
approvalCtx := formatter.Context{
Output: buf,
Format: formatter.NewApprovalsFormat(formatter.TableFormatKey, false),
}
err = formatter.ApprovalWrite(approvalCtx, approvals)
if err != nil {
return fmt.Sprintf("got error while formatting approvals: %s", err)
}
return buf.String()
}
func IsApproval(eventUser string, eventText string) (resp *ApprovalResponse, ok bool) {
if strings.HasPrefix(strings.ToLower(eventText), ApprovalResponseKeyword) {
return &ApprovalResponse{
User: eventUser,
Status: types.ApprovalStatusApproved,
Text: eventText,
}, true
}
if strings.HasPrefix(strings.ToLower(eventText), RejectResponseKeyword) {
return &ApprovalResponse{
User: eventUser,
Status: types.ApprovalStatusRejected,
Text: eventText,
}, true
}
return nil, false
}
func RemoveApprovalHandler(identifier string, approvalsManager approvals.Manager) string {
err := approvalsManager.Delete(identifier)
if err != nil {
return fmt.Sprintf("failed to remove '%s' approval: %s.", identifier, err)
}
return fmt.Sprintf("approval '%s' removed.", identifier)
}