diff --git a/pkg/common/webclient/client.go b/pkg/common/webclient/client.go index 477b2744..5369c8e0 100644 --- a/pkg/common/webclient/client.go +++ b/pkg/common/webclient/client.go @@ -12,7 +12,7 @@ import ( type client struct { headers http.Header indent string - HttpClient http.Client + httpClient http.Client parse ParserFunc write WriterFunc } @@ -32,9 +32,14 @@ func (c *client) Headers() http.Header { return c.headers } +// HTTPClient returns the underlying http.WebClient used by the WebClient +func (c *client) HTTPClient() *http.Client { + return &c.httpClient +} + // Get fetches url using GET and unmarshals into the passed response func (c *client) Get(url string, response interface{}) error { - return c.request(url, response, nil) + return c.request(http.MethodGet, url, response, nil) } // Post sends a serialized representation of request and deserializes the result into response @@ -44,7 +49,7 @@ func (c *client) Post(url string, request interface{}, response interface{}) err return fmt.Errorf("error creating payload: %v", err) } - return c.request(url, response, bytes.NewReader(body)) + return c.request(http.MethodPost, url, response, bytes.NewReader(body)) } // ErrorResponse tries to deserialize any response body into the supplied struct, returning whether successful or not @@ -57,8 +62,8 @@ func (c *client) ErrorResponse(err error, response interface{}) bool { return c.parse([]byte(jerr.Body), response) == nil } -func (c *client) request(url string, response interface{}, payload io.Reader) error { - req, err := http.NewRequest(http.MethodPost, url, payload) +func (c *client) request(method, url string, response interface{}, payload io.Reader) error { + req, err := http.NewRequest(method, url, payload) if err != nil { return err } @@ -67,7 +72,7 @@ func (c *client) request(url string, response interface{}, payload io.Reader) er req.Header.Set(key, val[0]) } - res, err := c.HttpClient.Do(req) + res, err := c.httpClient.Do(req) if err != nil { return fmt.Errorf("error sending payload: %v", err) } diff --git a/pkg/common/webclient/interface.go b/pkg/common/webclient/interface.go index 954f9a32..86a95810 100644 --- a/pkg/common/webclient/interface.go +++ b/pkg/common/webclient/interface.go @@ -12,4 +12,5 @@ type WebClient interface { ErrorResponse(err error, response interface{}) bool SetParser(ParserFunc) SetWriter(WriterFunc) + HTTPClient() *http.Client } diff --git a/pkg/common/webclient/service.go b/pkg/common/webclient/service.go index 2b8d157a..2218e24c 100644 --- a/pkg/common/webclient/service.go +++ b/pkg/common/webclient/service.go @@ -26,7 +26,7 @@ type ClientService struct { // HTTPClient returns the underlying http.WebClient used in the Service func (s *ClientService) HTTPClient() *http.Client { s.Initialize() - return &s.client.HttpClient + return s.client.HTTPClient() } // WebClient returns the WebClient instance, initializing it if necessary @@ -42,7 +42,7 @@ func (s *ClientService) Initialize() { } s.client = &client{ - HttpClient: http.Client{ + httpClient: http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{}, }, @@ -66,7 +66,7 @@ func (s *ClientService) AddTrustedRootCertificate(caPEM []byte) bool { certPool = x509.NewCertPool() } s.certPool = certPool - if tp, ok := s.client.HttpClient.Transport.(*http.Transport); ok { + if tp, ok := s.client.httpClient.Transport.(*http.Transport); ok { tp.TLSClientConfig.RootCAs = s.certPool } } diff --git a/pkg/services/telegram/telegram.go b/pkg/services/telegram/telegram.go index d55c7054..e3f0b47d 100644 --- a/pkg/services/telegram/telegram.go +++ b/pkg/services/telegram/telegram.go @@ -54,7 +54,7 @@ func (service *Service) Initialize(configURL *url.URL, logger types.StdLogger) e } func (service *Service) sendMessageForChatIDs(message string, config *Config) error { - client := &Client{token: config.Token, WebClient: service.WebClient()} + client := &Client{Token: config.Token, WebClient: service.WebClient()} for _, chat := range service.config.Chats { payload := createSendMessagePayload(message, chat, config) if _, err := client.SendMessage(&payload); err != nil { diff --git a/pkg/services/telegram/telegram_client.go b/pkg/services/telegram/telegram_client.go index aed34457..b10a2669 100644 --- a/pkg/services/telegram/telegram_client.go +++ b/pkg/services/telegram/telegram_client.go @@ -8,11 +8,11 @@ import ( // Client for Telegram API type Client struct { WebClient webclient.WebClient - token string + Token string } func (c *Client) apiURL(endpoint string) string { - return fmt.Sprintf(apiFormat, c.token, endpoint) + return fmt.Sprintf(apiFormat, c.Token, endpoint) } // GetBotInfo returns the bot User info @@ -61,7 +61,7 @@ func (c *Client) SendMessage(message *SendMessagePayload) (*Message, error) { // GetErrorResponse retrieves the error message from a failed request func (c *Client) getErrorResponse(err error) error { - var errResponse *errorResponse + errResponse := &ErrorResponse{} if c.WebClient.ErrorResponse(err, errResponse) { return errResponse } else { diff --git a/pkg/services/telegram/telegram_client_test.go b/pkg/services/telegram/telegram_client_test.go new file mode 100644 index 00000000..ae78cae3 --- /dev/null +++ b/pkg/services/telegram/telegram_client_test.go @@ -0,0 +1,45 @@ +package telegram_test + +import ( + "github.com/containrrr/shoutrrr/pkg/common/webclient" + "github.com/containrrr/shoutrrr/pkg/services/telegram" + "github.com/jarcoal/httpmock" + "net/http" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var client *telegram.Client + +var _ = Describe("the telegram client", func() { + BeforeEach(func() { + client = &telegram.Client{WebClient: webclient.NewJSONClient(), Token: `Test`} + httpmock.ActivateNonDefault(client.WebClient.HTTPClient()) + }) + AfterEach(func() { + httpmock.DeactivateAndReset() + }) + When("an error is returned from the API", func() { + It("should return the error description", func() { + + errRes := httpmock.NewJsonResponderOrPanic(http.StatusNotAcceptable, telegram.ErrorResponse{ + OK: false, + Description: "no.", + }) + httpmock.RegisterResponder("POST", `https://api.telegram.org/botTest/getUpdates`, errRes) + httpmock.RegisterResponder("GET", `https://api.telegram.org/botTest/getMe`, errRes) + httpmock.RegisterResponder("POST", `https://api.telegram.org/botTest/sendMessage`, errRes) + + _, err := client.GetUpdates(0, 1, 10, []string{}) + Expect(err).To(MatchError(`no.`)) + + _, err = client.GetBotInfo() + Expect(err).To(MatchError(`no.`)) + + _, err = client.SendMessage(&telegram.SendMessagePayload{}) + Expect(err).To(MatchError(`no.`)) + }) + }) + +}) diff --git a/pkg/services/telegram/telegram_generator.go b/pkg/services/telegram/telegram_generator.go index 2b3e945f..7087c0ac 100644 --- a/pkg/services/telegram/telegram_generator.go +++ b/pkg/services/telegram/telegram_generator.go @@ -1,6 +1,7 @@ package telegram import ( + "github.com/containrrr/shoutrrr/pkg/common/webclient" f "github.com/containrrr/shoutrrr/pkg/format" "github.com/containrrr/shoutrrr/pkg/types" "github.com/containrrr/shoutrrr/pkg/util/generator" @@ -41,7 +42,7 @@ func (g *Generator) Generate(_ types.Service, props map[string]string, _ []strin ud.Writeln("Fetching bot info...") // ud.Writeln("Session token: %v", g.sessionToken) - g.client = &Client{token: token} + g.client = &Client{Token: token, WebClient: webclient.NewJSONClient()} botInfo, err := g.client.GetBotInfo() if err != nil { return &Config{}, err diff --git a/pkg/services/telegram/telegram_json.go b/pkg/services/telegram/telegram_json.go index a44930ed..c46482bd 100644 --- a/pkg/services/telegram/telegram_json.go +++ b/pkg/services/telegram/telegram_json.go @@ -41,13 +41,14 @@ func createSendMessagePayload(message string, channel string, config *Config) Se return payload } -type errorResponse struct { +// ErrorResponse is the generic response from the API when an error occurred +type ErrorResponse struct { OK bool `json:"ok"` ErrorCode int `json:"error_code"` Description string `json:"description"` } -func (e *errorResponse) Error() string { +func (e *ErrorResponse) Error() string { return e.Description }