url_shortener/internal/http-server/handlers/url/save/save.go

92 lines
2.1 KiB
Go
Raw Normal View History

2023-12-08 12:59:55 +02:00
package save
import (
"errors"
"log/slog"
"net/http"
resp "url-shortener/internal/lib/api/response"
"url-shortener/internal/lib/logger/sl"
"url-shortener/internal/lib/random"
"url-shortener/internal/storage"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/render"
"github.com/go-playground/validator/v10"
)
type Request struct {
URL string `json:"url" validate:"required,url"`
Alias string `json:"alias,omitempty"`
}
type Response struct {
resp.Response
Alias string `json:"alias,omitempty"`
}
// TODO: move to config
const aliasLength = 6
//go:generate go run github.com/vektra/mockery/v2@v2.38.0 --name=URLSaver
type URLSaver interface {
SaveURL(urlToSave string, alias string) error
}
func New(log *slog.Logger, urlSaver URLSaver) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
const op = "handlers.url.save.New"
log = log.With(
slog.String("op", op),
slog.String("request_id", middleware.GetReqID(r.Context())),
)
var req Request
// decode
err := render.DecodeJSON(r.Body, &req)
if err != nil {
log.Error("failed to decode request body", sl.Err(err))
render.JSON(w, r, resp.Error("failed to decode request"))
return
}
log.Debug("request body decoded", slog.Any("request", req))
// validate fields
if err := validator.New().Struct(req); err != nil {
validateErr := err.(validator.ValidationErrors)
log.Error("invalid request", sl.Err(err))
render.JSON(w, r, resp.ValidationError(validateErr))
return
}
// get or generate alias
// TODO:
alias := req.Alias
if alias == "" {
alias = random.NewRandomString(aliasLength)
}
// save to storage
err = urlSaver.SaveURL(req.URL, alias)
if errors.Is(err, storage.ErrURLExists) {
log.Info("alias already exists", slog.String("url", req.URL))
render.JSON(w, r, resp.Error("alias already exists"))
return
}
if err != nil {
log.Error("failed to add url", sl.Err(err))
render.JSON(w, r, resp.Error("failed to add url"))
return
}
log.Info("url added", slog.String("alias", alias))
render.JSON(w, r, Response{
Response: resp.OK(),
Alias: alias,
})
}
}