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  }