New templates support
This commit is contained in:
commit
19bb8f8eaf
@ -157,6 +157,7 @@ issues:
|
|||||||
exclude-rules:
|
exclude-rules:
|
||||||
- path: _test\.go
|
- path: _test\.go
|
||||||
linters:
|
linters:
|
||||||
|
- dupl
|
||||||
- gomnd
|
- gomnd
|
||||||
- lll
|
- lll
|
||||||
- bodyclose
|
- bodyclose
|
||||||
|
1
mg-transport-api-client-go
Symbolic link
1
mg-transport-api-client-go
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/home/pavel/work/mg-transports/mg-transport-api-client-go
|
@ -153,15 +153,15 @@ func (c *MgClient) ActivateTemplate(channelID uint64, request ActivateTemplateRe
|
|||||||
// if err != nil {
|
// if err != nil {
|
||||||
// fmt.Printf("%#v", err)
|
// fmt.Printf("%#v", err)
|
||||||
// }
|
// }
|
||||||
func (c *MgClient) UpdateTemplate(request Template) (int, error) {
|
func (c *MgClient) UpdateTemplate(channelID uint64, code string, request UpdateTemplateRequest) (int, error) {
|
||||||
outgoing, _ := json.Marshal(&request)
|
outgoing, _ := json.Marshal(&request)
|
||||||
|
|
||||||
if request.ChannelID == 0 || request.Code == "" {
|
if channelID == 0 || code == "" {
|
||||||
return 0, errors.New("`ChannelID` and `Code` cannot be blank")
|
return 0, errors.New("`ChannelID` and `Code` cannot be blank")
|
||||||
}
|
}
|
||||||
|
|
||||||
data, status, err := c.PutRequest(
|
data, status, err := c.PutRequest(
|
||||||
fmt.Sprintf("/channels/%d/templates/%s", request.ChannelID, url.PathEscape(request.Code)), outgoing)
|
fmt.Sprintf("/channels/%d/templates/%s", channelID, url.PathEscape(code)), outgoing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
|
@ -351,8 +351,9 @@ func (t *MGClientTest) Test_ActivateTemplate() {
|
|||||||
c := t.client()
|
c := t.client()
|
||||||
req := ActivateTemplateRequest{
|
req := ActivateTemplateRequest{
|
||||||
Code: "tplCode",
|
Code: "tplCode",
|
||||||
Name: "tplCode",
|
|
||||||
Type: TemplateTypeText,
|
Type: TemplateTypeText,
|
||||||
|
UpdateTemplateRequest: UpdateTemplateRequest{
|
||||||
|
Name: "tplCode",
|
||||||
Template: []TemplateItem{
|
Template: []TemplateItem{
|
||||||
{
|
{
|
||||||
Type: TemplateItemTypeText,
|
Type: TemplateItemTypeText,
|
||||||
@ -368,7 +369,8 @@ func (t *MGClientTest) Test_ActivateTemplate() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
RejectionReason: "",
|
RejectionReason: "",
|
||||||
VerificationStatus: "approved",
|
VerificationStatus: TemplateStatusApproved,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
defer gock.Off()
|
defer gock.Off()
|
||||||
@ -384,12 +386,8 @@ func (t *MGClientTest) Test_ActivateTemplate() {
|
|||||||
|
|
||||||
func (t *MGClientTest) Test_UpdateTemplate() {
|
func (t *MGClientTest) Test_UpdateTemplate() {
|
||||||
c := t.client()
|
c := t.client()
|
||||||
tpl := Template{
|
tpl := UpdateTemplateRequest{
|
||||||
Code: "encodable#code",
|
|
||||||
ChannelID: 1,
|
|
||||||
Name: "updated name",
|
Name: "updated name",
|
||||||
Enabled: true,
|
|
||||||
Type: TemplateTypeText,
|
|
||||||
Template: []TemplateItem{
|
Template: []TemplateItem{
|
||||||
{
|
{
|
||||||
Type: TemplateItemTypeText,
|
Type: TemplateItemTypeText,
|
||||||
@ -421,16 +419,20 @@ func (t *MGClientTest) Test_UpdateTemplate() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Get(t.transportURL("templates")).
|
Get(t.transportURL("templates")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON([]Template{tpl})
|
JSON([]ActivateTemplateRequest{ActivateTemplateRequest{
|
||||||
|
UpdateTemplateRequest: tpl,
|
||||||
|
Code: "encodable#code",
|
||||||
|
Type: TemplateTypeText,
|
||||||
|
}})
|
||||||
|
|
||||||
status, err := c.UpdateTemplate(tpl)
|
status, err := c.UpdateTemplate(1, "encodable#code", tpl)
|
||||||
t.Assert().NoError(err, fmt.Sprintf("%d %s", status, err))
|
t.Assert().NoError(err, fmt.Sprintf("%d %s", status, err))
|
||||||
|
|
||||||
templates, status, err := c.TransportTemplates()
|
templates, status, err := c.TransportTemplates()
|
||||||
t.Assert().NoError(err, fmt.Sprintf("%d %s", status, err))
|
t.Assert().NoError(err, fmt.Sprintf("%d %s", status, err))
|
||||||
|
|
||||||
for _, template := range templates {
|
for _, template := range templates {
|
||||||
if template.Code == tpl.Code {
|
if template.Code == "encodable#code" {
|
||||||
t.Assert().Equal(tpl.Name, template.Name)
|
t.Assert().Equal(tpl.Name, template.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -438,10 +440,8 @@ func (t *MGClientTest) Test_UpdateTemplate() {
|
|||||||
|
|
||||||
func (t *MGClientTest) Test_UpdateTemplateFail() {
|
func (t *MGClientTest) Test_UpdateTemplateFail() {
|
||||||
c := t.client()
|
c := t.client()
|
||||||
tpl := Template{
|
tpl := UpdateTemplateRequest{
|
||||||
Name: "updated name",
|
Name: "updated name",
|
||||||
Enabled: true,
|
|
||||||
Type: TemplateTypeText,
|
|
||||||
Template: []TemplateItem{
|
Template: []TemplateItem{
|
||||||
{
|
{
|
||||||
Type: TemplateItemTypeText,
|
Type: TemplateItemTypeText,
|
||||||
@ -467,7 +467,7 @@ func (t *MGClientTest) Test_UpdateTemplateFail() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
status, err := c.UpdateTemplate(tpl)
|
status, err := c.UpdateTemplate(1, "encodable#code", tpl)
|
||||||
t.Assert().Error(err, fmt.Sprintf("%d %s", status, err))
|
t.Assert().Error(err, fmt.Sprintf("%d %s", status, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
263
v1/template.go
263
v1/template.go
@ -1,14 +1,12 @@
|
|||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TemplateTypeText is a text template type. There is no other types for now.
|
|
||||||
const TemplateTypeText = "text"
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TemplateItemTypeText is a type for text chunk in template.
|
// TemplateItemTypeText is a type for text chunk in template.
|
||||||
TemplateItemTypeText uint8 = iota
|
TemplateItemTypeText uint8 = iota
|
||||||
@ -37,19 +35,260 @@ var templateVarAssoc = map[string]interface{}{
|
|||||||
|
|
||||||
// Template struct.
|
// Template struct.
|
||||||
type Template struct {
|
type Template struct {
|
||||||
Code string `json:"code"`
|
ID int64 `json:"id,omitempty"`
|
||||||
ChannelID uint64 `json:"channel_id,omitempty"`
|
Code string `json:"code,omitempty"`
|
||||||
|
ChannelID uint64 `json:"channel_id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Enabled bool `json:"enabled"`
|
||||||
Type string `json:"type"`
|
Type TemplateType `json:"type"`
|
||||||
Template []TemplateItem `json:"template"`
|
Template []TemplateItem `json:"template"`
|
||||||
HeaderParams *HeaderParams `json:"headerParams,omitempty"`
|
Body string `json:"body"`
|
||||||
Footer *string `json:"footer,omitempty"`
|
|
||||||
ButtonParams []ButtonParam `json:"buttonParams,omitempty"`
|
|
||||||
Lang string `json:"lang,omitempty"`
|
Lang string `json:"lang,omitempty"`
|
||||||
Category string `json:"category,omitempty"`
|
Category string `json:"category,omitempty"`
|
||||||
RejectionReason string `json:"rejection_reason,omitempty"`
|
Example *TemplateExample `json:"example,omitempty"`
|
||||||
VerificationStatus string `json:"verification_status,omitempty"`
|
VerificationStatus TemplateVerificationStatus `json:"verification_status"`
|
||||||
|
RejectionReason TemplateRejectionReason `json:"rejection_reason,omitempty"`
|
||||||
|
Header *TemplateHeader `json:"header,omitempty"`
|
||||||
|
Footer string `json:"footer,omitempty"`
|
||||||
|
Buttons *TemplateButtons `json:"buttons,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TemplateExample struct {
|
||||||
|
Body []string `json:"body,omitempty"`
|
||||||
|
Header []string `json:"header,omitempty"`
|
||||||
|
Buttons [][]string `json:"buttons,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TemplateButtons struct {
|
||||||
|
Items []Button `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *TemplateButtons) UnmarshalJSON(value []byte) error {
|
||||||
|
var ScanType struct {
|
||||||
|
Items []json.RawMessage `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(value, &ScanType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ButtonType struct {
|
||||||
|
Type ButtonType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range ScanType.Items {
|
||||||
|
if err := json.Unmarshal(r, &ButtonType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var btn Button
|
||||||
|
switch ButtonType.Type {
|
||||||
|
case ButtonTypePlain:
|
||||||
|
btn = &PlainButton{}
|
||||||
|
case ButtonTypePhone:
|
||||||
|
btn = &PhoneButton{}
|
||||||
|
case ButtonTypeURL:
|
||||||
|
btn = &URLButton{}
|
||||||
|
default:
|
||||||
|
return errors.New("undefined type of button")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(r, btn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Items = append(b.Items, btn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b TemplateButtons) MarshalJSON() ([]byte, error) {
|
||||||
|
var ValueType struct {
|
||||||
|
Items []json.RawMessage `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, btn := range b.Items {
|
||||||
|
btnData, err := json.Marshal(btn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := bytes.NewBuffer(btnData[:len(btnData)-1])
|
||||||
|
buffer.WriteByte(',')
|
||||||
|
buffer.WriteString(fmt.Sprintf(`"type":"%s"`, btn.ButtonType()))
|
||||||
|
buffer.WriteByte('}')
|
||||||
|
|
||||||
|
ValueType.Items = append(ValueType.Items, buffer.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := json.Marshal(ValueType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Button interface {
|
||||||
|
ButtonType() ButtonType
|
||||||
|
}
|
||||||
|
|
||||||
|
type ButtonType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ButtonTypePlain ButtonType = "plain"
|
||||||
|
ButtonTypePhone ButtonType = "phone"
|
||||||
|
ButtonTypeURL ButtonType = "url"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PlainButton struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PlainButton) ButtonType() ButtonType { return ButtonTypePlain }
|
||||||
|
|
||||||
|
type PhoneButton struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PhoneButton) ButtonType() ButtonType { return ButtonTypePhone }
|
||||||
|
|
||||||
|
type URLButton struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (URLButton) ButtonType() ButtonType { return ButtonTypeURL }
|
||||||
|
|
||||||
|
type HeaderContent interface {
|
||||||
|
HeaderContentType() HeaderContentType
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeaderContentType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
HeaderContentTypeText HeaderContentType = "text"
|
||||||
|
HeaderContentTypeDocument HeaderContentType = "document"
|
||||||
|
HeaderContentTypeImage HeaderContentType = "image"
|
||||||
|
HeaderContentTypeVideo HeaderContentType = "video"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HeaderContentText struct {
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (HeaderContentText) HeaderContentType() HeaderContentType { return HeaderContentTypeText }
|
||||||
|
|
||||||
|
type HeaderContentDocument struct{}
|
||||||
|
|
||||||
|
func (HeaderContentDocument) HeaderContentType() HeaderContentType { return HeaderContentTypeDocument }
|
||||||
|
|
||||||
|
type HeaderContentImage struct{}
|
||||||
|
|
||||||
|
func (HeaderContentImage) HeaderContentType() HeaderContentType { return HeaderContentTypeImage }
|
||||||
|
|
||||||
|
type HeaderContentVideo struct{}
|
||||||
|
|
||||||
|
func (HeaderContentVideo) HeaderContentType() HeaderContentType { return HeaderContentTypeVideo }
|
||||||
|
|
||||||
|
type TemplateHeader struct {
|
||||||
|
Content HeaderContent `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TemplateHeader) TextContent() *HeaderContentText {
|
||||||
|
if h.Content.HeaderContentType() != HeaderContentTypeText {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return h.Content.(*HeaderContentText)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TemplateHeader) DocumentContent() *HeaderContentDocument {
|
||||||
|
if h.Content.HeaderContentType() != HeaderContentTypeDocument {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return h.Content.(*HeaderContentDocument)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TemplateHeader) ImageContent() *HeaderContentImage {
|
||||||
|
if h.Content.HeaderContentType() != HeaderContentTypeImage {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return h.Content.(*HeaderContentImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TemplateHeader) VideoContent() *HeaderContentVideo {
|
||||||
|
if h.Content.HeaderContentType() != HeaderContentTypeVideo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return h.Content.(*HeaderContentVideo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TemplateHeader) UnmarshalJSON(value []byte) error {
|
||||||
|
var ScanType struct {
|
||||||
|
Content json.RawMessage `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(value, &ScanType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ContentType struct {
|
||||||
|
Type HeaderContentType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(ScanType.Content, &ContentType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var c HeaderContent
|
||||||
|
switch ContentType.Type {
|
||||||
|
case HeaderContentTypeText:
|
||||||
|
c = &HeaderContentText{}
|
||||||
|
case HeaderContentTypeDocument:
|
||||||
|
c = &HeaderContentDocument{}
|
||||||
|
case HeaderContentTypeImage:
|
||||||
|
c = &HeaderContentImage{}
|
||||||
|
case HeaderContentTypeVideo:
|
||||||
|
c = &HeaderContentVideo{}
|
||||||
|
default:
|
||||||
|
return errors.New("undefined type of header content")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(ScanType.Content, c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Content = c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h TemplateHeader) MarshalJSON() ([]byte, error) {
|
||||||
|
content, err := json.Marshal(h.Content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := bytes.NewBuffer(content[:len(content)-1])
|
||||||
|
if buffer.Len() > 1 {
|
||||||
|
buffer.WriteByte(',')
|
||||||
|
}
|
||||||
|
buffer.WriteString(fmt.Sprintf(`"type":"%s"`, h.Content.HeaderContentType()))
|
||||||
|
buffer.WriteByte('}')
|
||||||
|
|
||||||
|
var ValueType struct {
|
||||||
|
Content json.RawMessage `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueType.Content = buffer.Bytes()
|
||||||
|
|
||||||
|
d, err := json.Marshal(ValueType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateItem is a part of template.
|
// TemplateItem is a part of template.
|
||||||
|
@ -64,51 +64,193 @@ func TestTemplateItem_UnmarshalJSON(t *testing.T) {
|
|||||||
assert.Empty(t, emptyVariableResult.Text)
|
assert.Empty(t, emptyVariableResult.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalMediaInteractiveTemplate(t *testing.T) {
|
func TestUnmarshalInteractiveTemplate_TextHeader(t *testing.T) {
|
||||||
var template Template
|
var template Template
|
||||||
input := `{
|
input := `{
|
||||||
"code":"aaa#bbb#ru",
|
"code":"aaa#bbb#ru",
|
||||||
"phone": "79252223456",
|
"phone": "79252223456",
|
||||||
"channel_id": 1,
|
"channel_id": 1,
|
||||||
"headerParams": {
|
"header": {
|
||||||
"textVars": [
|
"content": {
|
||||||
"Johny",
|
"type": "text",
|
||||||
"1234C"
|
"body": "Hello, {{1}}!"
|
||||||
],
|
}
|
||||||
"imageUrl": "http://example.com/intaro/d2354125",
|
|
||||||
"videoUrl": "http://example.com/intaro/d2222",
|
|
||||||
"documentUrl": "http://example.com/intaro/d4444"
|
|
||||||
},
|
},
|
||||||
"footer": "Scooter",
|
"footer": "Scooter",
|
||||||
"buttonParams": [
|
"buttons": {
|
||||||
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "URL",
|
"type": "url",
|
||||||
"urlParameter": "222ddd"
|
"label": "Go to website",
|
||||||
|
"url": "222ddd"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "QUICK_REPLY",
|
"type": "plain",
|
||||||
"text": "Yes"
|
"label": "Yes"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
|
},
|
||||||
"verification_status": "approved"
|
"verification_status": "approved"
|
||||||
}`
|
}`
|
||||||
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
||||||
|
|
||||||
assert.Equal(t, "aaa#bbb#ru", template.Code)
|
assert.Equal(t, "aaa#bbb#ru", template.Code)
|
||||||
assert.Equal(t, []string{"Johny", "1234C"}, template.HeaderParams.TextVars)
|
assert.Equal(t, HeaderContentTypeText, template.Header.Content.HeaderContentType())
|
||||||
assert.Equal(t, "http://example.com/intaro/d2354125", template.HeaderParams.ImageURL)
|
|
||||||
assert.Equal(t, "http://example.com/intaro/d2222", template.HeaderParams.VideoURL)
|
h := template.Header.TextContent()
|
||||||
assert.Equal(t, "http://example.com/intaro/d4444", template.HeaderParams.DocumentURL)
|
assert.Equal(t, "Hello, {{1}}!", h.Body)
|
||||||
assert.Equal(t, "Scooter", *template.Footer)
|
assert.Equal(t, "Scooter", template.Footer)
|
||||||
assert.Equal(t, "approved", template.VerificationStatus)
|
assert.Equal(t, TemplateStatusApproved, template.VerificationStatus)
|
||||||
assert.Equal(t, URLButton, template.ButtonParams[0].ButtonType)
|
assert.Equal(t, ButtonTypeURL, template.Buttons.Items[0].ButtonType())
|
||||||
assert.Equal(t, "222ddd", template.ButtonParams[0].URLParameter)
|
assert.Equal(t, "222ddd", template.Buttons.Items[0].(*URLButton).URL)
|
||||||
assert.Equal(t, QuickReplyButton, template.ButtonParams[1].ButtonType)
|
assert.Equal(t, "Go to website", template.Buttons.Items[0].(*URLButton).Label)
|
||||||
assert.Equal(t, "Yes", template.ButtonParams[1].Text)
|
assert.Equal(t, ButtonTypePlain, template.Buttons.Items[1].ButtonType())
|
||||||
|
assert.Equal(t, "Yes", template.Buttons.Items[1].(*PlainButton).Label)
|
||||||
|
|
||||||
input = `{"footer": "Scooter"}`
|
input = `{"footer": "Scooter"}`
|
||||||
template = Template{}
|
template = Template{}
|
||||||
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
||||||
assert.Nil(t, template.HeaderParams)
|
assert.Nil(t, template.Header)
|
||||||
assert.Empty(t, template.ButtonParams)
|
assert.Empty(t, template.Buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalInteractiveTemplate_DocumentHeader(t *testing.T) {
|
||||||
|
var template Template
|
||||||
|
input := `{
|
||||||
|
"code":"aaa#bbb#ru",
|
||||||
|
"phone": "79252223456",
|
||||||
|
"channel_id": 1,
|
||||||
|
"header": {
|
||||||
|
"content": {
|
||||||
|
"type": "document"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": "Scooter",
|
||||||
|
"buttons": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "url",
|
||||||
|
"label": "Go to website",
|
||||||
|
"url": "222ddd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "plain",
|
||||||
|
"label": "Yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"verification_status": "approved"
|
||||||
|
}`
|
||||||
|
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
||||||
|
|
||||||
|
assert.Equal(t, "aaa#bbb#ru", template.Code)
|
||||||
|
assert.Equal(t, HeaderContentTypeDocument, template.Header.Content.HeaderContentType())
|
||||||
|
assert.NotNil(t, template.Header.DocumentContent())
|
||||||
|
assert.Equal(t, "Scooter", template.Footer)
|
||||||
|
assert.Equal(t, TemplateStatusApproved, template.VerificationStatus)
|
||||||
|
assert.Equal(t, ButtonTypeURL, template.Buttons.Items[0].ButtonType())
|
||||||
|
assert.Equal(t, "222ddd", template.Buttons.Items[0].(*URLButton).URL)
|
||||||
|
assert.Equal(t, "Go to website", template.Buttons.Items[0].(*URLButton).Label)
|
||||||
|
assert.Equal(t, ButtonTypePlain, template.Buttons.Items[1].ButtonType())
|
||||||
|
assert.Equal(t, "Yes", template.Buttons.Items[1].(*PlainButton).Label)
|
||||||
|
|
||||||
|
input = `{"footer": "Scooter"}`
|
||||||
|
template = Template{}
|
||||||
|
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
||||||
|
assert.Nil(t, template.Header)
|
||||||
|
assert.Empty(t, template.Buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalInteractiveTemplate_ImageHeader(t *testing.T) {
|
||||||
|
var template Template
|
||||||
|
input := `{
|
||||||
|
"code":"aaa#bbb#ru",
|
||||||
|
"phone": "79252223456",
|
||||||
|
"channel_id": 1,
|
||||||
|
"header": {
|
||||||
|
"content": {
|
||||||
|
"type": "image"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": "Scooter",
|
||||||
|
"buttons": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "url",
|
||||||
|
"label": "Go to website",
|
||||||
|
"url": "222ddd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "plain",
|
||||||
|
"label": "Yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"verification_status": "approved"
|
||||||
|
}`
|
||||||
|
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
||||||
|
|
||||||
|
assert.Equal(t, "aaa#bbb#ru", template.Code)
|
||||||
|
assert.Equal(t, HeaderContentTypeImage, template.Header.Content.HeaderContentType())
|
||||||
|
assert.NotNil(t, template.Header.ImageContent())
|
||||||
|
assert.Equal(t, "Scooter", template.Footer)
|
||||||
|
assert.Equal(t, TemplateStatusApproved, template.VerificationStatus)
|
||||||
|
assert.Equal(t, ButtonTypeURL, template.Buttons.Items[0].ButtonType())
|
||||||
|
assert.Equal(t, "222ddd", template.Buttons.Items[0].(*URLButton).URL)
|
||||||
|
assert.Equal(t, "Go to website", template.Buttons.Items[0].(*URLButton).Label)
|
||||||
|
assert.Equal(t, ButtonTypePlain, template.Buttons.Items[1].ButtonType())
|
||||||
|
assert.Equal(t, "Yes", template.Buttons.Items[1].(*PlainButton).Label)
|
||||||
|
|
||||||
|
input = `{"footer": "Scooter"}`
|
||||||
|
template = Template{}
|
||||||
|
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
||||||
|
assert.Nil(t, template.Header)
|
||||||
|
assert.Empty(t, template.Buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalInteractiveTemplate_VideoHeader(t *testing.T) {
|
||||||
|
var template Template
|
||||||
|
input := `{
|
||||||
|
"code":"aaa#bbb#ru",
|
||||||
|
"phone": "79252223456",
|
||||||
|
"channel_id": 1,
|
||||||
|
"header": {
|
||||||
|
"content": {
|
||||||
|
"type": "video"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": "Scooter",
|
||||||
|
"buttons": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "url",
|
||||||
|
"label": "Go to website",
|
||||||
|
"url": "222ddd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "plain",
|
||||||
|
"label": "Yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"verification_status": "approved"
|
||||||
|
}`
|
||||||
|
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
||||||
|
|
||||||
|
assert.Equal(t, "aaa#bbb#ru", template.Code)
|
||||||
|
assert.Equal(t, HeaderContentTypeVideo, template.Header.Content.HeaderContentType())
|
||||||
|
assert.NotNil(t, template.Header.VideoContent())
|
||||||
|
assert.Equal(t, "Scooter", template.Footer)
|
||||||
|
assert.Equal(t, TemplateStatusApproved, template.VerificationStatus)
|
||||||
|
assert.Equal(t, ButtonTypeURL, template.Buttons.Items[0].ButtonType())
|
||||||
|
assert.Equal(t, "222ddd", template.Buttons.Items[0].(*URLButton).URL)
|
||||||
|
assert.Equal(t, "Go to website", template.Buttons.Items[0].(*URLButton).Label)
|
||||||
|
assert.Equal(t, ButtonTypePlain, template.Buttons.Items[1].ButtonType())
|
||||||
|
assert.Equal(t, "Yes", template.Buttons.Items[1].(*PlainButton).Label)
|
||||||
|
|
||||||
|
input = `{"footer": "Scooter"}`
|
||||||
|
template = Template{}
|
||||||
|
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
||||||
|
assert.Nil(t, template.Header)
|
||||||
|
assert.Empty(t, template.Buttons)
|
||||||
}
|
}
|
||||||
|
53
v1/template_type.go
Normal file
53
v1/template_type.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TemplateType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TemplateTypeText TemplateType = iota + 1
|
||||||
|
TemplateTypeMedia
|
||||||
|
)
|
||||||
|
|
||||||
|
var TypeMap = [][]byte{
|
||||||
|
TemplateTypeText: []byte("text"),
|
||||||
|
TemplateTypeMedia: []byte("media"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrUnknownTypeValue = errors.New("unknown TemplateType")
|
||||||
|
|
||||||
|
func (e TemplateType) MarshalText() (text []byte, err error) {
|
||||||
|
if e.isValid() {
|
||||||
|
return TypeMap[e], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrUnknownTypeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TemplateType) String() string {
|
||||||
|
if e.isValid() {
|
||||||
|
return string(TypeMap[e])
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(ErrUnknownTypeValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TemplateType) UnmarshalText(text []byte) error {
|
||||||
|
for f, v := range TypeMap {
|
||||||
|
if !bytes.Equal(text, v) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
*e = TemplateType(f)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrUnknownTypeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TemplateType) isValid() bool {
|
||||||
|
return int(e) < len(TypeMap)
|
||||||
|
}
|
48
v1/types.go
48
v1/types.go
@ -557,15 +557,25 @@ type TransportRequestMeta struct {
|
|||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActivateTemplateRequest struct {
|
type UpdateTemplateRequest struct {
|
||||||
Code string `binding:"required,min=1,max=512" json:"code"`
|
Name string `json:"name"`
|
||||||
Name string `binding:"required,min=1,max=512" json:"name"`
|
Template []TemplateItem `json:"template,omitempty"`
|
||||||
Type string `binding:"required" json:"type"`
|
Body string `json:"body"`
|
||||||
Template []TemplateItem `json:"template"`
|
|
||||||
Lang string `json:"lang,omitempty"`
|
Lang string `json:"lang,omitempty"`
|
||||||
Category string `json:"category,omitempty"`
|
Category string `json:"category,omitempty"`
|
||||||
RejectionReason string `json:"rejection_reason,omitempty"`
|
Example *TemplateExample `json:"example,omitempty"`
|
||||||
VerificationStatus string `json:"verification_status,omitempty"`
|
VerificationStatus TemplateVerificationStatus `json:"verification_status"`
|
||||||
|
RejectionReason TemplateRejectionReason `json:"rejection_reason,omitempty"`
|
||||||
|
Header *TemplateHeader `json:"header,omitempty"`
|
||||||
|
Footer string `json:"footer,omitempty"`
|
||||||
|
Buttons *TemplateButtons `json:"buttons,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActivateTemplateRequest struct {
|
||||||
|
UpdateTemplateRequest
|
||||||
|
|
||||||
|
Code string `json:"code"`
|
||||||
|
Type TemplateType `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrInvalidOriginator = errors.New("invalid originator")
|
var ErrInvalidOriginator = errors.New("invalid originator")
|
||||||
@ -646,14 +656,6 @@ type HeaderParams struct {
|
|||||||
DocumentURL string `json:"documentUrl,omitempty"`
|
DocumentURL string `json:"documentUrl,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
QuickReplyButton ButtonType = "QUICK_REPLY"
|
|
||||||
PhoneNumberButton ButtonType = "PHONE_NUMBER"
|
|
||||||
URLButton ButtonType = "URL"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ButtonType string
|
|
||||||
|
|
||||||
type ButtonParam struct {
|
type ButtonParam struct {
|
||||||
ButtonType ButtonType `json:"type"`
|
ButtonType ButtonType `json:"type"`
|
||||||
Text string `json:"text,omitempty"`
|
Text string `json:"text,omitempty"`
|
||||||
@ -665,9 +667,10 @@ type TemplateContent struct {
|
|||||||
Lang string `json:"lang"`
|
Lang string `json:"lang"`
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
Example struct {
|
Header *TemplateHeader `json:"header,omitempty"`
|
||||||
Body []string `json:"body"`
|
Footer string `json:"footer,omitempty"`
|
||||||
} `json:"example"`
|
Buttons *TemplateButtons `json:"buttons,omitempty"`
|
||||||
|
Example *TemplateExample `json:"example,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TemplateCreateWebhookData struct {
|
type TemplateCreateWebhookData struct {
|
||||||
@ -700,3 +703,12 @@ const (
|
|||||||
TemplateStatusRejected TemplateVerificationStatus = "rejected"
|
TemplateStatusRejected TemplateVerificationStatus = "rejected"
|
||||||
TemplateStatusNew TemplateVerificationStatus = "new"
|
TemplateStatusNew TemplateVerificationStatus = "new"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TemplateRejectionReason string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReasonAbusiveContent TemplateRejectionReason = "abusive_content"
|
||||||
|
ReasonIncorrectCategory TemplateRejectionReason = "incorrect_category"
|
||||||
|
ReasonInvalidFormat TemplateRejectionReason = "invalid_format"
|
||||||
|
ReasonScam TemplateRejectionReason = "scam"
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user