mirror of
https://github.com/retailcrm/api-client-go.git
synced 2024-11-22 04:46:03 +03:00
add methods for mg templates
This commit is contained in:
commit
280f078632
@ -16,8 +16,6 @@ output:
|
|||||||
linters:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
enable:
|
enable:
|
||||||
- paralleltest
|
|
||||||
- tparallel
|
|
||||||
- asciicheck
|
- asciicheck
|
||||||
- asasalint
|
- asasalint
|
||||||
- varnamelen
|
- varnamelen
|
||||||
|
51
client.go
51
client.go
@ -6531,3 +6531,54 @@ func (c *Client) NotificationsSend(req NotificationsSendRequest) (int, error) {
|
|||||||
|
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) ListMGChannelTemplates(channelID, page, limit int) (MGChannelTemplatesResponse, int, error) {
|
||||||
|
var resp MGChannelTemplatesResponse
|
||||||
|
|
||||||
|
values := url.Values{
|
||||||
|
"page": {fmt.Sprintf("%d", page)},
|
||||||
|
"limit": {fmt.Sprintf("%d", limit)},
|
||||||
|
"channel_id": {fmt.Sprintf("%d", channelID)},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, code, err := c.GetRequest(fmt.Sprintf("/reference/mg-channels/templates?%s", values.Encode()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return resp, code, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, &resp)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return resp, code, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) EditMGChannelTemplate(req EditMGChannelTemplateRequest) (int, error) {
|
||||||
|
templates, err := json.Marshal(req.Templates)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
removed, err := json.Marshal(req.Removed)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
values := url.Values{
|
||||||
|
"templates": {string(templates)},
|
||||||
|
"removed": {string(removed)},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, code, err := c.PostRequest("/reference/mg-channels/templates/edit", values)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return code, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return code, nil
|
||||||
|
}
|
||||||
|
126
client_test.go
126
client_test.go
@ -7771,3 +7771,129 @@ func TestClient_NotificationsSend(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.True(t, statuses[status])
|
assert.True(t, statuses[status])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClient_ListMGChannelTemplates(t *testing.T) {
|
||||||
|
defer gock.Off()
|
||||||
|
gock.New(crmURL).
|
||||||
|
Get("/reference/mg-channels/templates").
|
||||||
|
MatchParams(map[string]string{
|
||||||
|
"limit": "10",
|
||||||
|
"page": "5",
|
||||||
|
"channel_id": "1",
|
||||||
|
}).
|
||||||
|
Reply(http.StatusOK).
|
||||||
|
JSON(getMGTemplatesResponse())
|
||||||
|
|
||||||
|
resp, code, err := client().ListMGChannelTemplates(1, 5, 10)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, statuses[code])
|
||||||
|
assert.NotEmpty(t, resp.Pagination)
|
||||||
|
assert.NotEmpty(t, resp.Templates)
|
||||||
|
|
||||||
|
template := resp.Templates[0]
|
||||||
|
assert.Equal(t, 1, template.ID)
|
||||||
|
assert.Equal(t, 1, template.Channel.ID)
|
||||||
|
assert.Equal(t, 1, template.Channel.ExternalID)
|
||||||
|
assert.Equal(t, "NAMEAAA", template.Name)
|
||||||
|
|
||||||
|
assert.Equal(t, TemplateItemTypeText, template.BodyTemplate[0].Type)
|
||||||
|
assert.Equal(t, "Text_0", template.BodyTemplate[0].Text)
|
||||||
|
|
||||||
|
assert.Equal(t, TemplateItemTypeVar, template.BodyTemplate[1].Type)
|
||||||
|
assert.Equal(t, TemplateVarCustom, template.BodyTemplate[1].VarType)
|
||||||
|
assert.Equal(t, []string{"Text_1"}, template.BodyTemplateExample)
|
||||||
|
assert.Equal(t, "en", template.Lang)
|
||||||
|
assert.Equal(t, "test_0", template.Category)
|
||||||
|
|
||||||
|
assert.Equal(t, TemplateItemTypeText, template.Header.Text.Parts[0].Type)
|
||||||
|
assert.Equal(t, "JABAAA", template.Header.Text.Parts[0].Text)
|
||||||
|
assert.Equal(t, TemplateItemTypeVar, template.Header.Text.Parts[1].Type)
|
||||||
|
assert.Equal(t, TemplateVarCustom, template.Header.Text.Parts[1].VarType)
|
||||||
|
assert.Equal(t, []string{"AAAAAA"}, template.Header.Text.Example)
|
||||||
|
|
||||||
|
assert.Equal(t, "https://example.com/file/123.png", template.Header.Image.Example)
|
||||||
|
assert.Equal(t, "https://example.com/file/123.pdf", template.Header.Document.Example)
|
||||||
|
assert.Equal(t, "https://example.com/file/123.mp4", template.Header.Video.Example)
|
||||||
|
assert.Equal(t, "footer_0", template.Footer)
|
||||||
|
|
||||||
|
assert.Equal(t, PhoneNumber, template.Buttons[0].Type)
|
||||||
|
assert.Equal(t, "your-phone-button-text", template.Buttons[0].Text)
|
||||||
|
assert.Equal(t, "+79895553535", template.Buttons[0].PhoneNumber)
|
||||||
|
|
||||||
|
assert.Equal(t, QuickReply, template.Buttons[1].Type)
|
||||||
|
assert.Equal(t, "Yes", template.Buttons[1].Text)
|
||||||
|
|
||||||
|
assert.Equal(t, URL, template.Buttons[2].Type)
|
||||||
|
assert.Equal(t, "button", template.Buttons[2].Text)
|
||||||
|
assert.Equal(t, "https://example.com/file/{{1}}", template.Buttons[2].URL)
|
||||||
|
assert.Equal(t, []string{"https://www.website.com/dynamic-url-example"}, template.Buttons[2].Example)
|
||||||
|
|
||||||
|
assert.Equal(t, "APPROVED", template.VerificationStatus)
|
||||||
|
|
||||||
|
data, err := json.Marshal(template.BodyTemplate)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `["Text_0",{"var":"custom"}]`, string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_ListMGChannelEmptyHeaderButtons(t *testing.T) {
|
||||||
|
defer gock.Off()
|
||||||
|
gock.New(crmURL).
|
||||||
|
Get("/reference/mg-channels/templates").
|
||||||
|
MatchParams(map[string]string{
|
||||||
|
"limit": "1",
|
||||||
|
"page": "1",
|
||||||
|
"channel_id": "1",
|
||||||
|
}).
|
||||||
|
Reply(http.StatusOK).
|
||||||
|
JSON(`{
|
||||||
|
"success": true,
|
||||||
|
"pagination": {
|
||||||
|
"limit": 1,
|
||||||
|
"totalCount": 1,
|
||||||
|
"currentPage": 1,
|
||||||
|
"totalPageCount": 1
|
||||||
|
},
|
||||||
|
"templates": [
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"externalId": 0,
|
||||||
|
"channel": {
|
||||||
|
"id": 1,
|
||||||
|
"externalId": 1
|
||||||
|
},
|
||||||
|
"name": "name_0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
|
||||||
|
resp, code, err := client().ListMGChannelTemplates(1, 1, 1)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, statuses[code])
|
||||||
|
assert.NotEmpty(t, resp.Pagination)
|
||||||
|
assert.NotEmpty(t, resp.Templates)
|
||||||
|
assert.Nil(t, resp.Templates[0].Header)
|
||||||
|
assert.Nil(t, resp.Templates[0].Buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_EditMGChannelTemplate(t *testing.T) {
|
||||||
|
defer gock.Off()
|
||||||
|
|
||||||
|
tmplsJSON := getMGTemplatesForEdit()
|
||||||
|
var tmpls []MGChannelTemplate
|
||||||
|
assert.NoError(t, json.Unmarshal([]byte(tmplsJSON), &tmpls))
|
||||||
|
request := EditMGChannelTemplateRequest{Templates: tmpls, Removed: []int{1, 2, 3}}
|
||||||
|
reqValue := url.Values{
|
||||||
|
"templates": {tmplsJSON},
|
||||||
|
"removed": {"[1,2,3]"},
|
||||||
|
}
|
||||||
|
gock.New(crmURL).
|
||||||
|
Post("reference/mg-channels/templates/edit").
|
||||||
|
Body(strings.NewReader(reqValue.Encode())).
|
||||||
|
Reply(http.StatusOK)
|
||||||
|
|
||||||
|
code, err := client().EditMGChannelTemplate(request)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, statuses[code])
|
||||||
|
}
|
||||||
|
@ -284,6 +284,11 @@ type NotificationsSendRequest struct {
|
|||||||
UserIDs []string `json:"userIds,omitempty"`
|
UserIDs []string `json:"userIds,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EditMGChannelTemplateRequest struct {
|
||||||
|
Templates []MGChannelTemplate `json:"templates"`
|
||||||
|
Removed []int `json:"removed"`
|
||||||
|
}
|
||||||
|
|
||||||
// SystemURL returns system URL from the connection request without trailing slash.
|
// SystemURL returns system URL from the connection request without trailing slash.
|
||||||
func (r ConnectRequest) SystemURL() string {
|
func (r ConnectRequest) SystemURL() string {
|
||||||
if r.URL == "" {
|
if r.URL == "" {
|
||||||
|
@ -651,3 +651,9 @@ type ActionProductsGroupResponse struct {
|
|||||||
SuccessfulResponse
|
SuccessfulResponse
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MGChannelTemplatesResponse struct {
|
||||||
|
Pagination *Pagination `json:"pagination"`
|
||||||
|
Templates []MGChannelTemplate `json:"templates"`
|
||||||
|
SuccessfulResponse
|
||||||
|
}
|
||||||
|
122
template.go
Normal file
122
template.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package retailcrm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TemplateItemTypeText is a type for text chunk in template.
|
||||||
|
TemplateItemTypeText uint8 = iota
|
||||||
|
// TemplateItemTypeVar is a type for variable in template.
|
||||||
|
TemplateItemTypeVar
|
||||||
|
QuickReply ButtonType = "QUICK_REPLY"
|
||||||
|
PhoneNumber ButtonType = "PHONE_NUMBER"
|
||||||
|
URL ButtonType = "URL"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TemplateVarCustom is a custom variable type.
|
||||||
|
TemplateVarCustom = "custom"
|
||||||
|
// TemplateVarName is a name variable type.
|
||||||
|
TemplateVarName = "name"
|
||||||
|
// TemplateVarFirstName is a first name variable type.
|
||||||
|
TemplateVarFirstName = "first_name"
|
||||||
|
// TemplateVarLastName is a last name variable type.
|
||||||
|
TemplateVarLastName = "last_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
// templateVarAssoc for checking variable validity, only for internal use.
|
||||||
|
var templateVarAssoc = map[string]interface{}{
|
||||||
|
TemplateVarCustom: nil,
|
||||||
|
TemplateVarName: nil,
|
||||||
|
TemplateVarFirstName: nil,
|
||||||
|
TemplateVarLastName: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Text struct {
|
||||||
|
Parts []TextTemplateItem `json:"parts"`
|
||||||
|
Example []string `json:"example,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Media struct {
|
||||||
|
Example string `json:"example,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Header struct {
|
||||||
|
Text *Text `json:"text,omitempty"`
|
||||||
|
Document *Media `json:"document,omitempty"`
|
||||||
|
Image *Media `json:"image,omitempty"`
|
||||||
|
Video *Media `json:"video,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TemplateItemList []TextTemplateItem
|
||||||
|
|
||||||
|
// TextTemplateItem is a part of template.
|
||||||
|
type TextTemplateItem struct {
|
||||||
|
Text string
|
||||||
|
VarType string
|
||||||
|
Type uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON controls how TextTemplateItem will be marshaled into JSON.
|
||||||
|
func (t TextTemplateItem) MarshalJSON() ([]byte, error) {
|
||||||
|
switch t.Type {
|
||||||
|
case TemplateItemTypeText:
|
||||||
|
return json.Marshal(t.Text)
|
||||||
|
case TemplateItemTypeVar:
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"var": t.VarType,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("unknown TextTemplateItem type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON will correctly unmarshal TextTemplateItem.
|
||||||
|
func (t *TextTemplateItem) UnmarshalJSON(b []byte) error {
|
||||||
|
var obj interface{}
|
||||||
|
err := json.Unmarshal(b, &obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch bodyPart := obj.(type) {
|
||||||
|
case string:
|
||||||
|
t.Type = TemplateItemTypeText
|
||||||
|
t.Text = bodyPart
|
||||||
|
case map[string]interface{}:
|
||||||
|
// {} case
|
||||||
|
if len(bodyPart) == 0 {
|
||||||
|
t.Type = TemplateItemTypeText
|
||||||
|
t.Text = "{}"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if varTypeCurr, ok := bodyPart["var"].(string); ok {
|
||||||
|
if _, ok := templateVarAssoc[varTypeCurr]; !ok {
|
||||||
|
return fmt.Errorf("invalid placeholder var '%s'", varTypeCurr)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Type = TemplateItemTypeVar
|
||||||
|
t.VarType = varTypeCurr
|
||||||
|
} else {
|
||||||
|
return errors.New("invalid TextTemplateItem")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("invalid TextTemplateItem")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ButtonType string
|
||||||
|
|
||||||
|
type Button struct {
|
||||||
|
Type ButtonType `json:"type"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Text string `json:"text,omitempty"`
|
||||||
|
PhoneNumber string `json:"phoneNumber,omitempty"`
|
||||||
|
Example []string `json:"example,omitempty"`
|
||||||
|
}
|
86
testutils.go
86
testutils.go
@ -433,3 +433,89 @@ func getLoyaltyResponse() string {
|
|||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMGTemplatesResponse() string {
|
||||||
|
return `{
|
||||||
|
"success": true,
|
||||||
|
"pagination": {
|
||||||
|
"limit": 10,
|
||||||
|
"totalCount": 100,
|
||||||
|
"currentPage": 5,
|
||||||
|
"totalPageCount": 10
|
||||||
|
},
|
||||||
|
"templates": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"externalId": 0,
|
||||||
|
"channel": {
|
||||||
|
"allowedSendByPhone": false,
|
||||||
|
"id": 1,
|
||||||
|
"externalId": 1,
|
||||||
|
"type": "fbmessenger",
|
||||||
|
"active": true,
|
||||||
|
"name": "fbmessenger"
|
||||||
|
},
|
||||||
|
"code": "namespace#NAMEAAA#ru",
|
||||||
|
"name": "NAMEAAA",
|
||||||
|
"active": true,
|
||||||
|
"template": [
|
||||||
|
"Text_0",
|
||||||
|
{
|
||||||
|
"var": "custom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"templateExample": ["Text_1"],
|
||||||
|
"namespace": "namespace_0",
|
||||||
|
"lang": "en",
|
||||||
|
"category": "test_0",
|
||||||
|
"header": {
|
||||||
|
"text": {
|
||||||
|
"parts": [
|
||||||
|
"JABAAA",
|
||||||
|
{
|
||||||
|
"var": "custom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": [
|
||||||
|
"AAAAAA"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"example": "https://example.com/file/123.png"
|
||||||
|
},
|
||||||
|
"document": {
|
||||||
|
"example": "https://example.com/file/123.pdf"
|
||||||
|
},
|
||||||
|
"video": {
|
||||||
|
"example": "https://example.com/file/123.mp4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": "footer_0",
|
||||||
|
"buttons": [
|
||||||
|
{
|
||||||
|
"type": "PHONE_NUMBER",
|
||||||
|
"text": "your-phone-button-text",
|
||||||
|
"phoneNumber": "+79895553535"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "QUICK_REPLY",
|
||||||
|
"text": "Yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "URL",
|
||||||
|
"url": "https://example.com/file/{{1}}",
|
||||||
|
"text": "button",
|
||||||
|
"example": [
|
||||||
|
"https://www.website.com/dynamic-url-example"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"verificationStatus": "APPROVED"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMGTemplatesForEdit() string {
|
||||||
|
return `[{"header":{"text":{"parts":["Hello,",{"var":"custom"}],"example":["Henry"]},"document":{"example":"https://example.com/file/123.pdf"},"image":{"example":"https://example.com/file/123.png"},"video":{"example":"https://example.com/file/123.mp4"}},"lang":"en","category":"test_0","code":"namespace#name_0#ru","name":"name_0","namespace":"namespace","footer":"footer_0","verificationStatus":"REJECTED","template":["Text_0",{"var":"custom"}],"templateExample":["WIU"],"buttons":[{"type":"PHONE_NUMBER","text":"your-phone-button-text","phoneNumber":"+79895553535"},{"type":"QUICK_REPLY","text":"Yes"},{"type":"URL","url":"https://example.com/file/{{1}}","text":"button","example":["https://www.website.com/dynamic-url-example"]}],"channel":{"type":"fbmessenger","name":"JABAAAAAAAAAA","id":1,"externalId":1,"allowedSendByPhone":false,"active":true},"id":1,"externalId":10,"active":true}]`
|
||||||
|
}
|
||||||
|
27
types.go
27
types.go
@ -1463,3 +1463,30 @@ type ExternalID struct {
|
|||||||
|
|
||||||
type UserGroupType string
|
type UserGroupType string
|
||||||
type NotificationType string
|
type NotificationType string
|
||||||
|
|
||||||
|
type MGChannel struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
ExternalID int `json:"externalId"`
|
||||||
|
AllowedSendByPhone bool `json:"allowedSendByPhone"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MGChannelTemplate struct {
|
||||||
|
Header *Header `json:"header"`
|
||||||
|
Lang string `json:"lang"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Namespace string `json:"namespace,omitempty"`
|
||||||
|
Footer string `json:"footer,omitempty"`
|
||||||
|
VerificationStatus string `json:"verificationStatus,omitempty"`
|
||||||
|
BodyTemplate TemplateItemList `json:"template"`
|
||||||
|
BodyTemplateExample []string `json:"templateExample"`
|
||||||
|
Buttons []Button `json:"buttons,omitempty"`
|
||||||
|
Channel MGChannel `json:"channel"`
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
ExternalID int `json:"externalId,omitempty"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user