Bump Go dependencies in both backend/ and backend/_example/memory_store.
Notable updates:
- github.com/go-pkgz/lgr v0.12.1 -> v0.12.3
- github.com/klauspost/compress v1.18.2 -> v1.18.5
- github.com/PuerkitoBio/goquery v1.11.0 -> v1.12.0
- github.com/montanaflynn/stats v0.7.1 -> v0.9.0
- github.com/redis/go-redis/v9 v9.17.2 -> v9.18.0
- github.com/slack-go/slack v0.17.3 -> v0.21.1
- go.mongodb.org/mongo-driver v1.17.6 -> v1.17.9
- golang.org/x/crypto v0.48.0 -> v0.50.0
- golang.org/x/net v0.49.0 -> v0.53.0
- golang.org/x/image v0.36.0 -> v0.39.0
- golang.org/x/sys v0.41.0 -> v0.43.0
- golang.org/x/{oauth2,sync,text} minor bumps
Key markdown/sanitisation libs (bluemonday v1.0.27,
alecthomas/chroma/v2 v2.23.1, russross/blackfriday/v2 v2.1.0,
Depado/bfchroma/v2 v2.0.0) are already at the latest available
versions and were not bumped.
Verified the Chroma span-class allowlist regex in
backend/app/store/comment.go:128-131 is still fully in sync with
chroma/v2 types.go StandardTypes map (86 classes, byte-equal after
sorting). The inline comment references commit c263f6f which is
stale (Chroma is at v2 now), but the class list content is current.
Ran `go mod tidy` + `go mod vendor` + full race test suite on both
modules. All green. Added a reminder in CLAUDE.md that updating
backend/ Go modules also requires `go mod tidy` in
backend/_example/memory_store since the example module uses a
local replace directive and inherits indirect deps from the main
module.
498 lines
13 KiB
Go
498 lines
13 KiB
Go
package slack
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
type sumtype struct {
|
|
TypeVal string `json:"type"`
|
|
}
|
|
|
|
// MarshalJSON implements the Marshaller interface for Blocks so that any JSON
|
|
// marshalling is delegated and proper type determination can be made before marshal
|
|
func (b Blocks) MarshalJSON() ([]byte, error) {
|
|
bytes, err := json.Marshal(b.BlockSet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return bytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements the Unmarshaller interface for Blocks, so that any JSON
|
|
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
|
func (b *Blocks) UnmarshalJSON(data []byte) error {
|
|
var raw []json.RawMessage
|
|
|
|
if string(data) == "{}" {
|
|
return nil
|
|
}
|
|
|
|
err := json.Unmarshal(data, &raw)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var blocks Blocks
|
|
for _, r := range raw {
|
|
s := sumtype{}
|
|
err := json.Unmarshal(r, &s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var blockType string
|
|
if s.TypeVal != "" {
|
|
blockType = s.TypeVal
|
|
}
|
|
|
|
var block Block
|
|
switch blockType {
|
|
case "actions":
|
|
block = &ActionBlock{}
|
|
case "context":
|
|
block = &ContextBlock{}
|
|
case "context_actions":
|
|
block = &ContextActionsBlock{}
|
|
case "divider":
|
|
block = &DividerBlock{}
|
|
case "file":
|
|
block = &FileBlock{}
|
|
case "header":
|
|
block = &HeaderBlock{}
|
|
case "image":
|
|
block = &ImageBlock{}
|
|
case "input":
|
|
block = &InputBlock{}
|
|
case "markdown":
|
|
block = &MarkdownBlock{}
|
|
case "rich_text":
|
|
block = &RichTextBlock{}
|
|
case "rich_text_input":
|
|
block = &RichTextBlock{}
|
|
case "section":
|
|
block = &SectionBlock{}
|
|
case "call":
|
|
block = &CallBlock{}
|
|
case "video":
|
|
block = &VideoBlock{}
|
|
case "table":
|
|
block = &TableBlock{}
|
|
case "task_card":
|
|
block = &TaskCardBlock{}
|
|
case "plan":
|
|
block = &PlanBlock{}
|
|
default:
|
|
b := &UnknownBlock{raw: r}
|
|
if err = json.Unmarshal(r, b); err != nil {
|
|
return err
|
|
}
|
|
blocks.BlockSet = append(blocks.BlockSet, b)
|
|
continue
|
|
}
|
|
|
|
err = json.Unmarshal(r, block)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
blocks.BlockSet = append(blocks.BlockSet, block)
|
|
}
|
|
|
|
*b = blocks
|
|
return nil
|
|
}
|
|
|
|
// UnmarshalJSON implements the Unmarshaller interface for InputBlock, so that any JSON
|
|
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
|
func (b *InputBlock) UnmarshalJSON(data []byte) error {
|
|
type alias InputBlock
|
|
a := struct {
|
|
Element json.RawMessage `json:"element"`
|
|
*alias
|
|
}{
|
|
alias: (*alias)(b),
|
|
}
|
|
|
|
if err := json.Unmarshal(data, &a); err != nil {
|
|
return err
|
|
}
|
|
|
|
s := sumtype{}
|
|
if err := json.Unmarshal(a.Element, &s); err != nil {
|
|
return nil
|
|
}
|
|
|
|
var e BlockElement
|
|
switch s.TypeVal {
|
|
case "datepicker":
|
|
e = &DatePickerBlockElement{}
|
|
case "timepicker":
|
|
e = &TimePickerBlockElement{}
|
|
case "datetimepicker":
|
|
e = &DateTimePickerBlockElement{}
|
|
case "plain_text_input":
|
|
e = &PlainTextInputBlockElement{}
|
|
case "rich_text_input":
|
|
e = &RichTextInputBlockElement{}
|
|
case "email_text_input":
|
|
e = &EmailTextInputBlockElement{}
|
|
case "url_text_input":
|
|
e = &URLTextInputBlockElement{}
|
|
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
|
|
e = &SelectBlockElement{}
|
|
case "multi_static_select", "multi_external_select", "multi_users_select", "multi_conversations_select", "multi_channels_select":
|
|
e = &MultiSelectBlockElement{}
|
|
case "checkboxes":
|
|
e = &CheckboxGroupsBlockElement{}
|
|
case "overflow":
|
|
e = &OverflowBlockElement{}
|
|
case "radio_buttons":
|
|
e = &RadioButtonsBlockElement{}
|
|
case "number_input":
|
|
e = &NumberInputBlockElement{}
|
|
case "file_input":
|
|
e = &FileInputBlockElement{}
|
|
case "feedback_buttons":
|
|
e = &FeedbackButtonsBlockElement{}
|
|
case "icon_button":
|
|
e = &IconButtonBlockElement{}
|
|
case "workflow_button":
|
|
e = &WorkflowButtonBlockElement{}
|
|
default:
|
|
return fmt.Errorf("unsupported block element type %v", s.TypeVal)
|
|
}
|
|
|
|
if err := json.Unmarshal(a.Element, e); err != nil {
|
|
return err
|
|
}
|
|
b.Element = e
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalJSON implements the Marshaller interface for BlockElements so that any JSON
|
|
// marshalling is delegated and proper type determination can be made before marshal
|
|
func (b *BlockElements) MarshalJSON() ([]byte, error) {
|
|
bytes, err := json.Marshal(b.ElementSet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return bytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements the Unmarshaller interface for BlockElements, so that any JSON
|
|
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
|
func (b *BlockElements) UnmarshalJSON(data []byte) error {
|
|
var raw []json.RawMessage
|
|
|
|
if string(data) == "{}" {
|
|
return nil
|
|
}
|
|
|
|
err := json.Unmarshal(data, &raw)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var blockElements BlockElements
|
|
for _, r := range raw {
|
|
s := sumtype{}
|
|
err := json.Unmarshal(r, &s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var blockElementType string
|
|
if s.TypeVal != "" {
|
|
blockElementType = s.TypeVal
|
|
}
|
|
|
|
var blockElement BlockElement
|
|
switch blockElementType {
|
|
case "image":
|
|
blockElement = &ImageBlockElement{}
|
|
case "button":
|
|
blockElement = &ButtonBlockElement{}
|
|
case "overflow":
|
|
blockElement = &OverflowBlockElement{}
|
|
case "datepicker":
|
|
blockElement = &DatePickerBlockElement{}
|
|
case "timepicker":
|
|
blockElement = &TimePickerBlockElement{}
|
|
case "datetimepicker":
|
|
blockElement = &DateTimePickerBlockElement{}
|
|
case "plain_text_input":
|
|
blockElement = &PlainTextInputBlockElement{}
|
|
case "rich_text_input":
|
|
blockElement = &RichTextInputBlockElement{}
|
|
case "email_text_input":
|
|
blockElement = &EmailTextInputBlockElement{}
|
|
case "url_text_input":
|
|
blockElement = &URLTextInputBlockElement{}
|
|
case "checkboxes":
|
|
blockElement = &CheckboxGroupsBlockElement{}
|
|
case "radio_buttons":
|
|
blockElement = &RadioButtonsBlockElement{}
|
|
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
|
|
blockElement = &SelectBlockElement{}
|
|
case "multi_static_select", "multi_external_select", "multi_users_select", "multi_conversations_select", "multi_channels_select":
|
|
blockElement = &MultiSelectBlockElement{}
|
|
case "number_input":
|
|
blockElement = &NumberInputBlockElement{}
|
|
case "file_input":
|
|
blockElement = &FileInputBlockElement{}
|
|
case "feedback_buttons":
|
|
blockElement = &FeedbackButtonsBlockElement{}
|
|
case "icon_button":
|
|
blockElement = &IconButtonBlockElement{}
|
|
case "workflow_button":
|
|
blockElement = &WorkflowButtonBlockElement{}
|
|
default:
|
|
return fmt.Errorf("unsupported block element type %v", blockElementType)
|
|
}
|
|
|
|
err = json.Unmarshal(r, blockElement)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
blockElements.ElementSet = append(blockElements.ElementSet, blockElement)
|
|
}
|
|
|
|
*b = blockElements
|
|
return nil
|
|
}
|
|
|
|
// MarshalJSON implements the Marshaller interface for Accessory so that any JSON
|
|
// marshalling is delegated and proper type determination can be made before marshal
|
|
func (a *Accessory) MarshalJSON() ([]byte, error) {
|
|
bytes, err := json.Marshal(toBlockElement(a))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return bytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements the Unmarshaller interface for Accessory, so that any JSON
|
|
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
|
// Note: datetimepicker is not supported in Accessory
|
|
func (a *Accessory) UnmarshalJSON(data []byte) error {
|
|
var r json.RawMessage
|
|
|
|
if string(data) == "{\"accessory\":null}" {
|
|
return nil
|
|
}
|
|
|
|
err := json.Unmarshal(data, &r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s := sumtype{}
|
|
err = json.Unmarshal(r, &s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var blockElementType string
|
|
if s.TypeVal != "" {
|
|
blockElementType = s.TypeVal
|
|
}
|
|
|
|
switch blockElementType {
|
|
case "image":
|
|
element, err := unmarshalBlockElement(r, &ImageBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.ImageElement = element.(*ImageBlockElement)
|
|
case "button":
|
|
element, err := unmarshalBlockElement(r, &ButtonBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.ButtonElement = element.(*ButtonBlockElement)
|
|
case "overflow":
|
|
element, err := unmarshalBlockElement(r, &OverflowBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.OverflowElement = element.(*OverflowBlockElement)
|
|
case "datepicker":
|
|
element, err := unmarshalBlockElement(r, &DatePickerBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.DatePickerElement = element.(*DatePickerBlockElement)
|
|
case "timepicker":
|
|
element, err := unmarshalBlockElement(r, &TimePickerBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.TimePickerElement = element.(*TimePickerBlockElement)
|
|
case "plain_text_input":
|
|
element, err := unmarshalBlockElement(r, &PlainTextInputBlockElement{})
|
|
if err != nil {
|
|
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 {
|
|
return err
|
|
}
|
|
a.RadioButtonsElement = element.(*RadioButtonsBlockElement)
|
|
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
|
|
element, err := unmarshalBlockElement(r, &SelectBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.SelectElement = element.(*SelectBlockElement)
|
|
case "multi_static_select", "multi_external_select", "multi_users_select", "multi_conversations_select", "multi_channels_select":
|
|
element, err := unmarshalBlockElement(r, &MultiSelectBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.MultiSelectElement = element.(*MultiSelectBlockElement)
|
|
case "checkboxes":
|
|
element, err := unmarshalBlockElement(r, &CheckboxGroupsBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.CheckboxGroupsBlockElement = element.(*CheckboxGroupsBlockElement)
|
|
case "workflow_button":
|
|
element, err := unmarshalBlockElement(r, &WorkflowButtonBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.WorkflowButtonElement = element.(*WorkflowButtonBlockElement)
|
|
default:
|
|
element, err := unmarshalBlockElement(r, &UnknownBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.UnknownElement = element.(*UnknownBlockElement)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func unmarshalBlockElement(r json.RawMessage, element BlockElement) (BlockElement, error) {
|
|
err := json.Unmarshal(r, element)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return element, nil
|
|
}
|
|
|
|
func toBlockElement(element *Accessory) BlockElement {
|
|
if element.ImageElement != nil {
|
|
return element.ImageElement
|
|
}
|
|
if element.ButtonElement != nil {
|
|
return element.ButtonElement
|
|
}
|
|
if element.OverflowElement != nil {
|
|
return element.OverflowElement
|
|
}
|
|
if element.DatePickerElement != nil {
|
|
return element.DatePickerElement
|
|
}
|
|
if element.TimePickerElement != nil {
|
|
return element.TimePickerElement
|
|
}
|
|
if element.PlainTextInputElement != nil {
|
|
return element.PlainTextInputElement
|
|
}
|
|
if element.RadioButtonsElement != nil {
|
|
return element.RadioButtonsElement
|
|
}
|
|
if element.CheckboxGroupsBlockElement != nil {
|
|
return element.CheckboxGroupsBlockElement
|
|
}
|
|
if element.SelectElement != nil {
|
|
return element.SelectElement
|
|
}
|
|
if element.MultiSelectElement != nil {
|
|
return element.MultiSelectElement
|
|
}
|
|
if element.RichTextInputElement != nil {
|
|
return element.RichTextInputElement
|
|
}
|
|
if element.WorkflowButtonElement != nil {
|
|
return element.WorkflowButtonElement
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalJSON implements the Marshaller interface for ContextElements so that any JSON
|
|
// marshalling is delegated and proper type determination can be made before marshal
|
|
func (e *ContextElements) MarshalJSON() ([]byte, error) {
|
|
bytes, err := json.Marshal(e.Elements)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return bytes, nil
|
|
}
|
|
|
|
// UnmarshalJSON implements the Unmarshaller interface for ContextElements, so that any JSON
|
|
// unmarshalling is delegated and proper type determination can be made before unmarshal
|
|
func (e *ContextElements) UnmarshalJSON(data []byte) error {
|
|
var raw []json.RawMessage
|
|
|
|
if string(data) == "{\"elements\":null}" {
|
|
return nil
|
|
}
|
|
|
|
err := json.Unmarshal(data, &raw)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, r := range raw {
|
|
s := sumtype{}
|
|
err := json.Unmarshal(r, &s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var contextElementType string
|
|
if s.TypeVal != "" {
|
|
contextElementType = s.TypeVal
|
|
}
|
|
|
|
switch contextElementType {
|
|
case PlainTextType, MarkdownType:
|
|
elem, err := unmarshalBlockObject(r, &TextBlockObject{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
e.Elements = append(e.Elements, elem.(*TextBlockObject))
|
|
case "image":
|
|
elem, err := unmarshalBlockElement(r, &ImageBlockElement{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
e.Elements = append(e.Elements, elem.(*ImageBlockElement))
|
|
default:
|
|
return fmt.Errorf("unsupported context element type %v", contextElementType)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|