1
0
mirror of synced 2024-11-21 20:46:05 +03:00
* Channels methods
* Messages methods
* Partial tests
* Docs
This commit is contained in:
Alex Lushpai 2018-05-17 17:26:18 +03:00 committed by GitHub
parent 11cb6a026f
commit 11ed32136e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 701 additions and 23 deletions

29
.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
# IDE's files
.idea
*.iml
# Project ignores

9
.travis.yml Normal file
View File

@ -0,0 +1,9 @@
language: go
go:
- '1.8'
- '1.9'
- '1.10'
before_install:
- go get -v github.com/google/go-querystring/query
- go get -v github.com/h2non/gock
script: go test -v ./...

42
LICENSE
View File

@ -1,21 +1,21 @@
MIT License
Copyright (c) 2018 retailCRM
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The MIT License (MIT)
Copyright (c) 2015-2018 RetailDriver LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,3 +1,50 @@
# MG API Go client
[![Build Status](https://img.shields.io/travis/retailcrm/mg-transport-api-client-go/master.svg?style=flat-square)](https://travis-ci.org/retailcrm/mg-transport-api-client-go)
[![GitHub release](https://img.shields.io/github/release/retailcrm/mg-transport-api-client-go.svg?style=flat-square)](https://github.com/retailcrm/mg-transport-api-client-go/releases)
[![GoLang version](https://img.shields.io/badge/GoLang-1.8%2C%201.9%2C%201.10-blue.svg?style=flat-square)](https://golang.org/dl/)
Go client for MG
# retailCRM Message Gateway Transport API Go client
## Install
```bash
go get -x github.com/retailcrm/mg-transport-api-client-go
```
## Usage
```golang
package main
import (
"fmt"
"net/http"
"github.com/retailcrm/mg-transport-api-client-go/v1"
)
func main() {
var client = v1.New("https://token.url", "cb8ccf05e38a47543ad8477d49bcba99be73bff503ea6")
ch := Channel{
Type: "telegram",
Events: []string{
"message_sent",
"message_updated",
"message_deleted",
"message_read",
},
}
data, status, err := c.ActivateTransportChannel(ch)
if err != nil {
t.Errorf("%d %v", status, err)
}
fmt.Printf("%v", data.ChannelID)
}
```
## Documentation
* [GoDoc](https://godoc.org/github.com/retailcrm/mg-transport-api-client-go)

272
v1/client.go Normal file
View File

@ -0,0 +1,272 @@
package v1
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"time"
)
// New initialize client
func New(url string, token string) *MgClient {
return &MgClient{
url,
token,
&http.Client{Timeout: 20 * time.Second},
}
}
// ActivateTransportChannel implement channel activation
//
// Example:
//
// var client = v1.New("https://token.url", "cb8ccf05e38a47543ad8477d49bcba99be73bff503ea6")
//
// request := ActivateRequest{
// Type: "telegram",
// Events: [2]int{"message_sent", "message_sent"}
// }
//
// data, status, err := client.ActivateTransportChannel(request)
//
// if err != nil {
// fmt.Printf("%v", err)
// }
//
// fmt.Printf("%s\n", data.CreatedAt)
func (c *MgClient) ActivateTransportChannel(request Channel) (ActivateResponse, int, error) {
var resp ActivateResponse
outgoing, _ := json.Marshal(&request)
data, status, err := c.PostRequest("/transport/channels", []byte(outgoing))
if err != nil {
return resp, status, err
}
if err := json.Unmarshal(data, &resp); err != nil {
return resp, status, err
}
if status > http.StatusCreated || status < http.StatusOK {
return resp, status, c.Error(data)
}
return resp, status, err
}
// UpdateTransportChannel implement channel activation
//
// Example:
//
// var client = v1.New("https://token.url", "cb8ccf05e38a47543ad8477d49bcba99be73bff503ea6")
//
// request := ActivateRequest{
// ID: 3053450384,
// Type: "telegram",
// Events: [2]int{"message_sent", "message_sent"}
// }
//
// data, status, err := client.UpdateTransportChannel(request)
//
// if err != nil {
// fmt.Printf("%v", err)
// }
//
// fmt.Printf("%s\n", data.UpdatedAt)
func (c *MgClient) UpdateTransportChannel(request Channel) (UpdateResponse, int, error) {
var resp UpdateResponse
outgoing, _ := json.Marshal(&request)
data, status, err := c.PutRequest(fmt.Sprintf("/transport/channels/%d", request.ID), []byte(outgoing))
if err != nil {
return resp, status, err
}
if err := json.Unmarshal(data, &resp); err != nil {
return resp, status, err
}
if status != http.StatusOK {
return resp, status, c.Error(data)
}
return resp, status, err
}
// DeactivateTransportChannel implement channel deactivation
//
// Example:
//
// var client = v1.New("https://token.url", "cb8ccf05e38a47543ad8477d49bcba99be73bff503ea6")
//
// data, status, err := client.DeactivateTransportChannel(3053450384)
//
// if err != nil {
// fmt.Printf("%v", err)
// }
//
// fmt.Printf("%s\n", data.DeactivatedAt)
func (c *MgClient) DeactivateTransportChannel(id uint64) (DeleteResponse, int, error) {
var resp DeleteResponse
data, status, err := c.DeleteRequest(fmt.Sprintf("/transport/channels/%s", strconv.FormatUint(id, 10)))
if err != nil {
return resp, status, err
}
if err := json.Unmarshal(data, &resp); err != nil {
return resp, status, err
}
if status != http.StatusOK {
return resp, status, c.Error(data)
}
return resp, status, err
}
// Messages implement send message
//
// Example:
//
// var client = v1.New("https://token.url", "cb8ccf05e38a47543ad8477d49bcba99be73bff503ea6")
// snd := SendData{
// SendMessage{
// Message{
// ExternalID: "23e23e23",
// Channel: channelId,
// Type: "text",
// Text: "hello!",
// },
// time.Now(),
// },
// User{
// ExternalID: "8",
// Nickname: "@octopulus",
// Firstname: "Joe",
// },
// channelId,
// }
//
// data, status, err := client.Messages(snd)
//
// if err != nil {
// fmt.Printf("%v", err)
// }
//
// fmt.Printf("%s\n", data.MessageID)
func (c *MgClient) Messages(request SendData) (MessagesResponse, int, error) {
var resp MessagesResponse
outgoing, _ := json.Marshal(&request)
data, status, err := c.PostRequest("/transport/messages", []byte(outgoing))
if err != nil {
return resp, status, err
}
if err := json.Unmarshal(data, &resp); err != nil {
return resp, status, err
}
if status != http.StatusOK {
return resp, status, c.Error(data)
}
return resp, status, err
}
// UpdateMessages implement edit message
//
// Example:
//
// var client = v1.New("https://token.url", "cb8ccf05e38a47543ad8477d49bcba99be73bff503ea6")
// snd := SendData{
// SendMessage{
// Message{
// ExternalID: "23e23e23",
// Channel: channelId,
// Type: "text",
// Text: "hello!",
// },
// time.Now(),
// },
// User{
// ExternalID: "8",
// Nickname: "@octopulus",
// Firstname: "Joe",
// },
// channelId,
// }
//
// data, status, err := client.UpdateMessages(snd)
//
// if err != nil {
// fmt.Printf("%v", err)
// }
//
// fmt.Printf("%s\n", data.MessageID)
func (c *MgClient) UpdateMessages(request UpdateMessage) (MessagesResponse, int, error) {
var resp MessagesResponse
outgoing, _ := json.Marshal(&request)
data, status, err := c.PutRequest("/transport/messages", []byte(outgoing))
if err != nil {
return resp, status, err
}
if err := json.Unmarshal(data, &resp); err != nil {
return resp, status, err
}
if status != http.StatusOK {
return resp, status, c.Error(data)
}
return resp, status, err
}
// DeleteMessage implement delete message
//
// Example:
//
// var client = v1.New("https://token.url", "cb8ccf05e38a47543ad8477d49bcba99be73bff503ea6")
//
// data, status, err := client.DeleteMessage("3053450384")
//
// if err != nil {
// fmt.Printf("%v", err)
// }
//
// fmt.Printf("%s\n", data.MessageID)
func (c *MgClient) DeleteMessage(id string) (MessagesResponse, int, error) {
var resp MessagesResponse
data, status, err := c.DeleteRequest(fmt.Sprintf("/transport/messages/%s", id))
if err != nil {
return resp, status, err
}
if err := json.Unmarshal(data, &resp); err != nil {
return resp, status, err
}
if status != http.StatusOK {
return resp, status, c.Error(data)
}
return resp, status, err
}
func (c *MgClient) Error(info []byte) error {
var data map[string]interface{}
if err := json.Unmarshal(info, &data); err != nil {
return err
}
values := data["errors"].([]interface{})
return errors.New(values[0].(string))
}

127
v1/client_test.go Normal file
View File

@ -0,0 +1,127 @@
package v1
import (
"net/http"
"os"
"strconv"
"testing"
)
var (
mgURL = os.Getenv("MG_URL")
mgToken = os.Getenv("MG_TOKEN")
channelId, _ = strconv.ParseUint(os.Getenv("MG_CHANNEL"), 10, 64)
)
func client() *MgClient {
return New(mgURL, mgToken)
}
func TestMgClient_ActivateTransportChannel(t *testing.T) {
c := client()
ch := Channel{
ID: channelId,
Type: "telegram",
Events: []string{
"message_sent",
"message_updated",
"message_deleted",
"message_read",
},
}
data, status, err := c.ActivateTransportChannel(ch)
if err != nil {
t.Errorf("%d %v", status, err)
}
t.Logf("%v", data.ChannelID)
}
func TestMgClient_ActivateNewTransportChannel(t *testing.T) {
c := client()
ch := Channel{
Type: "telegram",
Events: []string{
"message_sent",
"message_updated",
"message_deleted",
"message_read",
},
}
data, status, err := c.ActivateTransportChannel(ch)
if err != nil {
t.Errorf("%d %v", status, err)
}
t.Logf("%v", data.ChannelID)
}
func TestMgClient_UpdateTransportChannel(t *testing.T) {
c := client()
ch := Channel{
ID: channelId,
Events: []string{
"message_sent",
"message_read",
},
}
data, status, err := c.UpdateTransportChannel(ch)
if status != http.StatusOK {
t.Errorf("%v", err)
}
t.Logf("%v", data.ChannelID)
}
func TestMgClient_DeactivateTransportChannel(t *testing.T) {
c := client()
deleteData, status, err := c.DeactivateTransportChannel(channelId)
if err != nil {
t.Errorf("%d %v", status, err)
}
if deleteData.DectivatedAt.String() == "" {
t.Errorf("%v", err)
}
t.Logf("%v", deleteData.ChannelID)
}
/*func TestMgClient_Messages(t *testing.T) {
c := client()
snd := SendData{
SendMessage{
Message{
ExternalID: "23e23e23",
Channel: channelId,
Type: "text",
Text: "hello!",
},
time.Now(),
},
User{
ExternalID: "8",
Nickname: "@octopulus",
Firstname: "Joe",
},
channelId,
}
data, status, err := c.Messages(snd)
if status != http.StatusOK {
t.Errorf("%v", err)
}
if data.Time.String() == "" {
t.Errorf("%v", err)
}
}*/

110
v1/request.go Normal file
View File

@ -0,0 +1,110 @@
package v1
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
)
var prefix = "/api/v1"
// GetRequest implements GET Request
func (c *MgClient) GetRequest(urlWithParameters string) ([]byte, int, error) {
var res []byte
req, err := http.NewRequest("GET", fmt.Sprintf("%s%s%s", c.URL, prefix, urlWithParameters), nil)
if err != nil {
return res, 0, err
}
req.Header.Set("X-Transport-Token", c.Token)
resp, err := c.httpClient.Do(req)
if err != nil {
return res, 0, err
}
if resp.StatusCode >= http.StatusInternalServerError {
err = fmt.Errorf("HTTP request error. Status code: %d.\n", resp.StatusCode)
return res, resp.StatusCode, err
}
res, err = buildRawResponse(resp)
if err != nil {
return res, resp.StatusCode, err
}
return res, resp.StatusCode, err
}
// PostRequest implements POST Request
func (c *MgClient) PostRequest(url string, parameters []byte) ([]byte, int, error) {
return makeRequest(
"POST",
fmt.Sprintf("%s%s%s", c.URL, prefix, url),
bytes.NewBuffer(parameters),
c,
)
}
// PutRequest implements PUT Request
func (c *MgClient) PutRequest(url string, parameters []byte) ([]byte, int, error) {
return makeRequest(
"PUT",
fmt.Sprintf("%s%s%s", c.URL, prefix, url),
bytes.NewBuffer(parameters),
c,
)
}
// DeleteRequest implements DELETE Request
func (c *MgClient) DeleteRequest(url string) ([]byte, int, error) {
var buf []byte
return makeRequest(
"DELETE",
fmt.Sprintf("%s%s%s", c.URL, prefix, url),
bytes.NewBuffer(buf),
c,
)
}
func makeRequest(reqType, url string, buf *bytes.Buffer, c *MgClient) ([]byte, int, error) {
var res []byte
req, err := http.NewRequest(reqType, url, buf)
if err != nil {
return res, 0, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Transport-Token", c.Token)
resp, err := c.httpClient.Do(req)
if err != nil {
return res, 0, err
}
if resp.StatusCode >= http.StatusInternalServerError {
err = fmt.Errorf("HTTP request error. Status code: %d.\n", resp.StatusCode)
return res, resp.StatusCode, err
}
res, err = buildRawResponse(resp)
if err != nil {
return res, 0, err
}
return res, resp.StatusCode, err
}
func buildRawResponse(resp *http.Response) ([]byte, error) {
defer resp.Body.Close()
res, err := ioutil.ReadAll(resp.Body)
if err != nil {
return res, err
}
return res, nil
}

1
v1/request_test.go Normal file
View File

@ -0,0 +1 @@
package v1

83
v1/types.go Normal file
View File

@ -0,0 +1,83 @@
package v1
import (
"net/http"
"time"
)
// MgClient type
type MgClient struct {
URL string
Token string
httpClient *http.Client
}
// Channel type
type Channel struct {
ID uint64 `url:"id,omitempty"`
Type string `url:"type,omitempty"`
Events []string `url:"events,omitempty,brackets"`
}
// ActivateResponse channel activation response
type ActivateResponse struct {
ChannelID uint64 `json:"id"`
ActivatedAt time.Time `json:"activated_at"`
}
// UpdateResponse channel update response
type UpdateResponse struct {
ChannelID uint64 `json:"id"`
UpdatedAt time.Time `json:"updated_at"`
}
// DeleteResponse channel deactivation response
type DeleteResponse struct {
ChannelID uint64 `json:"id"`
DectivatedAt time.Time `json:"deactivated_at"`
}
// User struct
type User struct {
ExternalID string `url:"external_id"`
Nickname string `url:"nickname"`
Firstname string `url:"first_name,omitempty"`
Lastname string `url:"last_name,omitempty"`
Avatar string `url:"avatar,omitempty"`
ProfileURL string `url:"profile_url,omitempty"`
Country string `url:"country,omitempty"`
Language string `url:"language,omitempty"`
Phone string `url:"phone,omitempty"`
}
// Message struct
type Message struct {
ExternalID string `url:"external_id"`
Channel uint64 `url:"channel"`
Type string `url:"type"`
Text string `url:"text,omitempty"`
}
// SendMessage struct
type SendMessage struct {
Message
SentAt time.Time `url:"sent_at,omitempty"`
}
// UpdateMessage struct
type UpdateMessage struct {
Message
EditedAt time.Time `url:"edited_at,omitempty"`
}
type SendData struct {
Message SendMessage `url:"message"`
User User `url:"user"`
Channel uint64 `url:"channel"`
}
// MessagesResponse message event response
type MessagesResponse struct {
MessageID string `json:"message_id"`
Time time.Time `json:"time"`
}