Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rich_text_quote and rich_text_preformatted #1260

Merged
merged 1 commit into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
68 changes: 67 additions & 1 deletion block_rich_text.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ func (e *RichTextBlock) UnmarshalJSON(b []byte) error {
elem = &RichTextSection{}
case RTEList:
elem = &RichTextList{}

case RTEQuote:
elem = &RichTextQuote{}
case RTEPreformatted:
elem = &RichTextPreformatted{}
default:
elems = append(elems, &RichTextUnknown{
Type: s.Type,
Expand Down Expand Up @@ -150,6 +153,10 @@ func (e *RichTextList) UnmarshalJSON(b []byte) error {
elem = &RichTextSection{}
case RTEList:
elem = &RichTextList{}
case RTEQuote:
elem = &RichTextQuote{}
case RTEPreformatted:
elem = &RichTextPreformatted{}
default:
elems = append(elems, &RichTextUnknown{
Type: s.Type,
Expand Down Expand Up @@ -460,3 +467,62 @@ type RichTextSectionUnknownElement struct {
func (r RichTextSectionUnknownElement) RichTextSectionElementType() RichTextSectionElementType {
return r.Type
}

// RichTextQuote represents rich_text_quote element type.
type RichTextQuote RichTextSection

// RichTextElementType returns the type of the Element
func (s *RichTextQuote) RichTextElementType() RichTextElementType {
return s.Type
}

func (s *RichTextQuote) UnmarshalJSON(b []byte) error {
// reusing the RichTextSection struct, as it's the same as RichTextQuote.
var rts RichTextSection
if err := json.Unmarshal(b, &rts); err != nil {
return err
}
*s = RichTextQuote(rts)
s.Type = RTEQuote
return nil
}

// RichTextPreformatted represents rich_text_quote element type.
type RichTextPreformatted struct {
RichTextSection
Border int `json:"border"`
}

// RichTextElementType returns the type of the Element
func (s *RichTextPreformatted) RichTextElementType() RichTextElementType {
return s.Type
}

func (s *RichTextPreformatted) UnmarshalJSON(b []byte) error {
var rts RichTextSection
if err := json.Unmarshal(b, &rts); err != nil {
return err
}
// we define standalone fields because we need to unmarshal the border
// field. We can not directly unmarshal the data into
// RichTextPreformatted because it will cause an infinite loop. We also
// can not define a struct with embedded RichTextSection and Border fields
// because the json package will not unmarshal the data into the
// standalone fields, once it sees UnmarshalJSON method on the embedded
// struct. The drawback is that we have to process the data twice, and
// have to define a standalone struct with the same set of fields as the
// original struct, which may become a maintenance burden (i.e. update the
// fields in two places, should it ever change).
var standalone struct {
Border int `json:"border"`
}
if err := json.Unmarshal(b, &standalone); err != nil {
return err
}
*s = RichTextPreformatted{
RichTextSection: rts,
Border: standalone.Border,
}
s.Type = RTEPreformatted
return nil
}
166 changes: 164 additions & 2 deletions block_rich_text_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,60 @@ const (
}
]
}`

richTextQuotePayload = `{
"type": "rich_text",
"block_id": "G7G",
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "Holy moly\n\n"
}
]
},
{
"type": "rich_text_preformatted",
"elements": [
{
"type": "text",
"text": "Preformatted\n\n"
}
],
"border": 2
},
{
"type": "rich_text_quote",
"elements": [
{
"type": "text",
"text": "Quote\n\n"
}
]
},
{
"type": "rich_text_quote",
"elements": [
{
"type": "text",
"text": "Another quote"
}
]
},
{
"type": "rich_text_preformatted",
"elements": [
{
"type": "text",
"text": "Another preformatted\n\n"
}
],
"border": 42
}
]
}`
)

func TestRichTextBlock_UnmarshalJSON(t *testing.T) {
Expand Down Expand Up @@ -55,6 +109,38 @@ func TestRichTextBlock_UnmarshalJSON(t *testing.T) {
},
nil,
},
{
[]byte(dummyPayload),
RichTextBlock{
Type: MBTRichText,
BlockID: "FaYCD",
Elements: []RichTextElement{
&RichTextSection{
Type: RTESection,
Elements: []RichTextSectionElement{
&RichTextSectionChannelElement{Type: RTSEChannel, ChannelID: "C012345678"},
&RichTextSectionTextElement{Type: RTSEText, Text: "dummy_text"},
},
},
},
},
nil,
},
{
[]byte(richTextQuotePayload),
RichTextBlock{
Type: MBTRichText,
BlockID: "G7G",
Elements: []RichTextElement{
&RichTextSection{Type: RTESection, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Holy moly\n\n"}}},
&RichTextPreformatted{RichTextSection: RichTextSection{Type: RTEPreformatted, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Preformatted\n\n"}}}, Border: 2},
&RichTextQuote{Type: RTEQuote, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Quote\n\n"}}},
&RichTextQuote{Type: RTEQuote, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Another quote"}}},
&RichTextPreformatted{RichTextSection: RichTextSection{Type: RTEPreformatted, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Another preformatted\n\n"}}}, Border: 42},
},
},
nil,
},
}
for _, tc := range cases {
var actual RichTextBlock
Expand All @@ -63,10 +149,10 @@ func TestRichTextBlock_UnmarshalJSON(t *testing.T) {
if tc.err == nil {
t.Errorf("unexpected error: %s", err)
}
t.Errorf("expected error is %s, but got %s", tc.err, err)
t.Errorf("expected error is %v, but got %v", tc.err, err)
}
if tc.err != nil {
t.Errorf("expected to raise an error %s", tc.err)
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)
Expand Down Expand Up @@ -199,3 +285,79 @@ func TestRichTextList_UnmarshalJSON(t *testing.T) {
}
}
}

func TestRichTextQuote_Marshal(t *testing.T) {
t.Run("rich_text_section", func(t *testing.T) {
const rawRSE = "{\"type\":\"rich_text_section\",\"elements\":[{\"type\":\"text\",\"text\":\"Some Text\"}]}"

var got RichTextSection
if err := json.Unmarshal([]byte(rawRSE), &got); err != nil {
t.Fatal(err)
}
want := RichTextSection{
Type: RTESection,
Elements: []RichTextSectionElement{
&RichTextSectionTextElement{Type: RTSEText, Text: "Some Text"},
},
}

if diff := deep.Equal(got, want); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
b, err := json.Marshal(got)
if err != nil {
t.Fatal(err)
}
if diff := deep.Equal(string(b), rawRSE); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
})
t.Run("rich_text_quote", func(t *testing.T) {
const rawRTS = "{\"type\":\"rich_text_quote\",\"elements\":[{\"type\":\"text\",\"text\":\"Some text\"}]}"

var got RichTextQuote
if err := json.Unmarshal([]byte(rawRTS), &got); err != nil {
t.Fatal(err)
}
want := RichTextQuote{
Type: RTEQuote,
Elements: []RichTextSectionElement{
&RichTextSectionTextElement{Type: RTSEText, Text: "Some text"},
},
}
if diff := deep.Equal(got, want); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
b, err := json.Marshal(got)
if err != nil {
t.Fatal(err)
}
if diff := deep.Equal(string(b), rawRTS); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
})
t.Run("rich_text_preformatted", func(t *testing.T) {
const rawRTP = "{\"type\":\"rich_text_preformatted\",\"elements\":[{\"type\":\"text\",\"text\":\"Some other text\"}],\"border\":2}"
want := RichTextPreformatted{
RichTextSection: RichTextSection{
Type: RTEPreformatted,
Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Some other text"}},
},
Border: 2,
}
var got RichTextPreformatted
if err := json.Unmarshal([]byte(rawRTP), &got); err != nil {
t.Fatal(err)
}
if diff := deep.Equal(got, want); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
b, err := json.Marshal(got)
if err != nil {
t.Fatal(err)
}
if diff := deep.Equal(string(b), rawRTP); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
})
}