MM-13826 Webhooks: Allow "true"/"false" for bool values in payload body (#10114)

* Webhooks: Allow "true"/"false" for bool values in payload body

Some slack integrations encode bool fields as "true"/"false", which
was previously unsupported in mattermost due to how encoding/json works.
This commit adds an aliased type for bool that implements json.Unmarshaler
to maintain compatibility with Slack.

* Add missing copyright header to added files
Этот коммит содержится в:
Nathan Lowe
2019-01-24 10:36:10 -05:00
коммит произвёл Harrison Healey
родитель 684e4cb007
Коммит e961b4cd0d
3 изменённых файлов: 90 добавлений и 3 удалений

Просмотреть файл

@@ -31,9 +31,9 @@ type SlackAttachment struct {
}
type SlackAttachmentField struct {
Title string `json:"title"`
Value interface{} `json:"value"`
Short bool `json:"short"`
Title string `json:"title"`
Value interface{} `json:"value"`
Short SlackCompatibleBool `json:"short"`
}
func StringifySlackFieldValue(a []*SlackAttachment) []*SlackAttachment {

30
model/slack_compatibility.go Обычный файл
Просмотреть файл

@@ -0,0 +1,30 @@
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"fmt"
"strings"
)
// SlackCompatibleBool is an alias for bool that implements json.Unmarshaler
type SlackCompatibleBool bool
// UnmarshalJSON implements json.Unmarshaler
//
// Slack allows bool values to be represented as strings ("true"/"false") or
// literals (true/false). To maintain compatibility, we define an Unmarshaler
// that supports both.
func (b *SlackCompatibleBool) UnmarshalJSON(data []byte) error {
value := strings.ToLower(string(data))
if value == "true" || value == `"true"` {
*b = true
} else if value == "false" || value == `"false"` {
*b = false
} else {
return fmt.Errorf("unmarshal: unable to convert %s to bool", data)
}
return nil
}

57
model/slack_compatibility_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,57 @@
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func TestSlackCompatibleBool_UnmarshalJSON_True(t *testing.T) {
cases := []struct {
name string
payload string
}{
{name: "literal", payload: `{"title": "Foo", "value": "Bar", "short": true}`},
{name: "stringLower", payload: `{"title": "Foo", "value": "Bar", "short": "true"}`},
{name: "stringMixed", payload: `{"title": "Foo", "value": "Bar", "short": "True"}`},
{name: "stringUpper", payload: `{"title": "Foo", "value": "Bar", "short": "TRUE"}`},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
field := &SlackAttachmentField{}
err := json.Unmarshal([]byte(tt.payload), field)
require.NoError(t, err)
require.True(t, bool(field.Short))
})
}
}
func TestSlackCompatibleBool_UnmarshalJSON_False(t *testing.T) {
cases := []struct {
name string
payload string
}{
{name: "literal", payload: `{"title": "Foo", "value": "Bar", "short": false}`},
{name: "stringLower", payload: `{"title": "Foo", "value": "Bar", "short": "false"}`},
{name: "stringMixed", payload: `{"title": "Foo", "value": "Bar", "short": "False"}`},
{name: "stringUpper", payload: `{"title": "Foo", "value": "Bar", "short": "FALSE"}`},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
field := &SlackAttachmentField{}
err := json.Unmarshal([]byte(tt.payload), field)
require.NoError(t, err)
require.False(t, bool(field.Short))
})
}
}