From 16e2bc304c53272d766bb1e613cc1b0d66bcc525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D1=84=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=A0=D1=83=D1=81?= =?UTF-8?q?=D0=BB=D0=B0=D0=BD?= Date: Wed, 17 May 2023 17:17:34 +0300 Subject: [PATCH 1/3] add methods for mg templates --- .golangci.yml | 2 - client.go | 51 +++++++++++++++++++++ client_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ request.go | 5 ++ response.go | 6 +++ template.go | 122 +++++++++++++++++++++++++++++++++++++++++++++++++ testutils.go | 83 +++++++++++++++++++++++++++++++++ types.go | 26 +++++++++++ 8 files changed, 409 insertions(+), 2 deletions(-) create mode 100644 template.go diff --git a/.golangci.yml b/.golangci.yml index ae89d21..93c1179 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -16,8 +16,6 @@ output: linters: disable-all: true enable: - - paralleltest - - tparallel - asciicheck - asasalint - varnamelen diff --git a/client.go b/client.go index d36cb49..0291ea3 100644 --- a/client.go +++ b/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 +} diff --git a/client_test.go b/client_test.go index e9ac0cc..d9c51eb 100644 --- a/client_test.go +++ b/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]) +} diff --git a/request.go b/request.go index bd88231..24ff2d0 100644 --- a/request.go +++ b/request.go @@ -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 == "" { diff --git a/response.go b/response.go index 501d01a..36c620e 100644 --- a/response.go +++ b/response.go @@ -651,3 +651,9 @@ type ActionProductsGroupResponse struct { SuccessfulResponse ID int `json:"id"` } + +type MGChannelTemplatesResponse struct { + Pagination *Pagination `json:"pagination"` + Templates []MGChannelTemplate `json:"templates"` + SuccessfulResponse +} diff --git a/template.go b/template.go new file mode 100644 index 0000000..018a664 --- /dev/null +++ b/template.go @@ -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"` +} diff --git a/testutils.go b/testutils.go index 6566bae..9e6e259 100644 --- a/testutils.go +++ b/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}]` +} diff --git a/types.go b/types.go index fc2ef16..95548b3 100644 --- a/types.go +++ b/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"` +} From 9bd3d646fc7b7a3385bc119cd422d61f31d9b6d2 Mon Sep 17 00:00:00 2001 From: Ruslan Efanov Date: Mon, 22 May 2023 09:00:51 +0300 Subject: [PATCH 2/3] correct templates methods --- client_test.go | 14 ++++++++++++-- template.go | 28 ++++++++++++++-------------- testutils.go | 11 +++++++---- types.go | 29 +++++++++++++++-------------- 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/client_test.go b/client_test.go index d9c51eb..21b0ff6 100644 --- a/client_test.go +++ b/client_test.go @@ -7796,12 +7796,22 @@ func TestClient_ListMGChannelTemplates(t *testing.T) { 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, []string{"Hello,", "{{1}}"}, template.Header.Text.Parts) + + 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) @@ -7823,7 +7833,7 @@ func TestClient_ListMGChannelTemplates(t *testing.T) { data, err := json.Marshal(template.BodyTemplate) assert.NoError(t, err) - assert.NotEmpty(t, `["Text_0",{}]`, string(data)) + assert.Equal(t, `["Text_0",{"var":"custom"}]`, string(data)) } func TestClient_ListMGChannelEmptyHeaderButtons(t *testing.T) { diff --git a/template.go b/template.go index 018a664..330f9c8 100644 --- a/template.go +++ b/template.go @@ -36,8 +36,8 @@ var templateVarAssoc = map[string]interface{}{ } type Text struct { - Parts []string `json:"parts"` - Example []string `json:"example,omitempty"` + Parts []TextTemplateItem `json:"parts"` + Example []string `json:"example,omitempty"` } type Media struct { @@ -51,17 +51,17 @@ type Header struct { Video *Media `json:"video,omitempty"` } -type TemplateItemList []BodyTemplateItem +type TemplateItemList []TextTemplateItem -// BodyTemplateItem is a part of template. -type BodyTemplateItem struct { +// TextTemplateItem is a part of template. +type TextTemplateItem struct { Text string VarType string Type uint8 } -// MarshalJSON controls how BodyTemplateItem will be marshaled into JSON. -func (t BodyTemplateItem) MarshalJSON() ([]byte, error) { +// 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) @@ -71,11 +71,11 @@ func (t BodyTemplateItem) MarshalJSON() ([]byte, error) { }) } - return nil, errors.New("unknown BodyTemplateItem type") + return nil, errors.New("unknown TextTemplateItem type") } -// UnmarshalJSON will correctly unmarshal BodyTemplateItem. -func (t *BodyTemplateItem) UnmarshalJSON(b []byte) error { +// UnmarshalJSON will correctly unmarshal TextTemplateItem. +func (t *TextTemplateItem) UnmarshalJSON(b []byte) error { var obj interface{} err := json.Unmarshal(b, &obj) if err != nil { @@ -89,8 +89,8 @@ func (t *BodyTemplateItem) UnmarshalJSON(b []byte) error { case map[string]interface{}: // {} case if len(bodyPart) == 0 { - t.Type = TemplateItemTypeVar - t.VarType = TemplateVarCustom + t.Type = TemplateItemTypeText + t.Text = "{}" return nil } @@ -102,10 +102,10 @@ func (t *BodyTemplateItem) UnmarshalJSON(b []byte) error { t.Type = TemplateItemTypeVar t.VarType = varTypeCurr } else { - return errors.New("invalid BodyTemplateItem") + return errors.New("invalid TextTemplateItem") } default: - return errors.New("invalid BodyTemplateItem") + return errors.New("invalid TextTemplateItem") } return nil diff --git a/testutils.go b/testutils.go index 9e6e259..ba13e20 100644 --- a/testutils.go +++ b/testutils.go @@ -464,15 +464,18 @@ func getMGTemplatesResponse() string { "var": "custom" } ], + "templateExample": ["Text_1"], "namespace": "namespace_0", "lang": "en", "category": "test_0", "header": { "text": { "parts": [ - "Hello,", - "{{1}}" - ], + "JABAAA", + { + "var": "custom" + } + ], "example": [ "AAAAAA" ] @@ -514,5 +517,5 @@ func getMGTemplatesResponse() string { } 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}]` + 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"}],"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}]` } diff --git a/types.go b/types.go index 95548b3..21ff671 100644 --- a/types.go +++ b/types.go @@ -1474,18 +1474,19 @@ type MGChannel struct { } 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"` + 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"` } From e6efd56497a110376b4512f82dc0805044171c78 Mon Sep 17 00:00:00 2001 From: Ruslan Efanov Date: Tue, 23 May 2023 15:17:38 +0300 Subject: [PATCH 3/3] fix test --- testutils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutils.go b/testutils.go index ba13e20..08ac619 100644 --- a/testutils.go +++ b/testutils.go @@ -517,5 +517,5 @@ func getMGTemplatesResponse() string { } 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"}],"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}]` + 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}]` }