github.com/gudimz/urlShortener@v0.0.0-20230129195305-c8ee33059a67/internal/server/handler.go (about) 1 package server 2 3 import ( 4 "errors" 5 "fmt" 6 "github.com/gudimz/urlShortener/internal/config" 7 "github.com/gudimz/urlShortener/internal/model" 8 "github.com/gudimz/urlShortener/internal/shorten" 9 "github.com/gudimz/urlShortener/pkg/logging" 10 "github.com/jackc/pgx/v5" 11 "github.com/jackc/pgx/v5/pgconn" 12 "github.com/labstack/echo/v4" 13 "github.com/samber/mo" 14 "net/http" 15 "strings" 16 ) 17 18 type handler struct { 19 logger *logging.Logger 20 shortener *shorten.Service 21 } 22 23 func NewHandler(shortener *shorten.Service, logger *logging.Logger) *handler { 24 return &handler{ 25 logger: logger, 26 shortener: shortener, 27 } 28 } 29 30 type request struct { 31 Url string `json:"url" validate:"required,url"` 32 ShortUrl string `json:"short_url,omitempty" validate:"omitempty,alphanum"` 33 } 34 35 type response struct { 36 Message string `json:"message,omitempty"` 37 } 38 39 func (h *handler) CreateShorten(ctx echo.Context) error { 40 var req request 41 if err := ctx.Bind(&req); err != nil { 42 return err 43 } 44 45 if err := ctx.Validate(req); err != nil { 46 return err 47 } 48 shortenUrl := mo.None[string]() 49 if strings.TrimSpace(req.ShortUrl) != "" { 50 shortenUrl = mo.Some(req.ShortUrl) 51 } 52 53 input := model.InputShorten{ 54 ShortenUrl: shortenUrl, 55 OriginUrl: req.Url, 56 } 57 58 h.logger.Infof("create shorten for short url \"%v\"", input.ShortenUrl) 59 shortener, err := h.shortener.CreateShorten(ctx.Request().Context(), input) 60 if err != nil { 61 var pgErr *pgconn.PgError 62 if errors.As(err, &pgErr) { 63 if strings.Compare(pgErr.Code, "23505") == 0 { 64 return echo.NewHTTPError(http.StatusConflict, "short url already exist") 65 } 66 } 67 h.logger.Errorf("error creating shorten: %v", err) 68 return echo.NewHTTPError(http.StatusInternalServerError, 69 fmt.Sprintf("can't create short url \"%v\"", input.ShortenUrl)) 70 } 71 message := fmt.Sprintf("%v:%v/%v", 72 config.GetConfig().Server.BaseUrl, 73 config.GetConfig().Server.Port, 74 shortener.ShortUrl, 75 ) 76 return ctx.JSON(http.StatusOK, response{Message: message}) 77 } 78 79 func (h *handler) Redirect(ctx echo.Context) error { 80 shortUrl := ctx.Param("short_url") 81 h.logger.Infof("redirect for short url %q", shortUrl) 82 originUrl, err := h.shortener.Redirect(ctx.Request().Context(), shortUrl) 83 if err != nil { 84 if errors.Is(err, pgx.ErrNoRows) { 85 return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("short url %q not found", shortUrl)) 86 } 87 88 h.logger.Errorf("error getting redirect for short url %q: %v", shortUrl, err) 89 return echo.NewHTTPError(http.StatusInternalServerError, 90 fmt.Sprintf("can't get url by short url %q", shortUrl)) 91 } 92 return ctx.Redirect(http.StatusMovedPermanently, originUrl) 93 } 94 95 func (h *handler) GetShorten(ctx echo.Context) error { 96 shortUrl := ctx.Param("short_url") 97 h.logger.Infof("get shorten from db for short url %q", shortUrl) 98 shortenInfo, err := h.shortener.GetShorten(ctx.Request().Context(), shortUrl) 99 if err != nil { 100 if errors.Is(err, pgx.ErrNoRows) { 101 return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("short url %q not found", shortUrl)) 102 } 103 104 h.logger.Errorf("error getting GetShorten for short url %q: %v", shortUrl, err) 105 return echo.NewHTTPError(http.StatusInternalServerError, 106 fmt.Sprintf("failed to get shorten for %q", shortUrl)) 107 } 108 return ctx.JSON(http.StatusOK, shortenInfo) 109 } 110 111 func (h *handler) DeleteShorten(ctx echo.Context) error { 112 shortUrl := ctx.Param("short_url") 113 h.logger.Infof("delete shorten from db for short url %q", shortUrl) 114 count, err := h.shortener.DeleteShorten(ctx.Request().Context(), shortUrl) 115 if count == 0 { 116 return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("short url %q not found", shortUrl)) 117 } 118 if err != nil { 119 h.logger.Errorf("error deleting GetShorten for short url %q: %v", shortUrl, err) 120 return echo.NewHTTPError(http.StatusInternalServerError, 121 fmt.Sprintf("failed to delete shorten for %q", shortUrl)) 122 } 123 return ctx.NoContent(http.StatusNoContent) 124 }