init
This commit is contained in:
commit
2c765d9bfd
14 changed files with 628 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
|
92
amocrm.go
Normal file
92
amocrm.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package amo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"vultras.su/core/amo/api"
|
||||||
|
"vultras.su/core/amo/companies"
|
||||||
|
"vultras.su/core/amo/contacts"
|
||||||
|
"vultras.su/core/amo/leads"
|
||||||
|
"vultras.su/core/amo/users"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IAmoClient interface {
|
||||||
|
GetUser(userId string) (*users.User, error)
|
||||||
|
GetLead(leadId string, query string) (*leads.Lead, error)
|
||||||
|
UpdateLead(lead *leads.Lead) error
|
||||||
|
GetCompany(companyId string, query string) (*companies.Company, error)
|
||||||
|
UpdateCompany(company *companies.Company) error
|
||||||
|
GetContact(contactId string, query string) (*contacts.Contact, error)
|
||||||
|
UpdateContact(contact *contacts.Contact) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Api *api.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
//goland:noinspection GoUnusedExportedFunction
|
||||||
|
func NewAmoClient(options *api.ClientOptions) (*Client, error) {
|
||||||
|
apiClient, err := api.NewClient(options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
Api: apiClient,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) updateEntity(url string, id int, body interface{}) error {
|
||||||
|
err := client.Api.Patch(fmt.Sprintf("%s/%d", url, id), body, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) GetUser(userId string) (*users.User, error) {
|
||||||
|
user := new(users.User)
|
||||||
|
err := client.Api.Get(fmt.Sprintf("/api/v4/users/%s", userId), user)
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) GetLead(leadId string, query string) (*leads.Lead, error) {
|
||||||
|
deal := new(leads.Lead)
|
||||||
|
resource := fmt.Sprintf("/api/v4/leads/%s", leadId)
|
||||||
|
if len(query) != 0 {
|
||||||
|
resource = resource + "?" + query
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Api.Get(resource, deal)
|
||||||
|
return deal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) UpdateLead(lead *leads.Lead) error {
|
||||||
|
return client.updateEntity("/api/v4/leads", lead.ID, lead)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) GetCompany(companyId string, query string) (*companies.Company, error) {
|
||||||
|
deal := new(companies.Company)
|
||||||
|
resource := fmt.Sprintf("/api/v4/companies/%s", companyId)
|
||||||
|
if len(query) != 0 {
|
||||||
|
resource = resource + "?" + query
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Api.Get(resource, deal)
|
||||||
|
return deal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) UpdateCompany(company *companies.Company) error {
|
||||||
|
return client.updateEntity("/api/v4/companies", company.ID, company)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) GetContact(contactId string, query string) (*contacts.Contact, error) {
|
||||||
|
deal := new(contacts.Contact)
|
||||||
|
resource := fmt.Sprintf("/api/v4/contacts/%s", contactId)
|
||||||
|
if len(query) != 0 {
|
||||||
|
resource = resource + "?" + query
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Api.Get(resource, deal)
|
||||||
|
return deal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) UpdateContact(contact *contacts.Contact) error {
|
||||||
|
return client.updateEntity("/api/v4/contacts", contact.ID, contact)
|
||||||
|
}
|
165
api/api.go
Normal file
165
api/api.go
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultContentType = "application/json"
|
||||||
|
DefaultAccept = DefaultContentType
|
||||||
|
DefaultCacheControl = "no-cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientOptions struct {
|
||||||
|
Url string
|
||||||
|
ClientId string
|
||||||
|
ClientSecret string
|
||||||
|
AccessToken string
|
||||||
|
RefreshToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
options *ClientOptions
|
||||||
|
BaseUrl *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
type requestOptions struct {
|
||||||
|
HttpMethod string
|
||||||
|
Body interface{}
|
||||||
|
Headers map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type OauthTokenResponse struct {
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(options *ClientOptions) (*Client, error) {
|
||||||
|
if len(options.AccessToken) == 0 || len(options.RefreshToken) == 0 || len(options.Url) == 0 {
|
||||||
|
return nil, errors.New("AmoCrm: Invalid options")
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedUrl, err := url.Parse(options.Url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
options: options,
|
||||||
|
BaseUrl: resolvedUrl,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *Client) doRequest(resourceUrl string, requestParams requestOptions, result interface{}) error {
|
||||||
|
resolvedUrl, err := url.Parse(resourceUrl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
requestUrl := api.BaseUrl.ResolveReference(resolvedUrl)
|
||||||
|
|
||||||
|
requestBody := new(bytes.Buffer)
|
||||||
|
if requestParams.Body != nil {
|
||||||
|
encoder := json.NewEncoder(requestBody)
|
||||||
|
if err := encoder.Encode(requestParams.Body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := http.NewRequest(requestParams.HttpMethod, requestUrl.String(), requestBody)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if requestParams.Headers != nil {
|
||||||
|
for k, v := range requestParams.Headers {
|
||||||
|
request.Header.Set(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := http.DefaultClient.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("Request error: %s %d %s %s", err.Error(), response.StatusCode, requestParams.HttpMethod, resourceUrl))
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
if response.StatusCode >= 400 {
|
||||||
|
bodyBytes, _ := ioutil.ReadAll(response.Body)
|
||||||
|
return errors.New(fmt.Sprintf("%s %d %s %s", string(bodyBytes), response.StatusCode, requestParams.HttpMethod, resourceUrl))
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
decoder := json.NewDecoder(response.Body)
|
||||||
|
err = decoder.Decode(result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *Client) RefreshToken() (*OauthTokenResponse, error) {
|
||||||
|
result := new(OauthTokenResponse)
|
||||||
|
request := map[string]string{
|
||||||
|
"client_id": api.options.ClientId,
|
||||||
|
"client_secret": api.options.ClientSecret,
|
||||||
|
"grant_type": "refresh_token",
|
||||||
|
"refresh_token": api.options.RefreshToken,
|
||||||
|
"redirect_uri": api.options.Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := api.doRequest("/oauth2/access_token", requestOptions{
|
||||||
|
HttpMethod: http.MethodPost,
|
||||||
|
Body: request,
|
||||||
|
Headers: getHeaders(""),
|
||||||
|
}, result)
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *Client) Get(resource string, result interface{}) error {
|
||||||
|
return api.doRequest(resource, requestOptions{
|
||||||
|
HttpMethod: http.MethodGet,
|
||||||
|
Body: nil,
|
||||||
|
Headers: getHeaders(api.options.AccessToken),
|
||||||
|
}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *Client) Post(resource string, request interface{}, result interface{}) error {
|
||||||
|
return api.doRequest(resource, requestOptions{
|
||||||
|
HttpMethod: http.MethodPost,
|
||||||
|
Body: request,
|
||||||
|
Headers: getHeaders(api.options.AccessToken),
|
||||||
|
}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *Client) Patch(resource string, request interface{}, result interface{}) error {
|
||||||
|
return api.doRequest(resource, requestOptions{
|
||||||
|
HttpMethod: http.MethodPatch,
|
||||||
|
Body: request,
|
||||||
|
Headers: getHeaders(api.options.AccessToken),
|
||||||
|
}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHeaders(token string) map[string]string {
|
||||||
|
headers := map[string]string{
|
||||||
|
"Accept": DefaultAccept,
|
||||||
|
"Cache-Control": DefaultCacheControl,
|
||||||
|
"Content-Type": DefaultContentType,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(token) > 0 {
|
||||||
|
headers["Authorization"] = fmt.Sprintf("Bearer %s", token)
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers
|
||||||
|
}
|
14
common/common.go
Normal file
14
common/common.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
type Values struct {
|
||||||
|
Value interface{} `json:"value,omitempty"`
|
||||||
|
EnumID int `json:"enum_id,omitempty"`
|
||||||
|
Enum string `json:"enum,omitempty"`
|
||||||
|
}
|
||||||
|
type CustomFieldsValue struct {
|
||||||
|
FieldID int `json:"field_id"`
|
||||||
|
FieldName string `json:"field_name,omitempty"`
|
||||||
|
FieldCode string `json:"field_code,omitempty"`
|
||||||
|
FieldType string `json:"field_type,omitempty"`
|
||||||
|
Values []Values `json:"values"`
|
||||||
|
}
|
36
companies/companies.go
Normal file
36
companies/companies.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package companies
|
||||||
|
|
||||||
|
import "vultras.su/core/amo/common"
|
||||||
|
|
||||||
|
type Company struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
ResponsibleUserID int `json:"responsible_user_id,omitempty"`
|
||||||
|
GroupID int `json:"group_id,omitempty"`
|
||||||
|
CreatedBy int `json:"created_by,omitempty"`
|
||||||
|
UpdatedBy int `json:"updated_by,omitempty"`
|
||||||
|
CreatedAt int `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt int `json:"updated_at,omitempty"`
|
||||||
|
ClosestTaskAt interface{} `json:"closest_task_at,omitempty"`
|
||||||
|
CustomFieldsValues []common.CustomFieldsValue `json:"custom_fields_values,omitempty"`
|
||||||
|
AccountID int `json:"account_id,omitempty"`
|
||||||
|
Links Links `json:"_links,omitempty"`
|
||||||
|
Embedded Embedded `json:"_embedded,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Self struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Links struct {
|
||||||
|
Self Self `json:"self"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Contacts struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Embedded struct {
|
||||||
|
Tags []interface{} `json:"tags"`
|
||||||
|
Contacts []*Contacts `json:"contacts"`
|
||||||
|
}
|
57
contacts/contacts.go
Normal file
57
contacts/contacts.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package contacts
|
||||||
|
|
||||||
|
import "vultras.su/core/amo/common"
|
||||||
|
|
||||||
|
type Contact struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
FirstName string `json:"first_name,omitempty"`
|
||||||
|
LastName string `json:"last_name,omitempty"`
|
||||||
|
ResponsibleUserID int `json:"responsible_user_id,omitempty"`
|
||||||
|
GroupID int `json:"group_id,omitempty"`
|
||||||
|
CreatedBy int `json:"created_by,omitempty"`
|
||||||
|
UpdatedBy int `json:"updated_by,omitempty"`
|
||||||
|
CreatedAt int `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt int `json:"updated_at,omitempty"`
|
||||||
|
ClosestTaskAt interface{} `json:"closest_task_at,omitempty"`
|
||||||
|
CustomFieldsValues []common.CustomFieldsValue `json:"custom_fields_values,omitempty"`
|
||||||
|
AccountID int `json:"account_id,omitempty"`
|
||||||
|
Links Links `json:"_links,omitempty"`
|
||||||
|
Embedded Embedded `json:"_embedded,omitempty"`
|
||||||
|
}
|
||||||
|
type Values struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
EnumID int `json:"enum_id"`
|
||||||
|
Enum string `json:"enum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Self struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Links struct {
|
||||||
|
Self Self `json:"self"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Leads struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Links Links `json:"_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Customers struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Links Links `json:"_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Companies struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Links Links `json:"_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Embedded struct {
|
||||||
|
Tags []interface{} `json:"tags"`
|
||||||
|
Leads []Leads `json:"leads"`
|
||||||
|
Customers []Customers `json:"customers"`
|
||||||
|
CatalogElements []interface{} `json:"catalog_elements"`
|
||||||
|
Companies []Companies `json:"companies"`
|
||||||
|
}
|
8
go.mod
Normal file
8
go.mod
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module vultras.su/core/amo
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gorilla/schema v1.2.0
|
||||||
|
github.com/stretchr/testify v1.6.1
|
||||||
|
)
|
13
go.sum
Normal file
13
go.sum
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||||
|
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
79
leads/leads.go
Normal file
79
leads/leads.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package leads
|
||||||
|
|
||||||
|
import "vultras.su/core/amo/common"
|
||||||
|
|
||||||
|
type Lead struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Price int `json:"price,omitempty"`
|
||||||
|
ResponsibleUserID int `json:"responsible_user_id,omitempty"`
|
||||||
|
GroupID int `json:"group_id,omitempty"`
|
||||||
|
StatusID int `json:"status_id,omitempty"`
|
||||||
|
PipelineID int `json:"pipeline_id,omitempty"`
|
||||||
|
LossReasonID int `json:"loss_reason_id,omitempty"`
|
||||||
|
SourceID interface{} `json:"source_id,omitempty"`
|
||||||
|
CreatedBy int `json:"created_by,omitempty"`
|
||||||
|
UpdatedBy int `json:"updated_by,omitempty"`
|
||||||
|
CreatedAt int `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt int `json:"updated_at,omitempty"`
|
||||||
|
ClosedAt int `json:"closed_at,omitempty"`
|
||||||
|
ClosestTaskAt interface{} `json:"closest_task_at,omitempty"`
|
||||||
|
IsDeleted bool `json:"is_deleted,omitempty"`
|
||||||
|
CustomFieldsValues []*common.CustomFieldsValue `json:"custom_fields_values,omitempty"`
|
||||||
|
Score interface{} `json:"score,omitempty"`
|
||||||
|
AccountID int `json:"account_id,omitempty"`
|
||||||
|
IsPriceModifiedByRobot bool `json:"is_price_modified_by_robot,omitempty"`
|
||||||
|
Links Links `json:"_links,omitempty"`
|
||||||
|
Embedded Embedded `json:"_embedded,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Self struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Links struct {
|
||||||
|
Self Self `json:"self"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tags struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Metadata struct {
|
||||||
|
Quantity int `json:"quantity"`
|
||||||
|
CatalogID int `json:"catalog_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CatalogElements struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Metadata Metadata `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LossReason struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Sort int `json:"sort"`
|
||||||
|
CreatedAt int `json:"created_at"`
|
||||||
|
UpdatedAt int `json:"updated_at"`
|
||||||
|
Links Links `json:"_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Companies struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Links Links `json:"_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Contacts struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
IsMain bool `json:"is_main"`
|
||||||
|
Links Links `json:"_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Embedded struct {
|
||||||
|
Tags []*Tags `json:"tags"`
|
||||||
|
CatalogElements []*CatalogElements `json:"catalog_elements"`
|
||||||
|
LossReason []*LossReason `json:"loss_reason"`
|
||||||
|
Companies []*Companies `json:"companies"`
|
||||||
|
Contacts []*Contacts `json:"contacts"`
|
||||||
|
}
|
22
license.txt
Normal file
22
license.txt
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright 2024 (c) surdeus
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
4
readme.md
Normal file
4
readme.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# amo
|
||||||
|
|
||||||
|
AmoCRM API implementation in Go.
|
||||||
|
|
10
users/users.go
Normal file
10
users/users.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package users
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Lang string `json:"lang"`
|
||||||
|
Rights interface{} `json:"rights"`
|
||||||
|
Links interface{} `json:"_links"`
|
||||||
|
}
|
48
webhooks/webhook.go
Normal file
48
webhooks/webhook.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package webhooks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"vultras.su/core/bond/urlenc"
|
||||||
|
"vultras.su/core/bond/jsons"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Leads Leads `schema:"leads"`
|
||||||
|
Account Account `schema:"account"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Leads struct {
|
||||||
|
Status jsons.ArrayMap[Status] `schema:"status"`
|
||||||
|
Add jsons.ArrayMap[Status] `schema:"add"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
Id jsons.Int `schema:"id"`
|
||||||
|
StatusId jsons.Int `schema:"status_id"`
|
||||||
|
PipelineId jsons.Int `schema:"pipeline_id"`
|
||||||
|
OldStatusId jsons.Int `schema:"old_status_id"`
|
||||||
|
OldPipelineId jsons.Int `schema:"old_pipeline_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
Id jsons.Int `schema:"id"`
|
||||||
|
SubDomain string `schema:"subdomain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFromString(body string) (*WebhookRequest, error) {
|
||||||
|
ret := &WebhookRequest{}
|
||||||
|
err := urlenc.Unmarsal(ret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *WebhookRequest) GetLeadId() string {
|
||||||
|
if hook.Leads.Status != nil {
|
||||||
|
return hook.Leads.Status[0].Id
|
||||||
|
}
|
||||||
|
return hook.Leads.Add[0].Id
|
||||||
|
}
|
77
webhooks/webhook_test.go
Normal file
77
webhooks/webhook_test.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package webhooks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Webhook_MoveIntoStage(t *testing.T) {
|
||||||
|
requestString := "leads[status][0][id]=2050297&" +
|
||||||
|
"leads[status][0][status_id]=35573056&" +
|
||||||
|
"leads[status][0][pipeline_id]=3643927&" +
|
||||||
|
"leads[status][0][old_status_id]=35572897&" +
|
||||||
|
"leads[status][0][old_pipeline_id]=3643927&" +
|
||||||
|
"account[id]=29085955&" +
|
||||||
|
"account[subdomain]=domain"
|
||||||
|
|
||||||
|
expected := &WebhookRequest{
|
||||||
|
Leads: Leads{
|
||||||
|
Status: []Status{
|
||||||
|
{
|
||||||
|
Id: "2050297",
|
||||||
|
StatusId: "35573056",
|
||||||
|
PipelineId: "3643927",
|
||||||
|
OldStatusId: "35572897",
|
||||||
|
OldPipelineId: "3643927",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Account: Account{
|
||||||
|
Id: "29085955",
|
||||||
|
SubDomain: "domain",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
webhook, err := NewFromString(requestString)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert.NotNil(t, webhook) {
|
||||||
|
assert.Equal(t, webhook, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Webhook_CreateIntoStage(t *testing.T) {
|
||||||
|
requestString := "leads[add][0][id]=2232929&" +
|
||||||
|
"leads[add][0][status_id]=35573056&" +
|
||||||
|
"leads[add][0][pipeline_id]=3643927&" +
|
||||||
|
"account[id]=29085955&account[subdomain]=domain"
|
||||||
|
|
||||||
|
expected := &WebhookRequest{
|
||||||
|
Leads: Leads{
|
||||||
|
Add: []Status{
|
||||||
|
{
|
||||||
|
Id: "2232929",
|
||||||
|
StatusId: "35573056",
|
||||||
|
PipelineId: "3643927",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Account: Account{
|
||||||
|
Id: "29085955",
|
||||||
|
SubDomain: "domain",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
webhook, err := NewFromString(requestString)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert.NotNil(t, webhook) {
|
||||||
|
assert.Equal(t, webhook, expected)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue