tiny-url-ex/service/shortener/shortener.go

163 lines
3.8 KiB
Go

package shortener
import (
"database/sql"
"log"
"net/http"
"git.xdrm.io/example/aicra/service/auth"
"git.xdrm.io/go/aicra"
"git.xdrm.io/go/aicra/api"
)
// Service manages the url shortener
type Service struct {
storage *sql.DB
authService *auth.Service
repo *repository
}
// New returns a bare service
func New(storage *sql.DB, auth *auth.Service) (*Service, error) {
log.Printf("[shortener] creating service")
log.Printf("[shortener] creating repo")
repo, err := newRepo(storage)
if err != nil {
return nil, err
}
log.Printf("[shortener] repo created")
log.Printf("[shortener] service created")
return &Service{
repo: repo,
authService: auth,
}, nil
}
// Wire to the aicra server
func (svc *Service) Wire(server *aicra.Server) {
log.Printf("[shortener] service wired")
server.HandleFunc("GET", "/", svc.redirect)
server.HandleFunc("POST", "/", svc.authService.CheckToken(svc.register))
server.HandleFunc("PUT", "/", svc.authService.CheckToken(svc.update))
server.HandleFunc("DELETE", "/", svc.authService.CheckToken(svc.delete))
}
// redirect from a tiny url to the long url
func (svc *Service) redirect(req api.Request, res *api.Response) {
// 1. extract input
tinyURL, err := req.Param.GetString("url")
if err != nil {
res.SetError(api.ErrorInvalidParam(), "url", err.Error())
return
}
// 2. check in db if exists
model := svc.repo.NewModel(tinyURL, "")
if model.Search() != nil {
res.SetError(api.ErrorNoMatchFound())
return
}
// 3. redirect
res.Status = http.StatusPermanentRedirect
res.Headers.Set("Location", model.long)
res.SetError(api.ErrorSuccess())
}
// register registers a new tiny url to a long one
func (svc *Service) register(req api.Request, res *api.Response) {
// 1. extract arguments
longURL, err := req.Param.GetString("target")
if err != nil {
res.SetError(api.ErrorInvalidParam(), "target", err.Error())
return
}
tinyURL, err := req.Param.GetString("url")
if err != nil {
res.SetError(api.ErrorInvalidParam(), "url", err.Error())
return
}
// 2. fail if already used
model := svc.repo.NewModel(tinyURL, "")
if model.Search() == nil {
res.SetError(api.ErrorAlreadyExists(), "url")
return
}
// 3. store association
model.long = longURL
if model.Create() != nil {
res.SetError(api.ErrorFailure())
return
}
log.Printf("[shortener] new %q -> %q", model.tiny, model.long)
res.SetError(api.ErrorSuccess())
}
// update updates an existing tiny url to a new long one
func (svc *Service) update(req api.Request, res *api.Response) {
// 1. extract arguments
longURL, err := req.Param.GetString("target")
if err != nil {
res.SetError(api.ErrorInvalidParam(), "target", err.Error())
return
}
tinyURL, err := req.Param.GetString("url")
if err != nil {
res.SetError(api.ErrorInvalidParam(), "url", err.Error())
return
}
// 2. fail if not already existing
model := svc.repo.NewModel(tinyURL, "")
if model.Search() != nil {
res.SetError(api.ErrorNoMatchFound())
return
}
// 3. update association
model.long = longURL
if model.Update() != nil {
res.SetError(api.ErrorFailure())
return
}
log.Printf("[shortener] upd %q -> %q", model.tiny, model.long)
res.SetError(api.ErrorSuccess())
}
// delete removes a new tiny url
func (svc *Service) delete(req api.Request, res *api.Response) {
// 1. extract arguments
tinyURL, err := req.Param.GetString("url")
if err != nil {
res.SetError(api.ErrorInvalidParam(), "url", err.Error())
return
}
// 2. fail if not already existing
model := svc.repo.NewModel(tinyURL, "")
if model.Search() != nil {
res.SetError(api.ErrorNoMatchFound())
return
}
// 3. delete association
if model.Delete() != nil {
res.SetError(api.ErrorFailure())
return
}
log.Printf("[shortener] del %q", model.tiny)
res.SetError(api.ErrorSuccess())
}