feat: better for simple authorization.

This commit is contained in:
Andrey Parhomenko 2024-01-15 03:48:01 +03:00
parent 5410765b3b
commit eaad1cbc06
6 changed files with 171 additions and 14 deletions

2
.gitignore vendored
View file

@ -1,3 +1,3 @@
*.exe *.exe
*.exe~ *.exe~
.env

View file

@ -19,19 +19,24 @@ type IAmoClient interface {
UpdateContact(contact *contacts.Contact) error UpdateContact(contact *contacts.Contact) error
} }
type TokenPair = api.TokenPair
type Options = api.ClientOptions
type Client struct { type Client struct {
Api *api.Client Api *api.Client
} }
func NewAmoClient(options *api.ClientOptions) (*Client, error) { type OauthTokenResponse = api.OauthTokenResponse
apiClient, err := api.NewClient(options)
func NewAmoClient(options *Options ) (*Client, *TokenPair, error) {
apiClient, pair, err := api.NewClient(options)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
return &Client{ return &Client{
Api: apiClient, Api: apiClient,
}, nil }, pair, nil
} }
func (client *Client) updateEntity(url string, id int, body interface{}) error { func (client *Client) updateEntity(url string, id int, body interface{}) error {
@ -78,7 +83,7 @@ func (client *Client) UpdateCompany(company *companies.Company) error {
func (client *Client) GetContact(contactId string, query string) (*contacts.Contact, error) { func (client *Client) GetContact(contactId string, query string) (*contacts.Contact, error) {
deal := new(contacts.Contact) deal := new(contacts.Contact)
resource := fmt.Sprintf("/api/v4/contacts/%s", contactId) resource := fmt.Sprintf("/api/v4/contacts/%s", contactId)
if len(query) != 0 { if query != "" {
resource = resource + "?" + query resource = resource + "?" + query
} }

View file

@ -18,8 +18,13 @@ const (
type ClientOptions struct { type ClientOptions struct {
Url string Url string
RedirectUrl string
AuthCode string
ClientId string ClientId string
ClientSecret string ClientSecret string
AccessToken string AccessToken string
RefreshToken string RefreshToken string
} }
@ -42,20 +47,37 @@ type OauthTokenResponse struct {
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
} }
func NewClient(options *ClientOptions) (*Client, error) { type TokenPair struct {
if len(options.AccessToken) == 0 || len(options.RefreshToken) == 0 || len(options.Url) == 0 { Access, Refresh string
return nil, errors.New("AmoCrm: Invalid options")
} }
func NewClient(options *ClientOptions) (*Client, *TokenPair, error) {
if options.Url == "" {
return nil, nil, errors.New("AmoCrm: Invalid options: Url")
}
resolvedUrl, err := url.Parse(options.Url) resolvedUrl, err := url.Parse(options.Url)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
ret := &Client{
options: options,
}
ret.BaseUrl = resolvedUrl
var pair *TokenPair
if options.AccessToken == "" || options.RefreshToken == "" {
pair, err = ret.ExchangeAuth()
if err != nil {
return nil, nil, err
}
}
return &Client{ return &Client{
options: options, options: options,
BaseUrl: resolvedUrl, BaseUrl: resolvedUrl,
}, nil }, pair, nil
} }
func (api *Client) doRequest(resourceUrl string, requestParams requestOptions, result interface{}) error { func (api *Client) doRequest(resourceUrl string, requestParams requestOptions, result interface{}) error {
@ -87,13 +109,25 @@ func (api *Client) doRequest(resourceUrl string, requestParams requestOptions, r
response, err := http.DefaultClient.Do(request) response, err := http.DefaultClient.Do(request)
if err != nil { if err != nil {
return errors.New(fmt.Sprintf("Request error: %s %d %s %s", err.Error(), response.StatusCode, requestParams.HttpMethod, resourceUrl)) return errors.New(fmt.Sprintf(
"Request error: %s %d %s %s",
err.Error(),
response.StatusCode,
requestParams.HttpMethod,
resourceUrl,
))
} }
defer response.Body.Close() defer response.Body.Close()
if response.StatusCode >= 400 { if response.StatusCode >= 400 {
bodyBytes, _ := ioutil.ReadAll(response.Body) bodyBytes, _ := ioutil.ReadAll(response.Body)
return errors.New(fmt.Sprintf("%s %d %s %s", string(bodyBytes), response.StatusCode, requestParams.HttpMethod, resourceUrl)) return errors.New(fmt.Sprintf(
"%s %d %s %s",
string(bodyBytes),
response.StatusCode,
requestParams.HttpMethod,
resourceUrl,
))
} }
if result != nil { if result != nil {
@ -107,6 +141,36 @@ func (api *Client) doRequest(resourceUrl string, requestParams requestOptions, r
return nil return nil
} }
func (api *Client) ExchangeAuth() (*TokenPair, error) {
result := &OauthTokenResponse{}
request := map[string] string {
"client_id": api.options.ClientId,
"client_secret": api.options.ClientSecret,
"grant_type": "authorization_code",
"code": api.options.AuthCode,
"redirect_uri": api.options.RedirectUrl,
}
err := api.doRequest("/oauth2/access_token", requestOptions{
HttpMethod: http.MethodPost,
Body: request,
Headers: getHeaders(""),
}, result)
if err != nil {
return nil, err
}
ret := &TokenPair{
Access: result.AccessToken,
Refresh: result.RefreshToken,
}
api.options.AccessToken = result.AccessToken
api.options.RefreshToken = result.RefreshToken
return ret, nil
}
func (api *Client) RefreshToken() (*OauthTokenResponse, error) { func (api *Client) RefreshToken() (*OauthTokenResponse, error) {
result := new(OauthTokenResponse) result := new(OauthTokenResponse)
request := map[string]string{ request := map[string]string{

77
cmd/test/main.go Normal file
View file

@ -0,0 +1,77 @@
package main
import (
"vultras.su/core/amo"
"vultras.su/core/bond"
"vultras.su/core/bond/statuses"
"os"
"fmt"
"io"
)
type Context = bond.Context
var root = bond.Root(bond.Path().
Def(
"hook",
bond.Func(func(c *Context){
v := map[string]any{}
c.Scan(&v)
if c.ScanErr() != nil {
fmt.Println("scan-err:", c.ScanErr())
}
fmt.Println("request:", v)
c.SetStatus(statuses.OK)
}),
).Def(
"auth",
bond.Func(func(c *Context){
fmt.Println("header:", c.R.Header)
fmt.Println("content-type:", c.ContentType())
bts, _ := io.ReadAll(c.R.Body)
fmt.Println("request:", string(bts))
c.SetStatus(statuses.OK)
return
v := map[string]any{}
c.Scan(&v)
if c.ScanErr() != nil {
fmt.Println("scan-err:", c.ScanErr())
}
fmt.Println("request:", v)
c.SetStatus(statuses.OK)
}),
))
func main() {
srv := bond.Server{
Addr: ":15080",
Handler: root,
}
go srv.ListenAndServe()
opts := &amo.Options{
Url: os.Getenv("AMO_URL"),
RedirectUrl: os.Getenv("AMO_REDIRECT_URL"),
AccessToken: os.Getenv("AMO_ACCESS"),
RefreshToken: os.Getenv("AMO_REFRESH"),
ClientSecret: os.Getenv("AMO_CLIENT_SECRET"),
ClientId: os.Getenv("AMO_CLIENT_ID"),
AuthCode: os.Getenv("AMO_AUTH_CODE"),
}
fmt.Println(opts)
client, pair, err := amo.NewAmoClient(opts)
if err != nil {
panic(err)
}
if pair != nil {
fmt.Println("%q", pair)
}
company, err := client.GetCompany("80699047", "")
if err != nil {
panic(err)
}
fmt.Println(company)
}

5
env.sh Normal file
View file

@ -0,0 +1,5 @@
lines=$(cat .env)
for line in $lines ; do
echo $line
eval "export $line"
done

6
taskfile.yml Normal file
View file

@ -0,0 +1,6 @@
version: 3
tasks:
btest:
cmds:
- go build ./cmd/test