[MM-62427] Add message attachments validation (#30180)
* Add message attachments validation * Add props validation * Validate slack attachment fields * Update tests and library usage * Improve interactive dialog error for length checks * Allow predefined colors for slack attachments * Fix TestPostAction * Use const for data source * Add tests * Cleanup unused props * Add happy path tests * lint fixes * Add validation for PostActionOptions
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
5609489e86
Коммит
9b5d8d52bf
@@ -6,9 +6,15 @@ package model
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
var linkWithTextRegex = regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
|
||||
var (
|
||||
linkWithTextRegex = regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
|
||||
hexColorRegex = regexp.MustCompile(`^#[0-9a-fA-F]{6}$`)
|
||||
)
|
||||
|
||||
type SlackAttachment struct {
|
||||
Id int64 `json:"id"`
|
||||
@@ -30,6 +36,78 @@ type SlackAttachment struct {
|
||||
Actions []*PostAction `json:"actions,omitempty"`
|
||||
}
|
||||
|
||||
func (s *SlackAttachment) IsValid() error {
|
||||
var multiErr *multierror.Error
|
||||
|
||||
if s.Color != "" {
|
||||
validStyles := []string{"good", "warning", "danger"}
|
||||
// If not a predefined style, check if it's a hex color
|
||||
if !slices.Contains(validStyles, s.Color) && !hexColorRegex.MatchString(s.Color) {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("invalid style '%s' - must be one of [good, warning, danger] or a hex color", s.Color))
|
||||
}
|
||||
}
|
||||
|
||||
if s.AuthorLink != "" {
|
||||
if s.AuthorName == "" {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("author link cannot be set without author name"))
|
||||
}
|
||||
|
||||
if !IsValidHTTPURL(s.AuthorLink) {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("invalid author link URL"))
|
||||
}
|
||||
}
|
||||
|
||||
if s.AuthorIcon != "" && !IsValidHTTPURL(s.AuthorIcon) {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("invalid author icon URL"))
|
||||
}
|
||||
|
||||
if s.TitleLink != "" {
|
||||
if s.Title == "" {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("title link cannot be set without title"))
|
||||
}
|
||||
|
||||
if !IsValidHTTPURL(s.TitleLink) {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("invalid title link URL"))
|
||||
}
|
||||
}
|
||||
|
||||
for _, field := range s.Fields {
|
||||
if err := field.IsValid(); err != nil {
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
if s.ImageURL != "" && !IsValidHTTPURL(s.ImageURL) {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("invalid image URL"))
|
||||
}
|
||||
|
||||
if s.ThumbURL != "" && !IsValidHTTPURL(s.ThumbURL) {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("invalid thumb URL"))
|
||||
}
|
||||
|
||||
if s.FooterIcon != "" && !IsValidHTTPURL(s.FooterIcon) {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("invalid footer icon URL"))
|
||||
}
|
||||
|
||||
// Validate timestamp is either string or int64
|
||||
if s.Timestamp != nil {
|
||||
switch s.Timestamp.(type) {
|
||||
case string, int64:
|
||||
// Valid types
|
||||
default:
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("timestamp must be either a string or int64"))
|
||||
}
|
||||
}
|
||||
|
||||
for i, action := range s.Actions {
|
||||
if err := action.IsValid(); err != nil {
|
||||
multiErr = multierror.Append(multiErr, multierror.Prefix(err, fmt.Sprintf("action at index %d is invalid:", i)))
|
||||
}
|
||||
}
|
||||
|
||||
return multiErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
func (s *SlackAttachment) Equals(input *SlackAttachment) bool {
|
||||
// Direct comparison of simple types
|
||||
|
||||
@@ -120,6 +198,21 @@ type SlackAttachmentField struct {
|
||||
Short SlackCompatibleBool `json:"short"`
|
||||
}
|
||||
|
||||
func (s *SlackAttachmentField) IsValid() error {
|
||||
var multiErr *multierror.Error
|
||||
|
||||
if s.Value != nil {
|
||||
switch s.Value.(type) {
|
||||
case string, int:
|
||||
// Valid types
|
||||
default:
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("value must be either a string or int"))
|
||||
}
|
||||
}
|
||||
|
||||
return multiErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
func (s *SlackAttachmentField) Equals(input *SlackAttachmentField) bool {
|
||||
if s.Title != input.Title {
|
||||
return false
|
||||
@@ -188,7 +281,7 @@ func ParseSlackAttachment(post *Post, attachments []*SlackAttachment) {
|
||||
}
|
||||
postAttachments = append(postAttachments, attachment)
|
||||
}
|
||||
post.AddProp("attachments", postAttachments)
|
||||
post.AddProp(PostPropsAttachments, postAttachments)
|
||||
}
|
||||
|
||||
func ParseSlackLinksToMarkdown(text string) string {
|
||||
|
||||
Ссылка в новой задаче
Block a user