From b0739ad9188f836599c7cfd62085969832030486 Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Thu, 24 Mar 2022 17:07:39 +0300 Subject: [PATCH 1/4] Release http. --- .deadlines.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.deadlines.yml b/.deadlines.yml index e005962..9bae095 100644 --- a/.deadlines.yml +++ b/.deadlines.yml @@ -1,3 +1,18 @@ +- group: HTTP + start: 23-03-2022 18:00 + deadline: 10-04-2022 23:59 + tasks: + - task: urlshortener + score: 100 + - task: digitalclock + score: 100 + - task: coverme + score: 200 + - task: olympics + score: 200 + - task: firewall + score: 200 + - group: Concurrency with shared memory start: 17-03-2022 23:59 deadline: 03-04-2022 23:59 From 8868dac428d9d5611a458818a20ae369b7c42c0b Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Thu, 24 Mar 2022 18:48:48 +0300 Subject: [PATCH 2/4] [coverme] Add finish todo handler. --- coverme/README.md | 22 +++++++++++++++++++--- coverme/app/app.go | 27 +++++++++++++++++++++------ coverme/client/client.go | 16 ++++++++++++++++ coverme/models/storage.go | 14 ++++++++++++++ 4 files changed, 70 insertions(+), 9 deletions(-) diff --git a/coverme/README.md b/coverme/README.md index 0efcaa2..042c2f5 100644 --- a/coverme/README.md +++ b/coverme/README.md @@ -44,7 +44,7 @@ Todo-app с минимальной функциональностью + client. Health check: ``` -✗ curl -i -X GET localhost:6029/ +✗ curl -i -X GET localhost:6029/ HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 19 Mar 2020 21:46:02 GMT @@ -66,7 +66,7 @@ Content-Length: 51 Получить todo по id: ``` -✗ curl -i localhost:6029/todo/0 +✗ curl -i localhost:6029/todo/0 HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 19 Mar 2020 21:44:17 GMT @@ -77,7 +77,7 @@ Content-Length: 51 Получить все todo: ``` -✗ curl -i -X GET localhost:6029/todo +✗ curl -i -X GET localhost:6029/todo HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 19 Mar 2020 21:44:37 GMT @@ -85,3 +85,19 @@ Content-Length: 53 [{"id":0,"title":"A","content":"a","finished":false}] ``` + +Завершить todo: +``` +✗ curl -i -X POST localhost:6029/todo/0/finish +HTTP/1.1 200 OK +Date: Thu, 24 Mar 2022 15:40:49 GMT +Content-Length: 0 + +✗ curl -i -X GET localhost:6029/todo +HTTP/1.1 200 OK +Content-Type: application/json +Date: Thu, 24 Mar 2022 15:41:04 GMT +Content-Length: 52 + +[{"id":0,"title":"A","content":"a","finished":true}]% +``` diff --git a/coverme/app/app.go b/coverme/app/app.go index c9387cd..22d39b6 100644 --- a/coverme/app/app.go +++ b/coverme/app/app.go @@ -9,7 +9,6 @@ import ( "net/http" "os" "strconv" - "strings" "github.com/gorilla/handlers" "github.com/gorilla/mux" @@ -34,10 +33,11 @@ func (app *App) Start(port int) { func (app *App) initRoutes() { app.router = mux.NewRouter() - app.router.HandleFunc("/", app.status).Methods("Get") - app.router.HandleFunc("/todo", app.list).Methods("Get") - app.router.HandleFunc("/todo/{id:[0-9]+}", app.getTodo).Methods("Get") - app.router.HandleFunc("/todo/create", app.addTodo).Methods("Post") + app.router.HandleFunc("/", app.status).Methods("GET") + app.router.HandleFunc("/todo", app.list).Methods("GET") + app.router.HandleFunc("/todo/{id:[0-9]+}", app.getTodo).Methods("GET") + app.router.HandleFunc("/todo/{id:[0-9]+}/finish", app.finishTodo).Methods("POST") + app.router.HandleFunc("/todo/create", app.addTodo).Methods("POST") } func (app *App) run(addr string) { @@ -80,7 +80,7 @@ func (app *App) addTodo(w http.ResponseWriter, r *http.Request) { } func (app *App) getTodo(w http.ResponseWriter, r *http.Request) { - id, err := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/todo/")) + id, err := strconv.Atoi(mux.Vars(r)["id"]) if err != nil { utils.BadRequest(w, "ID must be an int") return @@ -95,6 +95,21 @@ func (app *App) getTodo(w http.ResponseWriter, r *http.Request) { _ = utils.RespondJSON(w, http.StatusOK, todo) } +func (app *App) finishTodo(w http.ResponseWriter, r *http.Request) { + id, err := strconv.Atoi(mux.Vars(r)["id"]) + if err != nil { + utils.BadRequest(w, "ID must be an int") + return + } + + if err := app.db.FinishTodo(models.ID(id)); err != nil { + utils.ServerError(w) + return + } + + w.WriteHeader(http.StatusOK) +} + func (app *App) status(w http.ResponseWriter, r *http.Request) { _ = utils.RespondJSON(w, http.StatusOK, "API is up and working!") } diff --git a/coverme/client/client.go b/coverme/client/client.go index cac5a08..b3bbe22 100644 --- a/coverme/client/client.go +++ b/coverme/client/client.go @@ -69,3 +69,19 @@ func (c *Client) List() ([]*models.Todo, error) { err = json.NewDecoder(resp.Body).Decode(&todos) return todos, err } + +func (c *Client) Finish(id models.ID) error { + u := fmt.Sprintf("%s/todo/%d/finish", c.addr, id) + + resp, err := http.Post(u, "application/json", nil) + if err != nil { + return err + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected status code %d", resp.StatusCode) + } + + return err +} diff --git a/coverme/models/storage.go b/coverme/models/storage.go index 07185ee..c4fc981 100644 --- a/coverme/models/storage.go +++ b/coverme/models/storage.go @@ -12,6 +12,7 @@ type Storage interface { AddTodo(string, string) (*Todo, error) GetTodo(ID) (*Todo, error) GetAll() ([]*Todo, error) + FinishTodo(ID) error } type InMemoryStorage struct { @@ -69,3 +70,16 @@ func (s *InMemoryStorage) GetAll() ([]*Todo, error) { return out, nil } + +func (s *InMemoryStorage) FinishTodo(id ID) error { + s.mu.Lock() + defer s.mu.Unlock() + + todo, ok := s.todos[id] + if !ok { + return fmt.Errorf("todo %d not found", id) + } + + todo.MarkFinished() + return nil +} From 52d82909561864807544b7d285c970a24c7534ac Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Thu, 24 Mar 2022 18:49:56 +0300 Subject: [PATCH 3/4] [coverme] Update readme. --- coverme/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/coverme/README.md b/coverme/README.md index 042c2f5..a4243e0 100644 --- a/coverme/README.md +++ b/coverme/README.md @@ -16,12 +16,18 @@ Package main можно не тестировать. Coverage 100% говорит ровно о том, что все строки кода выполнялись. Хорошие тесты, в первую очередь, тестируют функциональность. -Как посмотреть coverage: +Как посмотреть общий coverage: ``` go test -v -cover ./coverme/... ``` -Coverage можно выводить в html (см. ссылки), и эта функциональность поддерживается в Goland. +Как посмотреть coverage пакета в html: +``` +go test -v -coverprofile=/tmp/coverage.out ./coverme/models/... +go tool cover -html=/tmp/coverage.out +``` +Аналогичная функциональность поддерживается в Goland. +Также рекомендуем ознакомиться с рассказом о cover в блоге (см. ссылки). ## Ссылки From 1bdf9f18a5507c30477487d741f00d9cf09a39de Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Thu, 24 Mar 2022 18:51:08 +0300 Subject: [PATCH 4/4] Inc coverme points. --- .deadlines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.deadlines.yml b/.deadlines.yml index 9bae095..26539e0 100644 --- a/.deadlines.yml +++ b/.deadlines.yml @@ -6,12 +6,12 @@ score: 100 - task: digitalclock score: 100 - - task: coverme - score: 200 - task: olympics score: 200 - task: firewall score: 200 + - task: coverme + score: 300 - group: Concurrency with shared memory start: 17-03-2022 23:59