Skip to content

Commit

Permalink
Merge pull request #8 from pylon-labs/bennett/sync-upstream-v13
Browse files Browse the repository at this point in the history
Bennett/sync upstream v13
  • Loading branch information
bamo authored Oct 6, 2024
2 parents 224dd24 + 9b4f5ba commit 26f6c99
Show file tree
Hide file tree
Showing 52 changed files with 2,783 additions and 728 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.test
*~
.idea/
/vendor/
2 changes: 2 additions & 0 deletions block.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
MBTInput MessageBlockType = "input"
MBTHeader MessageBlockType = "header"
MBTRichText MessageBlockType = "rich_text"
MBTVideo MessageBlockType = "video"
)

// Block defines an interface all block types should implement
Expand All @@ -39,6 +40,7 @@ type BlockAction struct {
Type ActionType `json:"type"`
Text TextBlockObject `json:"text"`
Value string `json:"value"`
Files []File `json:"files"`
ActionTs string `json:"action_ts"`
SelectedOption OptionBlockObject `json:"selected_option"`
SelectedOptions []OptionBlockObject `json:"selected_options"`
Expand Down
21 changes: 18 additions & 3 deletions block_conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package slack

import (
"encoding/json"
"errors"
"fmt"
)

Expand Down Expand Up @@ -66,8 +65,12 @@ func (b *Blocks) UnmarshalJSON(data []byte) error {
block = &InputBlock{}
case "rich_text":
block = &RichTextBlock{}
case "rich_text_input":
block = &RichTextBlock{}
case "section":
block = &SectionBlock{}
case "video":
block = &VideoBlock{}
default:
block = &UnknownBlock{}
}
Expand Down Expand Up @@ -114,6 +117,8 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error {
e = &DateTimePickerBlockElement{}
case "plain_text_input":
e = &PlainTextInputBlockElement{}
case "rich_text_input":
e = &RichTextInputBlockElement{}
case "email_text_input":
e = &EmailTextInputBlockElement{}
case "url_text_input":
Expand All @@ -130,8 +135,10 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error {
e = &RadioButtonsBlockElement{}
case "number_input":
e = &NumberInputBlockElement{}
case "file_input":
e = &FileInputBlockElement{}
default:
return errors.New("unsupported block element type")
return fmt.Errorf("unsupported block element type %v", s.TypeVal)
}

if err := json.Unmarshal(a.Element, e); err != nil {
Expand Down Expand Up @@ -196,6 +203,8 @@ func (b *BlockElements) UnmarshalJSON(data []byte) error {
blockElement = &DateTimePickerBlockElement{}
case "plain_text_input":
blockElement = &PlainTextInputBlockElement{}
case "rich_text_input":
blockElement = &RichTextInputBlockElement{}
case "email_text_input":
blockElement = &EmailTextInputBlockElement{}
case "url_text_input":
Expand Down Expand Up @@ -298,6 +307,12 @@ func (a *Accessory) UnmarshalJSON(data []byte) error {
return err
}
a.PlainTextInputElement = element.(*PlainTextInputBlockElement)
case "rich_text_input":
element, err := unmarshalBlockElement(r, &RichTextInputBlockElement{})
if err != nil {
return err
}
a.RichTextInputElement = element.(*RichTextInputBlockElement)
case "radio_buttons":
element, err := unmarshalBlockElement(r, &RadioButtonsBlockElement{})
if err != nil {
Expand Down Expand Up @@ -429,7 +444,7 @@ func (e *ContextElements) UnmarshalJSON(data []byte) error {

e.Elements = append(e.Elements, elem.(*ImageBlockElement))
default:
return errors.New("unsupported context element type")
return fmt.Errorf("unsupported context element type %v", contextElementType)
}
}

Expand Down
74 changes: 74 additions & 0 deletions block_element.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ const (
METDatetimepicker MessageElementType = "datetimepicker"
METPlainTextInput MessageElementType = "plain_text_input"
METRadioButtons MessageElementType = "radio_buttons"
METRichTextInput MessageElementType = "rich_text_input"
METEmailTextInput MessageElementType = "email_text_input"
METURLTextInput MessageElementType = "url_text_input"
METNumber MessageElementType = "number_input"
METFileInput MessageElementType = "file_input"

MixedElementImage MixedElementType = "mixed_image"
MixedElementText MixedElementType = "mixed_text"
Expand Down Expand Up @@ -51,6 +53,7 @@ type Accessory struct {
DatePickerElement *DatePickerBlockElement
TimePickerElement *TimePickerBlockElement
PlainTextInputElement *PlainTextInputBlockElement
RichTextInputElement *RichTextInputBlockElement
RadioButtonsElement *RadioButtonsBlockElement
SelectElement *SelectBlockElement
MultiSelectElement *MultiSelectBlockElement
Expand All @@ -73,6 +76,8 @@ func NewAccessory(element BlockElement) *Accessory {
return &Accessory{TimePickerElement: element.(*TimePickerBlockElement)}
case *PlainTextInputBlockElement:
return &Accessory{PlainTextInputElement: element.(*PlainTextInputBlockElement)}
case *RichTextInputBlockElement:
return &Accessory{RichTextInputElement: element.(*RichTextInputBlockElement)}
case *RadioButtonsBlockElement:
return &Accessory{RadioButtonsElement: element.(*RadioButtonsBlockElement)}
case *SelectBlockElement:
Expand Down Expand Up @@ -177,6 +182,12 @@ func (s *ButtonBlockElement) WithConfirm(confirm *ConfirmationBlockObject) *Butt
return s
}

// WithURL adds a URL for the button to link to and returns the modified ButtonBlockElement
func (s *ButtonBlockElement) WithURL(url string) *ButtonBlockElement {
s.URL = url
return s
}

// NewButtonBlockElement returns an instance of a new button element to be used within a block
func NewButtonBlockElement(actionID, value string, text *TextBlockObject) *ButtonBlockElement {
return &ButtonBlockElement{
Expand Down Expand Up @@ -509,6 +520,32 @@ func NewPlainTextInputBlockElement(placeholder *TextBlockObject, actionID string
}
}

// RichTextInputBlockElement creates a field where allows users to enter formatted text
// in a WYSIWYG composer, offering the same messaging writing experience as in Slack
// More Information: https://api.slack.com/reference/block-kit/block-elements#rich_text_input
type RichTextInputBlockElement struct {
Type MessageElementType `json:"type"`
ActionID string `json:"action_id,omitempty"`
Placeholder *TextBlockObject `json:"placeholder,omitempty"`
InitialValue string `json:"initial_value,omitempty"`
DispatchActionConfig *DispatchActionConfig `json:"dispatch_action_config,omitempty"`
FocusOnLoad bool `json:"focus_on_load,omitempty"`
}

// ElementType returns the type of the Element
func (s RichTextInputBlockElement) ElementType() MessageElementType {
return s.Type
}

// NewRichTextInputBlockElement returns an instance of a rich-text input element
func NewRichTextInputBlockElement(placeholder *TextBlockObject, actionID string) *RichTextInputBlockElement {
return &RichTextInputBlockElement{
Type: METRichTextInput,
ActionID: actionID,
Placeholder: placeholder,
}
}

// CheckboxGroupsBlockElement defines an element which allows users to choose
// one or more items from a list of possible options.
//
Expand Down Expand Up @@ -591,3 +628,40 @@ func NewNumberInputBlockElement(placeholder *TextBlockObject, actionID string, i
IsDecimalAllowed: isDecimalAllowed,
}
}

// FileInputBlockElement creates a field where a user can upload a file.
//
// File input elements are currently only available in modals.
//
// More Information: https://api.slack.com/reference/block-kit/block-elements#file_input
type FileInputBlockElement struct {
Type MessageElementType `json:"type"`
ActionID string `json:"action_id,omitempty"`
FileTypes []string `json:"filetypes,omitempty"`
MaxFiles int `json:"max_files,omitempty"`
}

// ElementType returns the type of the Element
func (s FileInputBlockElement) ElementType() MessageElementType {
return s.Type
}

// NewFileInputBlockElement returns an instance of a file input element
func NewFileInputBlockElement(actionID string) *FileInputBlockElement {
return &FileInputBlockElement{
Type: METFileInput,
ActionID: actionID,
}
}

// WithFileTypes sets the file types that can be uploaded
func (s *FileInputBlockElement) WithFileTypes(fileTypes ...string) *FileInputBlockElement {
s.FileTypes = fileTypes
return s
}

// WithMaxFiles sets the maximum number of files that can be uploaded
func (s *FileInputBlockElement) WithMaxFiles(maxFiles int) *FileInputBlockElement {
s.MaxFiles = maxFiles
return s
}
32 changes: 32 additions & 0 deletions block_element_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ func TestWithStyleForButtonElement(t *testing.T) {

}

func TestWithURLForButtonElement(t *testing.T) {

btnTxt := NewTextBlockObject("plain_text", "Next 2 Results", false, false)
btnElement := NewButtonBlockElement("test", "click_me_123", btnTxt)

btnElement.WithURL("https://foo.bar")
assert.Equal(t, btnElement.URL, "https://foo.bar")

}

func TestNewOptionsSelectBlockElement(t *testing.T) {

testOptionText := NewTextBlockObject("plain_text", "Option One", false, false)
Expand Down Expand Up @@ -148,6 +158,12 @@ func TestNewPlainTextInputBlockElement(t *testing.T) {

}

func TestNewRichTextInputBlockElement(t *testing.T) {
richTextInputElement := NewRichTextInputBlockElement(nil, "test")
assert.Equal(t, string(richTextInputElement.Type), "rich_text_input")
assert.Equal(t, richTextInputElement.ActionID, "test")
}

func TestNewEmailTextInputBlockElement(t *testing.T) {
emailTextInputElement := NewEmailTextInputBlockElement(nil, "[email protected]")

Expand Down Expand Up @@ -215,3 +231,19 @@ func TestNewNumberInputBlockElement(t *testing.T) {
assert.Equal(t, numberInputElement.IsDecimalAllowed, true)

}

func TestNewFileInputBlockElement(t *testing.T) {

fileInputElement := NewFileInputBlockElement("test")

assert.Equal(t, string(fileInputElement.Type), "file_input")
assert.Equal(t, fileInputElement.ActionID, "test")

fileInputElement.WithFileTypes("jpg", "png")
assert.Equal(t, len(fileInputElement.FileTypes), 2)
assert.Contains(t, fileInputElement.FileTypes, "jpg")
assert.Contains(t, fileInputElement.FileTypes, "png")

fileInputElement.WithMaxFiles(10)
assert.Equal(t, fileInputElement.MaxFiles, 10)
}
12 changes: 11 additions & 1 deletion block_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ func (s TextBlockObject) Validate() error {
return errors.New("emoji cannot be true in mrkdown")
}

// https://api.slack.com/reference/block-kit/composition-objects#text__fields
if len(s.Text) == 0 {
return errors.New("text must have a minimum length of 1")
}

// https://api.slack.com/reference/block-kit/composition-objects#text__fields
if len(s.Text) > 3000 {
return errors.New("text cannot be longer than 3000 characters")
}

return nil
}

Expand Down Expand Up @@ -177,7 +187,7 @@ type ConfirmationBlockObject struct {
Title *TextBlockObject `json:"title"`
Text *TextBlockObject `json:"text"`
Confirm *TextBlockObject `json:"confirm"`
Deny *TextBlockObject `json:"deny"`
Deny *TextBlockObject `json:"deny,omitempty"`
Style Style `json:"style,omitempty"`
}

Expand Down
19 changes: 19 additions & 0 deletions block_object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package slack

import (
"errors"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -126,6 +127,24 @@ func TestValidateTextBlockObject(t *testing.T) {
},
expected: errors.New("emoji cannot be true in mrkdown"),
},
{
input: TextBlockObject{
Type: "mrkdwn",
Text: "",
Emoji: false,
Verbatim: false,
},
expected: errors.New("text must have a minimum length of 1"),
},
{
input: TextBlockObject{
Type: "mrkdwn",
Text: strings.Repeat("a", 3001),
Emoji: false,
Verbatim: false,
},
expected: errors.New("text cannot be longer than 3000 characters"),
},
}

for _, test := range tests {
Expand Down
Loading

0 comments on commit 26f6c99

Please sign in to comment.