Skip to content

Commit

Permalink
refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
a-kataev committed Apr 25, 2023
1 parent c008c5f commit 9d7b151
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 59 deletions.
109 changes: 66 additions & 43 deletions tg.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io"
"net/http"
"net/url"
"regexp"
"time"
)

Expand All @@ -29,7 +30,7 @@ const (
HTMLParseMode = "HTML"
)

var parseModeList = []ParseMode{ //nolint
var parseModeList = []ParseMode{ //nolint:gochecknoglobals
MarkdownV2ParseMode,
MarkdownParseMode,
HTMLParseMode,
Expand All @@ -42,64 +43,84 @@ var (
ErrModeUnknown = errors.New("unknown mode")
)

// User -
// User .
type User struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"` //nolint
FirstName string `json:"first_name"`
UserName string `json:"username,omitempty"`
}

// Chat -
// Chat .
type Chat struct {
ChatID int64 `json:"chat_id,omitempty"` //nolint
ChatID int64 `json:"chat_id,omitempty"`
Text string `json:"text,omitempty"`
ParseMode string `json:"parse_mode,omitempty"` //nolint
ParseMode string `json:"parse_mode,omitempty"`
}

// Message -
// Message .
type Message struct {
MessageID int `json:"message_id"` //nolint
MessageID int `json:"message_id"`
Date int `json:"date"`
}

// APIResponse -
// APIResponse .
type APIResponse struct {
Result interface{} `json:"result,omitempty"`
APIResponseError
}

// APIResponseError -
// APIResponseError .
type APIResponseError struct {
Ok bool `json:"ok"`
ErrorCode int `json:"error_code,omitempty"` //nolint
ErrorCode int `json:"error_code,omitempty"`
Description string `json:"description,omitempty"`
Parameters struct {
RetryAfter int `json:"retry_after,omitempty"` //nolint
RetryAfter int `json:"retry_after,omitempty"`
} `json:"parameters,omitempty"`
}

func (r APIResponseError) Error() string {
return r.Description
}

type Option func(t *TG) error
var regexpBotToken = regexp.MustCompile(`/bot([0-9]+):([a-zA-Z0-9]+)/`)

type redactError struct {
err error
}

func newRedactError(err error) error {
return &redactError{
err: err,
}
}

func (e *redactError) Error() string {
return regexpBotToken.ReplaceAllString(e.err.Error(), "/bot*****/")
}

func (e *redactError) Unwrap() error {
return e.err
}

type Option func(*TG) error

func APIServer(server string) Option {
return func(t *TG) error {
u, err := url.ParseRequestURI(server)
return func(tg *TG) error {
url, err := url.ParseRequestURI(server)
if err != nil {
return fmt.Errorf("APIServer: %w", err)
}

if u.Scheme != "http" && u.Scheme != "https" {
if url.Scheme != "http" && url.Scheme != "https" {
return fmt.Errorf("APIServer: %w", ErrInvalidScheme)
}

if u.Host == "" {
if url.Host == "" {
return fmt.Errorf("APIServer: %w", ErrEmptyHost)
}

t.endpoint = server
tg.endpoint = server

return nil
}
Expand Down Expand Up @@ -140,7 +161,7 @@ type httpClient interface {
Do(*http.Request) (*http.Response, error)
}

// TG -
// TG .
type TG struct {
http httpClient
endpoint string
Expand All @@ -149,34 +170,36 @@ type TG struct {

var _ tg = (*TG)(nil)

// NewTG -
//nolint:gochecknoglobals,gomnd
var defaultHTTPClient = &http.Client{
Timeout: 2 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 10 * time.Second,
},
}

// NewTG .
func NewTG(token string, options ...Option) (*TG, error) {
t := &TG{
tg := &TG{
http: nil,
endpoint: apiServer,
parseMode: string(MarkdownParseMode),
}

for _, opt := range options {
if err := opt(t); err != nil {
return nil, err
if err := opt(tg); err != nil {
return nil, fmt.Errorf("TG: %w", err)
}
}

if t.http == nil {
client := http.DefaultClient
client.Timeout = 2 * time.Second //nolint
client.Transport = &http.Transport{ //nolint
MaxIdleConns: 10, //nolint
IdleConnTimeout: 10 * time.Second, //nolint
}

t.http = client
if tg.http == nil {
tg.http = defaultHTTPClient
}

t.endpoint += "/bot" + token + "/"
tg.endpoint += "/bot" + token + "/"

return t, nil
return tg, nil
}

func (t *TG) makeMessage(chatID int64, text string) (io.Reader, error) {
Expand Down Expand Up @@ -224,48 +247,48 @@ func (t *TG) makeResponse(resp *http.Response, result interface{}) error {
return nil
}

// GetMe -
// GetMe .
func (t *TG) GetMe(ctx context.Context) (*User, error) {
req, err := t.makeRequest(ctx, apiMethodGetMe, nil)
if err != nil {
return nil, err
return nil, fmt.Errorf("GetMe: %w", err)
}

resp, err := t.http.Do(req)
if err != nil {
return nil, fmt.Errorf("GetMe: http: %w", err)
return nil, fmt.Errorf("GetMe: http: %w", newRedactError(err))
}

user := new(User)

if err := t.makeResponse(resp, user); err != nil {
return nil, err
return nil, fmt.Errorf("GetMe: %w", err)
}

return user, nil
}

// SendMessage -
// SendMessage .
func (t *TG) SendMessage(ctx context.Context, chatID int64, text string) (*Message, error) {
reader, err := t.makeMessage(chatID, text)
if err != nil {
return nil, err
return nil, fmt.Errorf("SendMessage: %w", err)
}

req, err := t.makeRequest(ctx, apiMethodSendMessage, reader)
if err != nil {
return nil, err
return nil, fmt.Errorf("SendMessage: %w", err)
}

resp, err := t.http.Do(req)
if err != nil {
return nil, fmt.Errorf("SendMessage: http: %w", err)
return nil, fmt.Errorf("SendMessage: http: %w", newRedactError(err))
}

message := new(Message)

if err := t.makeResponse(resp, message); err != nil {
return nil, err
return nil, fmt.Errorf("SendMessage: %w", err)
}

return message, nil
Expand Down
32 changes: 16 additions & 16 deletions tg_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//go:generate mockery --name httpClient --structname mockHTTPClient --inpackage --filename tg_mock_test.go

package tg
package tg //nolint:testpackage

import (
"bytes"
Expand Down Expand Up @@ -30,15 +30,15 @@ func Test_makeRequest(t *testing.T) {

t.Run("1", func(t *testing.T) {
t.Parallel()
request, err := testTG.makeRequest(nil, apiMethod(""), nil) //nolint
request, err := testTG.makeRequest(nil, apiMethod(""), nil) //nolint:staticcheck
assert.Nil(t, request)
assert.EqualError(t, err, "makeMessage: request: net/http: nil Context")
})

t.Run("2", func(t *testing.T) {
t.Parallel()
request, err := testTG.makeRequest(context.Background(), apiMethod(""), nil)
assert.IsType(t, &http.Request{}, request) //nolint
assert.IsType(t, &http.Request{}, request)
assert.Nil(t, err)
})
}
Expand All @@ -47,7 +47,7 @@ var errTest = errors.New("test")

type errReader struct{}

func (e *errReader) Read(p []byte) (int, error) {
func (e *errReader) Read(_ []byte) (int, error) {
return 0, errTest
}

Expand All @@ -59,7 +59,7 @@ func Test_makeResponse_BadReader(t *testing.T) {
testTG := newTestTG()

t.Parallel()
err := testTG.makeResponse(&http.Response{ //nolint
err := testTG.makeResponse(&http.Response{
Body: &errReader{},
}, nil)
assert.NotNil(t, err)
Expand Down Expand Up @@ -102,7 +102,7 @@ func Test_makeResponse_Cases(t *testing.T) {
test := func(tn int, table table) {
t.Run(strconv.Itoa(tn), func(t *testing.T) {
t.Parallel()
clientResponse := &http.Response{ //nolint
clientResponse := &http.Response{
Body: io.NopCloser(bytes.NewBuffer(table.responseBody)),
}

Expand Down Expand Up @@ -133,7 +133,7 @@ func Test_makeResponse_Update(t *testing.T) {
t.Parallel()

err := testTG.makeResponse(
&http.Response{ //nolint
&http.Response{
Body: io.NopCloser(bytes.NewBuffer([]byte(`{"ok":true,"result":{"id":1,"first_name":"test"}}`))),
}, updateUser)
assert.Nil(t, err)
Expand All @@ -146,7 +146,7 @@ func Test_makeResponse_OK(t *testing.T) {
t.Parallel()

err := testTG.makeResponse(
&http.Response{ //nolint
&http.Response{
Body: io.NopCloser(bytes.NewBuffer([]byte(`{"ok":true}`))),
}, nil)
assert.Nil(t, err)
Expand All @@ -158,15 +158,15 @@ func Test_GetMe(t *testing.T) {
t.Run("1", func(t *testing.T) {
t.Parallel()
testTG := newTestTG()
user, err := testTG.GetMe(nil) //nolint
assert.EqualError(t, err, "makeMessage: request: net/http: nil Context")
user, err := testTG.GetMe(nil) //nolint:staticcheck
assert.EqualError(t, err, "GetMe: makeMessage: request: net/http: nil Context")
assert.Nil(t, user)
})

t.Run("2", func(t *testing.T) {
t.Parallel()
testTG := newTestTG()
testHTTPClient := &mockHTTPClient{} //nolint
testHTTPClient := &mockHTTPClient{} //nolint:exhaustruct
testHTTPClient.On("Do", mock.Anything, mock.Anything).Return(nil, errTest)
testTG.http = testHTTPClient
user, err := testTG.GetMe(context.Background())
Expand All @@ -177,14 +177,14 @@ func Test_GetMe(t *testing.T) {
t.Run("3", func(t *testing.T) {
t.Parallel()
testTG := newTestTG()
testHTTPClient := &mockHTTPClient{} //nolint
testHTTPClient := &mockHTTPClient{} //nolint:exhaustruct
testHTTPClient.On("Do", mock.Anything, mock.Anything).Return(
&http.Response{ //nolint
&http.Response{
Body: &errReader{},
}, nil)
testTG.http = testHTTPClient
user, err := testTG.GetMe(context.Background())
assert.EqualError(t, err, "makeResponse: body: test")
assert.EqualError(t, err, "GetMe: makeResponse: body: test")
assert.Nil(t, user)
})

Expand All @@ -196,9 +196,9 @@ func Test_GetMe(t *testing.T) {
FirstName: "test",
UserName: "",
}
testHTTPClient := &mockHTTPClient{} //nolint
testHTTPClient := &mockHTTPClient{} //nolint:exhaustruct
testHTTPClient.On("Do", mock.Anything, mock.Anything).Return(
&http.Response{ //nolint
&http.Response{
Body: io.NopCloser(bytes.NewBuffer([]byte(`{"ok":true,"result":{"id":1,"first_name":"test"}}`))),
}, nil)
testTG.http = testHTTPClient
Expand Down

0 comments on commit 9d7b151

Please sign in to comment.