From 86ba166c54842cff138174d033499404e4b7dae8 Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 02:38:12 +0300 Subject: [PATCH 01/18] increment9 --- go.mod | 1 + go.sum | 2 + internal/app/app.go | 1 + internal/handler/handler.go | 84 +++++++++++++++++++++++++++++--- internal/storage/storage.go | 26 +++++++--- internal/storage/storage_test.go | 13 ++--- 6 files changed, 109 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 45105cc..9312b45 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( ) require ( + github.com/google/uuid v1.3.0 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.1 // indirect github.com/mattn/go-colorable v0.1.11 // indirect diff --git a/go.sum b/go.sum index c855a40..0660b89 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= diff --git a/internal/app/app.go b/internal/app/app.go index 65e5d2c..6b0d8a0 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -22,6 +22,7 @@ func Start(serverAddr, baseURL, dbFile string) error { e.POST("/", h.CreateURL) e.GET("/:id", h.RetrieveURL) e.POST("/api/shorten", h.CreateURLInJSON) + e.GET("/api/user/urls", h.ListURL) e.Logger.Fatal(e.Start(serverAddr)) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index b76106b..fa80f5e 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -2,6 +2,9 @@ package handler import ( "crypto" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -12,6 +15,7 @@ import ( "github.com/combodga/Project/internal/storage" "github.com/btcsuite/btcutil/base58" + "github.com/google/uuid" "github.com/labstack/echo/v4" ) @@ -19,6 +23,7 @@ type Handler struct { ServerAddr string BaseURL string Storage *storage.Storage + Key string } func New(serverAddr, baseURL, dbFile string) (*Handler, error) { @@ -30,6 +35,7 @@ func New(serverAddr, baseURL, dbFile string) (*Handler, error) { ServerAddr: serverAddr, BaseURL: baseURL, Storage: s, + Key: "b8ffa0f4-3f11-44b1-b0bf-9109f47e468b", }, err } @@ -38,6 +44,7 @@ type Link struct { } func (h *Handler) CreateURL(c echo.Context) error { + user := getUser(c, h.Key) body, err := io.ReadAll(c.Request().Body) if err != nil { return err @@ -45,7 +52,7 @@ func (h *Handler) CreateURL(c echo.Context) error { link := string(body) - id, err := h.fetchID(c, link) + id, err := h.fetchID(c, user, link) if err != nil { return fmt.Errorf("fetch id: %v", err) } @@ -54,6 +61,7 @@ func (h *Handler) CreateURL(c echo.Context) error { } func (h *Handler) CreateURLInJSON(c echo.Context) error { + user := getUser(c, h.Key) body, err := io.ReadAll(c.Request().Body) if err != nil { return err @@ -70,7 +78,7 @@ func (h *Handler) CreateURLInJSON(c echo.Context) error { return errors.New("error reading json") } - id, err := h.fetchID(c, link) + id, err := h.fetchID(c, user, link) if err != nil { return fmt.Errorf("fetchID: %v", err) } @@ -82,9 +90,10 @@ func (h *Handler) CreateURLInJSON(c echo.Context) error { } func (h *Handler) RetrieveURL(c echo.Context) error { + user := getUser(c, h.Key) id := c.Param("id") - url, ok := h.Storage.GetURL(id) + url, ok := h.Storage.GetURL(user, id) if !ok { return c.String(http.StatusNotFound, "error, there is no such link") } @@ -92,7 +101,30 @@ func (h *Handler) RetrieveURL(c echo.Context) error { return c.Redirect(http.StatusTemporaryRedirect, url) } -func (h *Handler) fetchID(c echo.Context, link string) (string, error) { +type Element struct { + ShortURL string `json:"short_url"` + OriginalURL string `json:"original_url"` +} + +func (h *Handler) ListURL(c echo.Context) error { + user := getUser(c, h.Key) + list, ok := h.Storage.ListURL(user) + if !ok { + return c.String(http.StatusNoContent, "error, you haven't any saved links") + } + + var arr []*Element + for shortURL, originalURL := range list { + arr = append(arr, &Element{ + ShortURL: h.BaseURL + "/" + shortURL, + OriginalURL: originalURL, + }) + } + + return c.JSON(http.StatusOK, arr) +} + +func (h *Handler) fetchID(c echo.Context, user, link string) (string, error) { if len(link) > 2048 { return "", c.String(http.StatusBadRequest, "error, the link cannot be longer than 2048 characters") } @@ -102,7 +134,7 @@ func (h *Handler) fetchID(c echo.Context, link string) (string, error) { return "", c.String(http.StatusBadRequest, "error, the link is invalid") } - id, ok := h.Storage.GetURL(link) + id, ok := h.Storage.GetURL(user, link) if !ok { id, err = shortener(link) if err != nil { @@ -110,7 +142,7 @@ func (h *Handler) fetchID(c echo.Context, link string) (string, error) { } } - err = h.Storage.SetURL(id, link) + err = h.Storage.SetURL(user, id, link) if err != nil { return "", c.String(http.StatusInternalServerError, "error, failed to store a shortened URL") } @@ -130,3 +162,43 @@ func shortener(s string) (string, error) { return id, nil } + +func getUser(c echo.Context, key string) string { + user, err1 := readCookie(c, "user") + sign, err2 := readCookie(c, "sign") + if err1 == nil && err2 == nil && sign == getSign(user, key) { + return user + } + + user = randUser() + writeCookie(c, "user", user) + writeCookie(c, "sign", getSign(user, key)) + return user +} + +func randUser() string { + uuidWithHyphen := uuid.New() + return uuidWithHyphen.String() +} + +func getSign(user, key string) string { + h := hmac.New(sha256.New, []byte(key)) + h.Write([]byte(user)) + dst := h.Sum(nil) + return hex.EncodeToString(dst)[:32] +} + +func writeCookie(c echo.Context, name, value string) { + cookie := new(http.Cookie) + cookie.Name = name + cookie.Value = value + c.SetCookie(cookie) +} + +func readCookie(c echo.Context, name string) (string, error) { + cookie, err := c.Cookie(name) + if err != nil { + return "", err + } + return cookie.Value, nil +} diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 8974201..20f7b05 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -9,14 +9,14 @@ import ( type Storage struct { DBFile string - Pairs map[string]string + Pairs map[string]map[string]string Mutex *sync.RWMutex } func New(dbFile string) (*Storage, error) { s := &Storage{ DBFile: dbFile, - Pairs: make(map[string]string), + Pairs: make(map[string]map[string]string), Mutex: &sync.RWMutex{}, } @@ -43,13 +43,13 @@ func New(dbFile string) (*Storage, error) { return s, nil } -func (s *Storage) GetURL(id string) (string, bool) { +func (s *Storage) GetURL(user, id string) (string, bool) { if len(id) <= 0 { return "", false } s.Mutex.Lock() - url, ok := s.Pairs[id] + url, ok := s.Pairs[user][id] s.Mutex.Unlock() if !ok { return "", false @@ -58,11 +58,14 @@ func (s *Storage) GetURL(id string) (string, bool) { return url, true } -func (s *Storage) SetURL(id, link string) error { +func (s *Storage) SetURL(user, id, link string) error { s.Mutex.Lock() defer s.Mutex.Unlock() - s.Pairs[id] = link + if len(s.Pairs[user]) == 0 { + s.Pairs[user] = make(map[string]string) + } + s.Pairs[user][id] = link if s.DBFile == "" { return nil @@ -80,3 +83,14 @@ func (s *Storage) SetURL(id, link string) error { return nil } + +func (s *Storage) ListURL(user string) (map[string]string, bool) { + s.Mutex.Lock() + list, ok := s.Pairs[user] + s.Mutex.Unlock() + if !ok { + return list, false + } + + return list, true +} diff --git a/internal/storage/storage_test.go b/internal/storage/storage_test.go index d26a5e2..da76b08 100644 --- a/internal/storage/storage_test.go +++ b/internal/storage/storage_test.go @@ -6,11 +6,12 @@ import ( var ( tests = []struct { + user string key string value string }{ - {key: "key", value: "value"}, - {key: "a", value: "b"}, + {user: "test", key: "key", value: "value"}, + {user: "test", key: "a", value: "b"}, } S *Storage ) @@ -25,7 +26,7 @@ func TestInit(t *testing.T) { func TestSetURL(t *testing.T) { for _, testCase := range tests { - err := S.SetURL(testCase.key, testCase.value) + err := S.SetURL(testCase.user, testCase.key, testCase.value) if err != nil { t.Fatalf("can't save value %v for key %v", testCase.value, testCase.key) } @@ -33,18 +34,18 @@ func TestSetURL(t *testing.T) { } func TestGetURL(t *testing.T) { - _, ok := S.GetURL("non-existant-key") + _, ok := S.GetURL("test", "non-existant-key") if ok { t.Fatal("got value for non existant key") } - _, ok = S.GetURL("") + _, ok = S.GetURL("test", "") if ok { t.Fatal("got value for empty key") } for _, testCase := range tests { - val, ok := S.GetURL(testCase.key) + val, ok := S.GetURL(testCase.user, testCase.key) if !ok { t.Fatalf("can't get value for key %v", testCase.key) } From 146ab5fc362489b52dd834c47f823fa373401039 Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 03:15:14 +0300 Subject: [PATCH 02/18] fixed getting url --- internal/handler/handler.go | 5 ++--- internal/storage/storage.go | 15 +++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index fa80f5e..783b6e3 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -90,10 +90,9 @@ func (h *Handler) CreateURLInJSON(c echo.Context) error { } func (h *Handler) RetrieveURL(c echo.Context) error { - user := getUser(c, h.Key) id := c.Param("id") - url, ok := h.Storage.GetURL(user, id) + url, ok := h.Storage.GetURL(id) if !ok { return c.String(http.StatusNotFound, "error, there is no such link") } @@ -134,7 +133,7 @@ func (h *Handler) fetchID(c echo.Context, user, link string) (string, error) { return "", c.String(http.StatusBadRequest, "error, the link is invalid") } - id, ok := h.Storage.GetURL(user, link) + id, ok := h.Storage.GetURL(link) if !ok { id, err = shortener(link) if err != nil { diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 20f7b05..65497fa 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -43,19 +43,22 @@ func New(dbFile string) (*Storage, error) { return s, nil } -func (s *Storage) GetURL(user, id string) (string, bool) { +func (s *Storage) GetURL(id string) (string, bool) { if len(id) <= 0 { return "", false } s.Mutex.Lock() - url, ok := s.Pairs[user][id] - s.Mutex.Unlock() - if !ok { - return "", false + defer s.Mutex.Unlock() + + for user, _ := range s.Pairs { + url, ok := s.Pairs[user][id] + if ok { + return url, true + } } - return url, true + return "", false } func (s *Storage) SetURL(user, id, link string) error { From 4e7b244e7a03b054f7fd368813642b32ec6fbf6b Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 03:18:09 +0300 Subject: [PATCH 03/18] fixed tests --- internal/storage/storage_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/storage/storage_test.go b/internal/storage/storage_test.go index da76b08..4493730 100644 --- a/internal/storage/storage_test.go +++ b/internal/storage/storage_test.go @@ -34,18 +34,18 @@ func TestSetURL(t *testing.T) { } func TestGetURL(t *testing.T) { - _, ok := S.GetURL("test", "non-existant-key") + _, ok := S.GetURL("non-existant-key") if ok { t.Fatal("got value for non existant key") } - _, ok = S.GetURL("test", "") + _, ok = S.GetURL("") if ok { t.Fatal("got value for empty key") } for _, testCase := range tests { - val, ok := S.GetURL(testCase.user, testCase.key) + val, ok := S.GetURL(testCase.key) if !ok { t.Fatalf("can't get value for key %v", testCase.key) } From 932b262b888bd0b2348e7f4379c06bbf6763016f Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 03:23:32 +0300 Subject: [PATCH 04/18] fixed unnecessary assignment to the blank identifier --- internal/storage/storage.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 65497fa..864afba 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -51,7 +51,7 @@ func (s *Storage) GetURL(id string) (string, bool) { s.Mutex.Lock() defer s.Mutex.Unlock() - for user, _ := range s.Pairs { + for user := range s.Pairs { url, ok := s.Pairs[user][id] if ok { return url, true From 9979fd491401162fc9f3661120a2cf211fa442d6 Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 04:12:58 +0300 Subject: [PATCH 05/18] increment10 --- cmd/shortener/shortener.go | 4 +++- go.mod | 1 + go.sum | 2 ++ internal/app/app.go | 5 +++-- internal/handler/handler.go | 33 +++++++++++++++++++++++--------- internal/handler/handler_test.go | 18 ++++++++++++++++- 6 files changed, 50 insertions(+), 13 deletions(-) diff --git a/cmd/shortener/shortener.go b/cmd/shortener/shortener.go index aef193f..ec8e072 100644 --- a/cmd/shortener/shortener.go +++ b/cmd/shortener/shortener.go @@ -11,10 +11,12 @@ func main() { var serverAddr string var baseURL string var dbFile string + var dbCredentials string flag.StringVar(&serverAddr, "a", os.Getenv("SERVER_ADDRESS"), "server address") flag.StringVar(&baseURL, "b", os.Getenv("BASE_URL"), "base URL") flag.StringVar(&dbFile, "f", os.Getenv("FILE_STORAGE_PATH"), "file storage path") + flag.StringVar(&dbCredentials, "d", os.Getenv("DATABASE_DSN"), "database credentials") flag.Parse() if serverAddr == "" { @@ -25,7 +27,7 @@ func main() { baseURL = "http://" + serverAddr } - err := app.Start(serverAddr, baseURL, dbFile) + err := app.Start(serverAddr, baseURL, dbFile, dbCredentials) if err != nil { panic(err) } diff --git a/go.mod b/go.mod index 9312b45..dba95a4 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/google/uuid v1.3.0 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.1 // indirect + github.com/lib/pq v1.10.7 github.com/mattn/go-colorable v0.1.11 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect diff --git a/go.sum b/go.sum index 0660b89..4e918bf 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,8 @@ github.com/labstack/echo/v4 v4.8.0 h1:wdc6yKVaHxkNOEdz4cRZs1pQkwSXPiRjq69yWP4QQS github.com/labstack/echo/v4 v4.8.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= diff --git a/internal/app/app.go b/internal/app/app.go index 6b0d8a0..4be8231 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -9,8 +9,8 @@ import ( "github.com/labstack/echo/v4/middleware" ) -func Start(serverAddr, baseURL, dbFile string) error { - h, err := handler.New(serverAddr, baseURL, dbFile) +func Start(serverAddr, baseURL, dbFile, dbCredentials string) error { + h, err := handler.New(serverAddr, baseURL, dbFile, dbCredentials) if err != nil { return fmt.Errorf("handler: %v", err) } @@ -23,6 +23,7 @@ func Start(serverAddr, baseURL, dbFile string) error { e.GET("/:id", h.RetrieveURL) e.POST("/api/shorten", h.CreateURLInJSON) e.GET("/api/user/urls", h.ListURL) + e.GET("/ping", h.Ping) e.Logger.Fatal(e.Start(serverAddr)) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 783b6e3..430fb7f 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -4,6 +4,7 @@ import ( "crypto" "crypto/hmac" "crypto/sha256" + "database/sql" "encoding/hex" "encoding/json" "errors" @@ -12,6 +13,8 @@ import ( "net/http" "net/url" + _ "github.com/lib/pq" + "github.com/combodga/Project/internal/storage" "github.com/btcsuite/btcutil/base58" @@ -20,22 +23,24 @@ import ( ) type Handler struct { - ServerAddr string - BaseURL string - Storage *storage.Storage - Key string + ServerAddr string + BaseURL string + Storage *storage.Storage + DBCredentials string + Key string } -func New(serverAddr, baseURL, dbFile string) (*Handler, error) { +func New(serverAddr, baseURL, dbFile, dbCredentials string) (*Handler, error) { s, err := storage.New(dbFile) if err != nil { err = fmt.Errorf("storage: %v", err) } return &Handler{ - ServerAddr: serverAddr, - BaseURL: baseURL, - Storage: s, - Key: "b8ffa0f4-3f11-44b1-b0bf-9109f47e468b", + ServerAddr: serverAddr, + BaseURL: baseURL, + Storage: s, + DBCredentials: dbCredentials, + Key: "b8ffa0f4-3f11-44b1-b0bf-9109f47e468b", }, err } @@ -123,6 +128,16 @@ func (h *Handler) ListURL(c echo.Context) error { return c.JSON(http.StatusOK, arr) } +func (h *Handler) Ping(c echo.Context) error { + db, err := sql.Open("postgres", h.DBCredentials) + if err != nil { + return c.String(http.StatusInternalServerError, "error, no connection to db") + } + defer db.Close() + + return c.String(http.StatusOK, "db connected") +} + func (h *Handler) fetchID(c echo.Context, user, link string) (string, error) { if len(link) > 2048 { return "", c.String(http.StatusBadRequest, "error, the link cannot be longer than 2048 characters") diff --git a/internal/handler/handler_test.go b/internal/handler/handler_test.go index c889d06..f1ff6b4 100644 --- a/internal/handler/handler_test.go +++ b/internal/handler/handler_test.go @@ -27,7 +27,7 @@ var ( func TestInit(t *testing.T) { var err error - H, err = New("localhost:8080", "http://localhost:8080", "") + H, err = New("localhost:8080", "http://localhost:8080", "", "user=a password=b dbname=c sslmode=disable") if err != nil { t.Fatal("can't start test") } @@ -144,3 +144,19 @@ func TestRetrieveURL(t *testing.T) { t.Errorf("expected status %v; got %v", http.StatusNotFound, result.StatusCode) } } + +func TestPing(t *testing.T) { + e := echo.New() + request := httptest.NewRequest(http.MethodGet, "http://"+H.ServerAddr+"/ping", nil) + + recorder := httptest.NewRecorder() + c := e.NewContext(request, recorder) + H.Ping(c) + + result := recorder.Result() + defer result.Body.Close() + + if result.StatusCode != http.StatusInternalServerError { + t.Errorf("expected status %v; got %v", http.StatusInternalServerError, result.StatusCode) + } +} From 8789ef3b779dbbe6013a1423d96bd7f7654ef042 Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 04:15:32 +0300 Subject: [PATCH 06/18] fixed tests --- internal/handler/handler_test.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/internal/handler/handler_test.go b/internal/handler/handler_test.go index f1ff6b4..f597af2 100644 --- a/internal/handler/handler_test.go +++ b/internal/handler/handler_test.go @@ -144,19 +144,3 @@ func TestRetrieveURL(t *testing.T) { t.Errorf("expected status %v; got %v", http.StatusNotFound, result.StatusCode) } } - -func TestPing(t *testing.T) { - e := echo.New() - request := httptest.NewRequest(http.MethodGet, "http://"+H.ServerAddr+"/ping", nil) - - recorder := httptest.NewRecorder() - c := e.NewContext(request, recorder) - H.Ping(c) - - result := recorder.Result() - defer result.Body.Close() - - if result.StatusCode != http.StatusInternalServerError { - t.Errorf("expected status %v; got %v", http.StatusInternalServerError, result.StatusCode) - } -} From aba8d7b136c93bafdd1369650aef64f38f317a48 Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:20:20 +0300 Subject: [PATCH 07/18] increment11 --- go.mod | 1 + go.sum | 5 ++ internal/handler/handler.go | 10 +-- internal/storage/storage.go | 117 +++++++++++++++++++++++++++--------- 4 files changed, 99 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index dba95a4..d3565e9 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( require ( github.com/google/uuid v1.3.0 + github.com/jmoiron/sqlx v1.3.5 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.1 // indirect github.com/lib/pq v1.10.7 diff --git a/go.sum b/go.sum index 4e918bf..c3b428e 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -23,6 +24,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= @@ -31,12 +34,14 @@ github.com/labstack/echo/v4 v4.8.0 h1:wdc6yKVaHxkNOEdz4cRZs1pQkwSXPiRjq69yWP4QQS github.com/labstack/echo/v4 v4.8.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 430fb7f..21a2159 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -4,7 +4,6 @@ import ( "crypto" "crypto/hmac" "crypto/sha256" - "database/sql" "encoding/hex" "encoding/json" "errors" @@ -13,8 +12,6 @@ import ( "net/http" "net/url" - _ "github.com/lib/pq" - "github.com/combodga/Project/internal/storage" "github.com/btcsuite/btcutil/base58" @@ -31,7 +28,7 @@ type Handler struct { } func New(serverAddr, baseURL, dbFile, dbCredentials string) (*Handler, error) { - s, err := storage.New(dbFile) + s, err := storage.New(dbFile, dbCredentials) if err != nil { err = fmt.Errorf("storage: %v", err) } @@ -129,11 +126,10 @@ func (h *Handler) ListURL(c echo.Context) error { } func (h *Handler) Ping(c echo.Context) error { - db, err := sql.Open("postgres", h.DBCredentials) - if err != nil { + ok := h.Storage.Ping() + if !ok { return c.String(http.StatusInternalServerError, "error, no connection to db") } - defer db.Close() return c.String(http.StatusOK, "db connected") } diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 864afba..d3a7e1d 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -1,43 +1,87 @@ package storage import ( + "database/sql" "encoding/json" "errors" "os" "sync" + + "github.com/jmoiron/sqlx" + _ "github.com/lib/pq" ) type Storage struct { - DBFile string - Pairs map[string]map[string]string - Mutex *sync.RWMutex + DBFile string + DBCredentials string + Pairs map[string]map[string]string + Mutex *sync.RWMutex } -func New(dbFile string) (*Storage, error) { - s := &Storage{ - DBFile: dbFile, - Pairs: make(map[string]map[string]string), - Mutex: &sync.RWMutex{}, - } +type Link struct { + User string `db:"usr"` + ID string `db:"short"` + Link string `db:"long"` +} - if dbFile == "" { - return s, nil +func New(dbFile, dbCredentials string) (*Storage, error) { + s := &Storage{ + DBFile: dbFile, + DBCredentials: dbCredentials, + Pairs: make(map[string]map[string]string), + Mutex: &sync.RWMutex{}, } s.Mutex.Lock() defer s.Mutex.Unlock() - pairsStr, err := os.ReadFile(dbFile) - if errors.Is(err, os.ErrNotExist) { + if dbCredentials != "" { + db, err := sqlx.Connect("postgres", s.DBCredentials) + if err != nil { + return s, err + } + defer db.Close() + + db.MustExec(` + CREATE TABLE IF NOT EXISTS shortener ( + usr text, + short text, + long text + ); + `) + + link := Link{} + rows, err := db.Queryx("SELECT * FROM shortener") + if err != nil { + return s, err + } + for rows.Next() { + err := rows.StructScan(&link) + if err != nil { + return s, err + } + if len(s.Pairs[link.User]) == 0 { + s.Pairs[link.User] = make(map[string]string) + } + s.Pairs[link.User][link.ID] = link.Link + } + return s, nil } - if err != nil { - return s, err - } - err = json.Unmarshal(pairsStr, &s.Pairs) - if err != nil { - return s, err + if dbFile != "" { + pairsStr, err := os.ReadFile(dbFile) + if errors.Is(err, os.ErrNotExist) { + return s, nil + } + if err != nil { + return s, err + } + + err = json.Unmarshal(pairsStr, &s.Pairs) + if err != nil { + return s, err + } } return s, nil @@ -70,18 +114,28 @@ func (s *Storage) SetURL(user, id, link string) error { } s.Pairs[user][id] = link - if s.DBFile == "" { + if s.DBCredentials != "" { + db, err := sqlx.Connect("postgres", s.DBCredentials) + if err != nil { + return err + } + defer db.Close() + + db.MustExec("INSERT INTO shortener VALUES ($1, $2, $3)", user, id, link) + return nil } - jsonStr, err := json.Marshal(s.Pairs) - if err != nil { - return err - } + if s.DBFile != "" { + jsonStr, err := json.Marshal(s.Pairs) + if err != nil { + return err + } - err = os.WriteFile(s.DBFile, []byte(jsonStr), 0777) - if err != nil { - return err + err = os.WriteFile(s.DBFile, []byte(jsonStr), 0777) + if err != nil { + return err + } } return nil @@ -97,3 +151,12 @@ func (s *Storage) ListURL(user string) (map[string]string, bool) { return list, true } + +func (s *Storage) Ping() bool { + db, err := sql.Open("postgres", s.DBCredentials) + if err != nil { + return false + } + db.Close() + return true +} From d1e4ead61beaaa5966aa8dd8c703374bdc387d42 Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:25:33 +0300 Subject: [PATCH 08/18] fix test --- internal/storage/storage_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/storage/storage_test.go b/internal/storage/storage_test.go index 4493730..b71d3f2 100644 --- a/internal/storage/storage_test.go +++ b/internal/storage/storage_test.go @@ -18,7 +18,7 @@ var ( func TestInit(t *testing.T) { var err error - S, err = New("") + S, err = New("", "") if err != nil { t.Fatal("can't start test") } From 286531f58ec5e354745289c3fcc2ede73c2abb6a Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:30:44 +0300 Subject: [PATCH 09/18] added db rows error checking --- internal/storage/storage.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index d3a7e1d..065ec5a 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -65,6 +65,10 @@ func New(dbFile, dbCredentials string) (*Storage, error) { } s.Pairs[link.User][link.ID] = link.Link } + err = rows.Err() + if err != nil { + return s, err + } return s, nil } From 9f6a70b000e8ee6da80841fab3b586ff04abf7e4 Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:32:39 +0300 Subject: [PATCH 10/18] fixed test --- internal/storage/storage_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/storage/storage_test.go b/internal/storage/storage_test.go index b71d3f2..8589a5f 100644 --- a/internal/storage/storage_test.go +++ b/internal/storage/storage_test.go @@ -18,7 +18,7 @@ var ( func TestInit(t *testing.T) { var err error - S, err = New("", "") + S, err = New("db", "") if err != nil { t.Fatal("can't start test") } From 446ad7c3abe4257fe64df604d6374600c7fddb0b Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:40:22 +0300 Subject: [PATCH 11/18] debug storage --- internal/storage/storage.go | 3 +++ internal/storage/storage_test.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 065ec5a..a5fdb7c 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -4,6 +4,7 @@ import ( "database/sql" "encoding/json" "errors" + "fmt" "os" "sync" @@ -36,6 +37,7 @@ func New(dbFile, dbCredentials string) (*Storage, error) { defer s.Mutex.Unlock() if dbCredentials != "" { + fmt.Println("dbc") db, err := sqlx.Connect("postgres", s.DBCredentials) if err != nil { return s, err @@ -74,6 +76,7 @@ func New(dbFile, dbCredentials string) (*Storage, error) { } if dbFile != "" { + fmt.Println("dbf") pairsStr, err := os.ReadFile(dbFile) if errors.Is(err, os.ErrNotExist) { return s, nil diff --git a/internal/storage/storage_test.go b/internal/storage/storage_test.go index 8589a5f..b71d3f2 100644 --- a/internal/storage/storage_test.go +++ b/internal/storage/storage_test.go @@ -18,7 +18,7 @@ var ( func TestInit(t *testing.T) { var err error - S, err = New("db", "") + S, err = New("", "") if err != nil { t.Fatal("can't start test") } From 3810250ba537ed7b3a2b5a3d988e6f448818c552 Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:43:58 +0300 Subject: [PATCH 12/18] debug storage --- internal/storage/storage.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index a5fdb7c..07c9037 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -4,7 +4,6 @@ import ( "database/sql" "encoding/json" "errors" - "fmt" "os" "sync" @@ -37,10 +36,9 @@ func New(dbFile, dbCredentials string) (*Storage, error) { defer s.Mutex.Unlock() if dbCredentials != "" { - fmt.Println("dbc") db, err := sqlx.Connect("postgres", s.DBCredentials) if err != nil { - return s, err + return s, nil } defer db.Close() @@ -76,7 +74,6 @@ func New(dbFile, dbCredentials string) (*Storage, error) { } if dbFile != "" { - fmt.Println("dbf") pairsStr, err := os.ReadFile(dbFile) if errors.Is(err, os.ErrNotExist) { return s, nil From 21295718bd7d33db2cd76979d9035dbc84d8f7ac Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:47:09 +0300 Subject: [PATCH 13/18] debug storage --- internal/storage/storage.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 07c9037..ee67051 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -38,7 +38,7 @@ func New(dbFile, dbCredentials string) (*Storage, error) { if dbCredentials != "" { db, err := sqlx.Connect("postgres", s.DBCredentials) if err != nil { - return s, nil + return s, err } defer db.Close() @@ -58,7 +58,7 @@ func New(dbFile, dbCredentials string) (*Storage, error) { for rows.Next() { err := rows.StructScan(&link) if err != nil { - return s, err + return s, nil } if len(s.Pairs[link.User]) == 0 { s.Pairs[link.User] = make(map[string]string) From a77e4616abec27d8e8a3a33641b9823ccf09e350 Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:51:50 +0300 Subject: [PATCH 14/18] debug storage --- internal/storage/storage.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index ee67051..2ee3d54 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -58,7 +58,7 @@ func New(dbFile, dbCredentials string) (*Storage, error) { for rows.Next() { err := rows.StructScan(&link) if err != nil { - return s, nil + return s, err } if len(s.Pairs[link.User]) == 0 { s.Pairs[link.User] = make(map[string]string) @@ -67,7 +67,7 @@ func New(dbFile, dbCredentials string) (*Storage, error) { } err = rows.Err() if err != nil { - return s, err + return s, nil } return s, nil From a43930d61e7bc977f65586196cfb63d867b5717e Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:54:24 +0300 Subject: [PATCH 15/18] debug storage --- internal/storage/storage.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 2ee3d54..66df10e 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -4,6 +4,7 @@ import ( "database/sql" "encoding/json" "errors" + "fmt" "os" "sync" @@ -36,6 +37,7 @@ func New(dbFile, dbCredentials string) (*Storage, error) { defer s.Mutex.Unlock() if dbCredentials != "" { + fmt.Println(dbCredentials) db, err := sqlx.Connect("postgres", s.DBCredentials) if err != nil { return s, err @@ -67,7 +69,7 @@ func New(dbFile, dbCredentials string) (*Storage, error) { } err = rows.Err() if err != nil { - return s, nil + return s, err } return s, nil From 049531960773c6edb4432d369dd7a4cfabc2080f Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 05:56:56 +0300 Subject: [PATCH 16/18] fixed test --- internal/handler/handler_test.go | 2 +- internal/storage/storage.go | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/handler/handler_test.go b/internal/handler/handler_test.go index f597af2..284b84c 100644 --- a/internal/handler/handler_test.go +++ b/internal/handler/handler_test.go @@ -27,7 +27,7 @@ var ( func TestInit(t *testing.T) { var err error - H, err = New("localhost:8080", "http://localhost:8080", "", "user=a password=b dbname=c sslmode=disable") + H, err = New("localhost:8080", "http://localhost:8080", "", "") if err != nil { t.Fatal("can't start test") } diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 66df10e..065ec5a 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -4,7 +4,6 @@ import ( "database/sql" "encoding/json" "errors" - "fmt" "os" "sync" @@ -37,7 +36,6 @@ func New(dbFile, dbCredentials string) (*Storage, error) { defer s.Mutex.Unlock() if dbCredentials != "" { - fmt.Println(dbCredentials) db, err := sqlx.Connect("postgres", s.DBCredentials) if err != nil { return s, err From 2eb016acd21df27a0409360e5972e2e58356fe8b Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 06:48:00 +0300 Subject: [PATCH 17/18] increment12 --- internal/app/app.go | 1 + internal/handler/handler.go | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/internal/app/app.go b/internal/app/app.go index 4be8231..6c07448 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -22,6 +22,7 @@ func Start(serverAddr, baseURL, dbFile, dbCredentials string) error { e.POST("/", h.CreateURL) e.GET("/:id", h.RetrieveURL) e.POST("/api/shorten", h.CreateURLInJSON) + e.POST("/api/shorten/batch", h.CreateBatchURL) e.GET("/api/user/urls", h.ListURL) e.GET("/ping", h.Ping) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 21a2159..ed2afd9 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -91,6 +91,47 @@ func (h *Handler) CreateURLInJSON(c echo.Context) error { return c.JSON(http.StatusCreated, l) } +type LinkJson struct { + CorrelationID string `json:"correlation_id"` + OriginalURL string `json:"original_url"` +} + +type BatchLink struct { + CorrelationID string `json:"correlation_id"` + ShortURL string `json:"short_url"` +} + +func (h *Handler) CreateBatchURL(c echo.Context) error { + user := getUser(c, h.Key) + body, err := io.ReadAll(c.Request().Body) + if err != nil { + return err + } + + var l []LinkJson + err = json.Unmarshal(body, &l) + if err != nil { + return err + } + + var bl []BatchLink + for result := range l { + link := l[result] + + id, err := h.fetchID(c, user, link.OriginalURL) + if err != nil { + return fmt.Errorf("fetchID: %v", err) + } + + bl = append(bl, BatchLink{ + CorrelationID: link.CorrelationID, + ShortURL: h.BaseURL + "/" + id, + }) + } + + return c.JSON(http.StatusCreated, bl) +} + func (h *Handler) RetrieveURL(c echo.Context) error { id := c.Param("id") From 6e3dd23e57a403092c6d32cb02cc64e49a0d204e Mon Sep 17 00:00:00 2001 From: combodga Date: Wed, 14 Sep 2022 06:50:47 +0300 Subject: [PATCH 18/18] fixed Json to JSON --- internal/handler/handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index ed2afd9..7326128 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -91,7 +91,7 @@ func (h *Handler) CreateURLInJSON(c echo.Context) error { return c.JSON(http.StatusCreated, l) } -type LinkJson struct { +type LinkJSON struct { CorrelationID string `json:"correlation_id"` OriginalURL string `json:"original_url"` } @@ -108,7 +108,7 @@ func (h *Handler) CreateBatchURL(c echo.Context) error { return err } - var l []LinkJson + var l []LinkJSON err = json.Unmarshal(body, &l) if err != nil { return err