Skip to content

Commit

Permalink
feat: add rest client package (#8)
Browse files Browse the repository at this point in the history
* first commit

* decrease number of uploads and downloads in test

* add docker compose, to test the rest client in the CI workflow

* fix: url path

* feat: add e2etest(unit tests for rest-client packages)

* update Makefile

* update workflow
  • Loading branch information
DipandaAser authored Jul 27, 2022
1 parent 84e089d commit 7dbbbfe
Show file tree
Hide file tree
Showing 12 changed files with 506 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
.env.test
.env
*.env
14 changes: 10 additions & 4 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ jobs:
go-version: ${{ env.GO_VERSION }}
- name: Run Tests
run: |
export BOT_TOKEN=${{ secrets.CI_BOT_TOKEN }}
export BOT_TOKENS=${{ secrets.CI_BOT_TOKENS }}
export DRAFT_CHAT_ID=${{ secrets.CI_DRAFT_CHAT_ID }}
export CHAT_ID=${{ secrets.CI_CHAT_ID }}
(
echo BOT_TOKEN=${{ secrets.CI_BOT_TOKEN_1 }}
echo BOT_TOKENS=${{ secrets.CI_BOT_TOKEN_1 }},${{ secrets.CI_BOT_TOKEN_2 }},${{ secrets.CI_BOT_TOKEN_3 }}
echo TOKENS=${{ secrets.CI_BOT_TOKEN_1 }},${{ secrets.CI_BOT_TOKEN_2 }},${{ secrets.CI_BOT_TOKEN_3 }}
echo DRAFT_CHAT_ID=${{ secrets.CI_DRAFT_CHAT_ID }}
echo CHAT_ID=${{ secrets.CI_CHAT_ID }}
) > .env
cp .env .env.test
make test
chmod 777 ./scripts/wait-for-it/wait-for-it.sh
make docker-e2etest
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
.idea
.env.test
.env
*.env
21 changes: 20 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,32 @@ APPNAME=tg-bot-storage
## test: run tests on cmd and pkg files.
.PHONY: test
test: vet fmt
go test ./...
CI="false" go test ./...

## build: build application binary.
.PHONY: build
build:
go build -o $(APPNAME)

## run: run the api
.PHONY: run
run:
go run ./cmd/main.go

## e2etest-compose: run end to end tests in the docker-compose.test.yaml. Basically this is the test for the rest-client package
.PHONY: e2etest-compose
e2etest-compose:
cd ./pkg/rest-client/ && CI="true" go test -v -count=1 .

## e2etest: run end to end tests against local api
.PHONY: e2etest
e2etest:
cd ./pkg/rest-client/ && api_host="http://localhost:7000/" CI="true" go test -v -count=1 .

## docker-e2etest: run e2etests in a docker compose
docker-e2etest:
docker-compose -f docker-compose.test.yml up --abort-on-container-exit --exit-code-from e2etests

## docker-build: build the api docker image
.PHONY: docker-build
docker-build:
Expand Down
30 changes: 30 additions & 0 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: '3'

services:
api:
restart: always
build:
context: .
dockerfile: Dockerfile
env_file:
- ./.env
environment:
api_key: "12345"
ports:
- 7000:7000
extra_hosts:
- host.docker.internal:host-gateway

e2etests:
depends_on:
- api
image: golang:buster
command: /app/scripts/wait-for-it/wait-for-it.sh api:7000 -t 300 -- make -C /app e2etest-compose
env_file:
- ./.env
environment:
CI: "true"
api_host: "http://api:7000/"
api_key: "12345"
volumes:
- .:/app
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ require (
github.com/gin-gonic/gin v1.8.1 // indirect
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
github.com/joho/godotenv v1.4.0
github.com/stretchr/objx v0.4.0 // indirect
github.com/stretchr/testify v1.7.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
Expand Down Expand Up @@ -50,15 +51,20 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
Expand Down Expand Up @@ -95,3 +101,5 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2 changes: 1 addition & 1 deletion pkg/bot/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func Test_DownloadFileReader(t *testing.T) {
draftChatId, _ := strconv.ParseInt(os.Getenv(ENVDRAFTCHATID), 10, 64)
lock := sync.Mutex{}
count := 0
total := 100
total := 5
for i := 0; i < total; i++ {
wg.Add(1)
go func() {
Expand Down
6 changes: 3 additions & 3 deletions pkg/manager/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func Test_UploadFileReaderWithOneBot(t *testing.T) {
lock := sync.Mutex{}
chatId, _ := strconv.ParseInt(os.Getenv(ENVCHATID), 10, 64)
count := 0
total := 25
total := 5
for i := 0; i < total; i++ {
wg.Add(1)
go func(i int) {
Expand Down Expand Up @@ -75,7 +75,7 @@ func Test_UploadFileReaderWithMultipleBot(t *testing.T) {
lock := sync.Mutex{}
chatId, _ := strconv.ParseInt(os.Getenv(ENVCHATID), 10, 64)
count := 0
total := 60
total := 5
for i := 0; i < total; i++ {
wg.Add(1)
go func(i int) {
Expand Down Expand Up @@ -126,7 +126,7 @@ func Test_DownloadFileReader(t *testing.T) {
wg := sync.WaitGroup{}
lock := sync.Mutex{}
count := 0
total := 60
total := 5
for i := 0; i < total; i++ {
wg.Add(1)
go func() {
Expand Down
121 changes: 121 additions & 0 deletions pkg/rest-client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package rest_client

import (
"bytes"
"encoding/json"
"fmt"
v1 "github.com/DipandaAser/tg-bot-storage/pkg/models/v1"
"io"
"mime"
"net/http"
"net/url"
"strconv"
"strings"
)

type RestClient struct {
apiKey string
apiUrl string
client http.Client
}

type apiError struct {
Error string `json:"error"`
}

func NewRestClient(apiUrl, apiKey string) RestClient {
return RestClient{
apiKey: apiKey,
apiUrl: strings.TrimSuffix(apiUrl, "/"),
client: http.Client{},
}
}

func (rc *RestClient) getApiUrl() string {
return rc.apiUrl + "/api"
}

func (rc *RestClient) UploadFileReader(chatId int64, fileName string, fileReader io.Reader) (v1.MessageIdentifier, error) {
params := url.Values{}
params.Set("chat_id", fmt.Sprintf("%d", chatId))
params.Set("file_name", fileName)
params.Set("api-key", rc.apiKey)
var finalUrl = fmt.Sprintf("%s/%s?%s", rc.getApiUrl(), "files", params.Encode())
response, err := rc.client.Post(finalUrl, "application/octet-stream", fileReader)
if err != nil {
return v1.MessageIdentifier{}, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
var apiErr apiError
if err := json.NewDecoder(response.Body).Decode(&apiErr); err != nil {
return v1.MessageIdentifier{}, fmt.Errorf("could not upload file. status code: %d", response.StatusCode)
}
return v1.MessageIdentifier{}, fmt.Errorf("could not upload file. status code: %d, error: %s", response.StatusCode, apiErr.Error)
}
var fileIdentifier v1.MessageIdentifier
err = json.NewDecoder(response.Body).Decode(&fileIdentifier)
if err != nil {
return v1.MessageIdentifier{}, err
}
return fileIdentifier, nil
}

func (rc *RestClient) UploadFileBuffer(chatId int64, fileName string, fileData []byte) (v1.MessageIdentifier, error) {
reader := bytes.NewReader(fileData)
return rc.UploadFileReader(chatId, fileName, reader)
}

func (rc *RestClient) DownloadFileReader(identifier v1.MessageIdentifier, copyChat int64) (*v1.DownloadReaderResult, error) {
params := url.Values{}
params.Set("chat_id", fmt.Sprintf("%d", identifier.ChatId))
params.Set("msg_id", fmt.Sprintf("%d", identifier.MessageId))
params.Set("draft_chat_id", fmt.Sprintf("%d", copyChat))
params.Set("api-key", rc.apiKey)
var finalUrl = fmt.Sprintf("%s/%s?%s", rc.getApiUrl(), "files", params.Encode())
response, err := rc.client.Get(finalUrl)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
var apiErr apiError
if err := json.NewDecoder(response.Body).Decode(&apiErr); err != nil {
return nil, fmt.Errorf("could not upload file. status code: %d", response.StatusCode)
}
return nil, fmt.Errorf("could not upload file. status code: %d, error: %s", response.StatusCode, apiErr.Error)
}
// get the file size in header content-length and convert it to int64
var fileSize int64 = 0
if contentLength := response.Header.Get("Content-Length"); contentLength != "" {
fileSize, _ = strconv.ParseInt(contentLength, 10, 64)
}

_, contentDispParams, err := mime.ParseMediaType(response.Header.Get("Content-Disposition"))
return &v1.DownloadReaderResult{
Data: response.Body,
FileInfo: v1.FileInfo{
Size: fileSize,
Name: contentDispParams["filename"],
ContentType: response.Header.Get("Content-Type"),
},
}, nil
}

func (rc *RestClient) DownloadFileBuffer(identifier v1.MessageIdentifier, copyChat int64) (*v1.DownloadBufferResult, error) {
result, err := rc.DownloadFileReader(identifier, copyChat)
if err != nil {
return nil, err
}

defer result.Data.Close()

buf := new(bytes.Buffer)
if _, err := buf.ReadFrom(result.Data); err != nil {
return nil, err
}

return &v1.DownloadBufferResult{
Data: buf.Bytes(),
FileInfo: result.FileInfo,
}, nil
}
Loading

0 comments on commit 7dbbbfe

Please sign in to comment.