more fields for templates
This commit is contained in:
parent
9f3cda8be6
commit
55d16c4dff
@ -153,15 +153,15 @@ func (c *MgClient) ActivateTemplate(channelID uint64, request ActivateTemplateRe
|
||||
// if err != nil {
|
||||
// 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)
|
||||
|
||||
if request.ChannelID == 0 || request.Code == "" {
|
||||
if channelID == 0 || code == "" {
|
||||
return 0, errors.New("`ChannelID` and `Code` cannot be blank")
|
||||
}
|
||||
|
||||
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 {
|
||||
return status, err
|
||||
}
|
||||
|
@ -351,8 +351,9 @@ func (t *MGClientTest) Test_ActivateTemplate() {
|
||||
c := t.client()
|
||||
req := ActivateTemplateRequest{
|
||||
Code: "tplCode",
|
||||
Name: "tplCode",
|
||||
Type: TemplateTypeText,
|
||||
UpdateTemplateRequest: UpdateTemplateRequest{
|
||||
Name: "tplCode",
|
||||
Template: []TemplateItem{
|
||||
{
|
||||
Type: TemplateItemTypeText,
|
||||
@ -368,7 +369,8 @@ func (t *MGClientTest) Test_ActivateTemplate() {
|
||||
},
|
||||
},
|
||||
RejectionReason: "",
|
||||
VerificationStatus: "approved",
|
||||
VerificationStatus: TemplateStatusApproved,
|
||||
},
|
||||
}
|
||||
|
||||
defer gock.Off()
|
||||
@ -384,12 +386,8 @@ func (t *MGClientTest) Test_ActivateTemplate() {
|
||||
|
||||
func (t *MGClientTest) Test_UpdateTemplate() {
|
||||
c := t.client()
|
||||
tpl := Template{
|
||||
Code: "encodable#code",
|
||||
ChannelID: 1,
|
||||
tpl := UpdateTemplateRequest{
|
||||
Name: "updated name",
|
||||
Enabled: true,
|
||||
Type: TemplateTypeText,
|
||||
Template: []TemplateItem{
|
||||
{
|
||||
Type: TemplateItemTypeText,
|
||||
@ -421,16 +419,20 @@ func (t *MGClientTest) Test_UpdateTemplate() {
|
||||
t.gock().
|
||||
Get(t.transportURL("templates")).
|
||||
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))
|
||||
|
||||
templates, status, err := c.TransportTemplates()
|
||||
t.Assert().NoError(err, fmt.Sprintf("%d %s", status, err))
|
||||
|
||||
for _, template := range templates {
|
||||
if template.Code == tpl.Code {
|
||||
if template.Code == "encodable#code" {
|
||||
t.Assert().Equal(tpl.Name, template.Name)
|
||||
}
|
||||
}
|
||||
@ -438,10 +440,8 @@ func (t *MGClientTest) Test_UpdateTemplate() {
|
||||
|
||||
func (t *MGClientTest) Test_UpdateTemplateFail() {
|
||||
c := t.client()
|
||||
tpl := Template{
|
||||
tpl := UpdateTemplateRequest{
|
||||
Name: "updated name",
|
||||
Enabled: true,
|
||||
Type: TemplateTypeText,
|
||||
Template: []TemplateItem{
|
||||
{
|
||||
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))
|
||||
}
|
||||
|
||||
|
235
v1/template.go
235
v1/template.go
@ -1,14 +1,12 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// TemplateTypeText is a text template type. There is no other types for now.
|
||||
const TemplateTypeText = "text"
|
||||
|
||||
const (
|
||||
// TemplateItemTypeText is a type for text chunk in template.
|
||||
TemplateItemTypeText uint8 = iota
|
||||
@ -37,19 +35,232 @@ var templateVarAssoc = map[string]interface{}{
|
||||
|
||||
// Template struct.
|
||||
type Template struct {
|
||||
Code string `json:"code"`
|
||||
ChannelID uint64 `json:"channel_id,omitempty"`
|
||||
ID int64 `json:"id,omitempty"`
|
||||
Code string `json:"code,omitempty"`
|
||||
ChannelID uint64 `json:"channel_id"`
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Type TemplateType `json:"type"`
|
||||
Template []TemplateItem `json:"template"`
|
||||
HeaderParams *HeaderParams `json:"headerParams,omitempty"`
|
||||
Footer *string `json:"footer,omitempty"`
|
||||
ButtonParams []ButtonParam `json:"buttonParams,omitempty"`
|
||||
Body string `json:"body"`
|
||||
Lang string `json:"lang,omitempty"`
|
||||
Category string `json:"category,omitempty"`
|
||||
RejectionReason string `json:"rejection_reason,omitempty"`
|
||||
VerificationStatus string `json:"verification_status,omitempty"`
|
||||
Example *TemplateExample `json:"example,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) 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.
|
||||
|
@ -95,15 +95,15 @@ func TestUnmarshalMediaInteractiveTemplate(t *testing.T) {
|
||||
assert.NoError(t, json.Unmarshal([]byte(input), &template))
|
||||
|
||||
assert.Equal(t, "aaa#bbb#ru", template.Code)
|
||||
assert.Equal(t, []string{"Johny", "1234C"}, template.HeaderParams.TextVars)
|
||||
assert.Equal(t, []string{"Johny", "1234C"}, template.Header.Content.TextVars)
|
||||
assert.Equal(t, "http://example.com/intaro/d2354125", template.HeaderParams.ImageURL)
|
||||
assert.Equal(t, "http://example.com/intaro/d2222", template.HeaderParams.VideoURL)
|
||||
assert.Equal(t, "http://example.com/intaro/d4444", template.HeaderParams.DocumentURL)
|
||||
assert.Equal(t, "Scooter", *template.Footer)
|
||||
assert.Equal(t, "approved", template.VerificationStatus)
|
||||
assert.Equal(t, URLButton, template.ButtonParams[0].ButtonType)
|
||||
assert.Equal(t, "URL", template.ButtonParams[0].ButtonType)
|
||||
assert.Equal(t, "222ddd", template.ButtonParams[0].URLParameter)
|
||||
assert.Equal(t, QuickReplyButton, template.ButtonParams[1].ButtonType)
|
||||
assert.Equal(t, "QUICK_REPLY", template.ButtonParams[1].ButtonType)
|
||||
assert.Equal(t, "Yes", template.ButtonParams[1].Text)
|
||||
|
||||
input = `{"footer": "Scooter"}`
|
||||
|
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 UnknownTypeValue = errors.New("unknown TemplateType")
|
||||
|
||||
func (e TemplateType) MarshalText() (text []byte, err error) {
|
||||
if e.isValid() {
|
||||
return TypeMap[e], nil
|
||||
}
|
||||
|
||||
return nil, UnknownTypeValue
|
||||
}
|
||||
|
||||
func (e TemplateType) String() string {
|
||||
if e.isValid() {
|
||||
return string(TypeMap[e])
|
||||
}
|
||||
|
||||
panic(UnknownTypeValue)
|
||||
}
|
||||
|
||||
func (e *TemplateType) UnmarshalText(text []byte) error {
|
||||
for f, v := range TypeMap {
|
||||
if !bytes.Equal(text, v) {
|
||||
continue
|
||||
}
|
||||
|
||||
*e = TemplateType(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
return UnknownTypeValue
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type ActivateTemplateRequest struct {
|
||||
Code string `binding:"required,min=1,max=512" json:"code"`
|
||||
Name string `binding:"required,min=1,max=512" json:"name"`
|
||||
Type string `binding:"required" json:"type"`
|
||||
Template []TemplateItem `json:"template"`
|
||||
type UpdateTemplateRequest struct {
|
||||
Name string `json:"name"`
|
||||
Template []TemplateItem `json:"template,omitempty"`
|
||||
Body string `json:"body"`
|
||||
Lang string `json:"lang,omitempty"`
|
||||
Category string `json:"category,omitempty"`
|
||||
RejectionReason string `json:"rejection_reason,omitempty"`
|
||||
VerificationStatus string `json:"verification_status,omitempty"`
|
||||
Example *TemplateExample `json:"example,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")
|
||||
@ -646,14 +656,6 @@ type HeaderParams struct {
|
||||
DocumentURL string `json:"documentUrl,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
QuickReplyButton ButtonType = "QUICK_REPLY"
|
||||
PhoneNumberButton ButtonType = "PHONE_NUMBER"
|
||||
URLButton ButtonType = "URL"
|
||||
)
|
||||
|
||||
type ButtonType string
|
||||
|
||||
type ButtonParam struct {
|
||||
ButtonType ButtonType `json:"type"`
|
||||
Text string `json:"text,omitempty"`
|
||||
@ -665,9 +667,10 @@ type TemplateContent struct {
|
||||
Lang string `json:"lang"`
|
||||
Category string `json:"category"`
|
||||
Body string `json:"body"`
|
||||
Example struct {
|
||||
Body []string `json:"body"`
|
||||
} `json:"example"`
|
||||
Header *TemplateHeader `json:"header,omitempty"`
|
||||
Footer string `json:"footer,omitempty"`
|
||||
Buttons *TemplateButtons `json:"buttons,omitempty"`
|
||||
Example *TemplateExample `json:"example,omitempty"`
|
||||
}
|
||||
|
||||
type TemplateCreateWebhookData struct {
|
||||
@ -700,3 +703,12 @@ const (
|
||||
TemplateStatusRejected TemplateVerificationStatus = "rejected"
|
||||
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