mirror of
https://github.com/retailcrm/api-client-go.git
synced 2024-11-24 22:06:03 +03:00
add methods for mg templates
This commit is contained in:
parent
d08ed4e1b2
commit
16e2bc304c
@ -16,8 +16,6 @@ output:
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- paralleltest
|
||||
- tparallel
|
||||
- asciicheck
|
||||
- asasalint
|
||||
- varnamelen
|
||||
|
51
client.go
51
client.go
@ -6531,3 +6531,54 @@ func (c *Client) NotificationsSend(req NotificationsSendRequest) (int, error) {
|
||||
|
||||
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
|
||||
}
|
||||
|
116
client_test.go
116
client_test.go
@ -7771,3 +7771,119 @@ func TestClient_NotificationsSend(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
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, TemplateItemTypeVar, template.BodyTemplate[1].Type)
|
||||
assert.Equal(t, "en", template.Lang)
|
||||
assert.Equal(t, "test_0", template.Category)
|
||||
assert.Equal(t, []string{"Hello,", "{{1}}"}, template.Header.Text.Parts)
|
||||
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.NotEmpty(t, `["Text_0",{}]`, 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"`
|
||||
}
|
||||
|
||||
type EditMGChannelTemplateRequest struct {
|
||||
Templates []MGChannelTemplate `json:"templates"`
|
||||
Removed []int `json:"removed"`
|
||||
}
|
||||
|
||||
// SystemURL returns system URL from the connection request without trailing slash.
|
||||
func (r ConnectRequest) SystemURL() string {
|
||||
if r.URL == "" {
|
||||
|
@ -651,3 +651,9 @@ type ActionProductsGroupResponse struct {
|
||||
SuccessfulResponse
|
||||
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 []string `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 []BodyTemplateItem
|
||||
|
||||
// BodyTemplateItem is a part of template.
|
||||
type BodyTemplateItem struct {
|
||||
Text string
|
||||
VarType string
|
||||
Type uint8
|
||||
}
|
||||
|
||||
// MarshalJSON controls how BodyTemplateItem will be marshaled into JSON.
|
||||
func (t BodyTemplateItem) 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 BodyTemplateItem type")
|
||||
}
|
||||
|
||||
// UnmarshalJSON will correctly unmarshal BodyTemplateItem.
|
||||
func (t *BodyTemplateItem) 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 = TemplateItemTypeVar
|
||||
t.VarType = TemplateVarCustom
|
||||
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 BodyTemplateItem")
|
||||
}
|
||||
default:
|
||||
return errors.New("invalid BodyTemplateItem")
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
83
testutils.go
83
testutils.go
@ -433,3 +433,86 @@ 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"
|
||||
}
|
||||
],
|
||||
"namespace": "namespace_0",
|
||||
"lang": "en",
|
||||
"category": "test_0",
|
||||
"header": {
|
||||
"text": {
|
||||
"parts": [
|
||||
"Hello,",
|
||||
"{{1}}"
|
||||
],
|
||||
"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,","{{1}}"],"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_0","footer":"footer_0","verificationStatus":"REJECTED","template":["Text_0",{"var":"custom"}],"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}]`
|
||||
}
|
||||
|
26
types.go
26
types.go
@ -1463,3 +1463,29 @@ type ExternalID struct {
|
||||
|
||||
type UserGroupType 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"`
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
Footer string `json:"footer,omitempty"`
|
||||
VerificationStatus string `json:"verificationStatus"`
|
||||
BodyTemplate TemplateItemList `json:"template,omitempty"`
|
||||
Buttons []Button `json:"buttons,omitempty"`
|
||||
Channel MGChannel `json:"channel"`
|
||||
ID int `json:"id"`
|
||||
ExternalID int `json:"externalId,omitempty"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user