From 00b433c0800172fc7efa67e4bbdcc12222b409c1 Mon Sep 17 00:00:00 2001 From: Caleb McKay Date: Wed, 27 Nov 2024 05:35:57 +0000 Subject: [PATCH 1/3] Add guregu/null package dependency --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index 5cc8e1a7e..ba4648b9a 100644 --- a/go.mod +++ b/go.mod @@ -9,4 +9,5 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 + gopkg.in/guregu/null.v4 v4.0.0 ) diff --git a/go.sum b/go.sum index 194956433..7b2e6ceca 100644 --- a/go.sum +++ b/go.sum @@ -12,3 +12,5 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= +gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= From 31a57fa352529219004f477c68f30c4772b3b0c3 Mon Sep 17 00:00:00 2001 From: Caleb McKay Date: Wed, 27 Nov 2024 05:37:43 +0000 Subject: [PATCH 2/3] Change TextBlockObject's emoji field to a nullable Bool value --- attachments_test.go | 1 + block_object.go | 16 +++++++++------- block_object_test.go | 33 ++++++++++++++++++++++++++------- block_section_test.go | 2 +- chat_test.go | 2 +- interactions_test.go | 18 ++++++++++++------ views_test.go | 9 ++++++--- webhooks_test.go | 4 ++-- 8 files changed, 58 insertions(+), 27 deletions(-) diff --git a/attachments_test.go b/attachments_test.go index 4b951f110..cbb139c38 100644 --- a/attachments_test.go +++ b/attachments_test.go @@ -19,6 +19,7 @@ func TestAttachment_UnmarshalMarshalJSON_WithBlocks(t *testing.T) { "text": { "type": "mrkdwn", "text": "Pick something:", + "emoji": null, "verbatim": true }, "accessory": { diff --git a/block_object.go b/block_object.go index a3e78d4ea..7b6ccd427 100644 --- a/block_object.go +++ b/block_object.go @@ -3,6 +3,8 @@ package slack import ( "encoding/json" "errors" + + "gopkg.in/guregu/null.v4" ) // Block Objects are also known as Composition Objects @@ -120,10 +122,10 @@ func unmarshalBlockObject(r json.RawMessage, object blockObject) (blockObject, e // // More Information: https://api.slack.com/reference/messaging/composition-objects#text type TextBlockObject struct { - Type string `json:"type"` - Text string `json:"text"` - Emoji bool `json:"emoji,omitempty"` - Verbatim bool `json:"verbatim,omitempty"` + Type string `json:"type"` + Text string `json:"text"` + Emoji null.Bool `json:"emoji,omitempty"` + Verbatim bool `json:"verbatim,omitempty"` } // validateType enforces block objects for element and block parameters @@ -143,7 +145,7 @@ func (s TextBlockObject) Validate() error { } // https://github.com/slack-go/slack/issues/881 - if s.Type == "mrkdwn" && s.Emoji { + if s.Type == "mrkdwn" && s.Emoji.ValueOrZero() == true { return errors.New("emoji cannot be true in mrkdown") } @@ -161,11 +163,11 @@ func (s TextBlockObject) Validate() error { } // NewTextBlockObject returns an instance of a new Text Block Object -func NewTextBlockObject(elementType, text string, emoji, verbatim bool) *TextBlockObject { +func NewTextBlockObject(elementType, text string, emoji bool, verbatim bool) *TextBlockObject { return &TextBlockObject{ Type: elementType, Text: text, - Emoji: emoji, + Emoji: null.BoolFrom(emoji), Verbatim: verbatim, } } diff --git a/block_object_test.go b/block_object_test.go index 1f4874a02..3499327e9 100644 --- a/block_object_test.go +++ b/block_object_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "gopkg.in/guregu/null.v4" ) func TestNewImageBlockObject(t *testing.T) { @@ -24,7 +25,7 @@ func TestNewTextBlockObject(t *testing.T) { assert.Equal(t, textObject.Type, "plain_text") assert.Equal(t, textObject.Text, "test") - assert.True(t, textObject.Emoji, "Emoji property should be true") + assert.True(t, textObject.Emoji.ValueOrZero(), "Emoji property should be true") assert.False(t, textObject.Verbatim, "Verbatim should be false") } @@ -95,7 +96,25 @@ func TestValidateTextBlockObject(t *testing.T) { input: TextBlockObject{ Type: "plain_text", Text: "testText", - Emoji: false, + Emoji: null.BoolFrom(false), + Verbatim: false, + }, + expected: nil, + }, + { + input: TextBlockObject{ + Type: "plain_text", + Text: "testText", + Emoji: null.BoolFromPtr(nil), + Verbatim: false, + }, + expected: nil, + }, + { + input: TextBlockObject{ + Type: "mrkdwn", + Text: "testText", + Emoji: null.BoolFrom(false), Verbatim: false, }, expected: nil, @@ -104,7 +123,7 @@ func TestValidateTextBlockObject(t *testing.T) { input: TextBlockObject{ Type: "mrkdwn", Text: "testText", - Emoji: false, + Emoji: null.BoolFromPtr(nil), Verbatim: false, }, expected: nil, @@ -113,7 +132,7 @@ func TestValidateTextBlockObject(t *testing.T) { input: TextBlockObject{ Type: "invalid", Text: "testText", - Emoji: false, + Emoji: null.BoolFrom(false), Verbatim: false, }, expected: errors.New("type must be either of plain_text or mrkdwn"), @@ -122,7 +141,7 @@ func TestValidateTextBlockObject(t *testing.T) { input: TextBlockObject{ Type: "mrkdwn", Text: "testText", - Emoji: true, + Emoji: null.BoolFrom(true), Verbatim: false, }, expected: errors.New("emoji cannot be true in mrkdown"), @@ -131,7 +150,7 @@ func TestValidateTextBlockObject(t *testing.T) { input: TextBlockObject{ Type: "mrkdwn", Text: "", - Emoji: false, + Emoji: null.BoolFrom(false), Verbatim: false, }, expected: errors.New("text must have a minimum length of 1"), @@ -140,7 +159,7 @@ func TestValidateTextBlockObject(t *testing.T) { input: TextBlockObject{ Type: "mrkdwn", Text: strings.Repeat("a", 3001), - Emoji: false, + Emoji: null.BoolFrom(false), Verbatim: false, }, expected: errors.New("text cannot be longer than 3000 characters"), diff --git a/block_section_test.go b/block_section_test.go index 68329679b..3a3cbbac7 100644 --- a/block_section_test.go +++ b/block_section_test.go @@ -30,7 +30,7 @@ func TestNewBlockSectionContainsAddedTextBlockAndAccessory(t *testing.T) { textBlockInSection := sectionBlock.Text assert.Equal(t, textBlockInSection.Text, textBlockObject.Text) assert.Equal(t, textBlockInSection.Type, textBlockObject.Type) - assert.True(t, textBlockInSection.Emoji) + assert.True(t, textBlockInSection.Emoji.ValueOrZero()) assert.False(t, textBlockInSection.Verbatim) assert.Equal(t, sectionBlock.Accessory.ImageElement, conflictImage) } diff --git a/chat_test.go b/chat_test.go index 917ff965e..ab326f2e4 100644 --- a/chat_test.go +++ b/chat_test.go @@ -81,7 +81,7 @@ func TestPostMessage(t *testing.T) { } blocks := []Block{NewContextBlock("context", NewTextBlockObject(PlainTextType, "hello", false, false))} - blockStr := `[{"type":"context","block_id":"context","elements":[{"type":"plain_text","text":"hello"}]}]` + blockStr := `[{"type":"context","block_id":"context","elements":[{"type":"plain_text","text":"hello","emoji":false}]}]` tests := map[string]messageTest{ "OnlyBasicProperties": { diff --git a/interactions_test.go b/interactions_test.go index 989690e27..934d30258 100644 --- a/interactions_test.go +++ b/interactions_test.go @@ -51,13 +51,15 @@ const ( "type": "modal", "title": { "type": "plain_text", - "text": "launch project" + "text": "launch project", + "emoji": false }, "blocks": [{ "type": "section", "text": { "text": "*Sally* has requested you set the deadline for the Nano launch project", - "type": "mrkdwn" + "type": "mrkdwn", + "emoji": false }, "accessory": { "type": "datepicker", @@ -65,7 +67,8 @@ const ( "initial_date": "1990-04-28", "placeholder": { "type": "plain_text", - "text": "Select a date" + "text": "Select a date", + "emoji": false } } }], @@ -92,7 +95,8 @@ const ( "type": "modal", "title": { "type": "plain_text", - "text": "meal choice" + "text": "meal choice", + "emoji": false }, "blocks": [ { @@ -100,7 +104,8 @@ const ( "block_id": "multi-line", "label": { "type": "plain_text", - "text": "dietary restrictions" + "text": "dietary restrictions", + "emoji": false }, "element": { "type": "plain_text_input", @@ -113,7 +118,8 @@ const ( "block_id": "target_channel", "label": { "type": "plain_text", - "text": "Select a channel to post the result on" + "text": "Select a channel to post the result on", + "emoji": false }, "element": { "type": "conversations_select", diff --git a/views_test.go b/views_test.go index 5908737f8..c86c0a085 100644 --- a/views_test.go +++ b/views_test.go @@ -767,7 +767,8 @@ func TestSlack_UpdateViewSubmissionResponse(t *testing.T) { "type": "modal", "title": { "type": "plain_text", - "text": "Test update view submission response" + "text": "Test update view submission response", + "emoji": false }, "blocks": [ { @@ -803,7 +804,8 @@ func TestSlack_PushViewSubmissionResponse(t *testing.T) { "type": "modal", "title": { "type": "plain_text", - "text": "Test update view submission response" + "text": "Test update view submission response", + "emoji": false }, "blocks": [ { @@ -812,7 +814,8 @@ func TestSlack_PushViewSubmissionResponse(t *testing.T) { "elements": [ { "type": "plain_text", - "text": "Context text" + "text": "Context text", + "emoji": false }, { "type": "image", diff --git a/webhooks_test.go b/webhooks_test.go index 90b7e285d..4c76a1dd0 100644 --- a/webhooks_test.go +++ b/webhooks_test.go @@ -77,13 +77,13 @@ func TestWebhookMessage_WithBlocks(t *testing.T) { assert.Equal(t, 1, len(msgSingleBlock.Blocks.BlockSet)) msgJsonSingleBlock, _ := json.Marshal(msgSingleBlock) - assert.Equal(t, `{"blocks":[{"type":"section","text":{"type":"plain_text","text":"text"}}],"replace_original":false,"delete_original":false}`, string(msgJsonSingleBlock)) + assert.Equal(t, `{"blocks":[{"type":"section","text":{"type":"plain_text","text":"text","emoji":false}}],"replace_original":false,"delete_original":false}`, string(msgJsonSingleBlock)) msgTwoBlocks := WebhookMessage{Blocks: twoBlocks} assert.Equal(t, 2, len(msgTwoBlocks.Blocks.BlockSet)) msgJsonTwoBlocks, _ := json.Marshal(msgTwoBlocks) - assert.Equal(t, `{"blocks":[{"type":"section","text":{"type":"plain_text","text":"text"}},{"type":"section","text":{"type":"plain_text","text":"text"}}],"replace_original":false,"delete_original":false}`, string(msgJsonTwoBlocks)) + assert.Equal(t, `{"blocks":[{"type":"section","text":{"type":"plain_text","text":"text","emoji":false}},{"type":"section","text":{"type":"plain_text","text":"text","emoji":false}}],"replace_original":false,"delete_original":false}`, string(msgJsonTwoBlocks)) msgNoBlocks := WebhookMessage{Text: "foo"} msgJsonNoBlocks, _ := json.Marshal(msgNoBlocks) From 17ab0ef01e6c5ec19ecedc882ebd81205e764a55 Mon Sep 17 00:00:00 2001 From: Caleb McKay Date: Wed, 27 Nov 2024 06:00:42 +0000 Subject: [PATCH 3/3] Add tests for unmarshalling TextBlockObject --- block_object_test.go | 86 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/block_object_test.go b/block_object_test.go index 3499327e9..6aa0e9f40 100644 --- a/block_object_test.go +++ b/block_object_test.go @@ -1,10 +1,12 @@ package slack import ( + "encoding/json" "errors" "strings" "testing" + "github.com/go-test/deep" "github.com/stretchr/testify/assert" "gopkg.in/guregu/null.v4" ) @@ -101,6 +103,15 @@ func TestValidateTextBlockObject(t *testing.T) { }, expected: nil, }, + { + input: TextBlockObject{ + Type: "plain_text", + Text: "testText", + Emoji: null.BoolFrom(true), + Verbatim: false, + }, + expected: nil, + }, { input: TextBlockObject{ Type: "plain_text", @@ -171,3 +182,78 @@ func TestValidateTextBlockObject(t *testing.T) { assert.Equal(t, err, test.expected) } } + +func TestTextBlockObject_UnmarshalJSON(t *testing.T) { + cases := []struct { + raw []byte + expected TextBlockObject + err error + }{ + { + []byte(`{"type":"plain_text","text":"testText"}`), + TextBlockObject{ + Type: "plain_text", + Text: "testText", + Emoji: null.BoolFromPtr(nil), + Verbatim: false, + }, + nil, + }, + { + []byte(`{"type":"plain_text","text":":+1:","emoji":true}`), + TextBlockObject{ + Type: "plain_text", + Text: ":+1:", + Emoji: null.BoolFrom(true), + Verbatim: false, + }, + nil, + }, + { + []byte(`{"type":"plain_text","text":"No emojis allowed :(","emoji":false}`), + TextBlockObject{ + Type: "plain_text", + Text: "No emojis allowed :(", + Emoji: null.BoolFrom(false), + Verbatim: false, + }, + nil, + }, + { + []byte(`{"type":"mrkdwn","text":"testText"}`), + TextBlockObject{ + Type: "mrkdwn", + Text: "testText", + Emoji: null.BoolFromPtr(nil), + Verbatim: false, + }, + nil, + }, + { + []byte(`{"type":"mrkdwn","text":"No emojis allowed :(","emoji":false}`), + TextBlockObject{ + Type: "mrkdwn", + Text: "No emojis allowed :(", + Emoji: null.BoolFrom(false), + Verbatim: false, + }, + nil, + }, + } + for _, tc := range cases { + var actual TextBlockObject + err := json.Unmarshal(tc.raw, &actual) + if err != nil { + if tc.err == nil { + t.Errorf("unexpected error: %s", err) + } + t.Errorf("expected error is %v, but got %v", tc.err, err) + } + if tc.err != nil { + t.Errorf("expected to raise an error %v", tc.err) + } + if diff := deep.Equal(actual, tc.expected); diff != nil { + t.Errorf("actual value does not match expected one\n%s", diff) + } + } +}