2019-05-01 14:42:17 +00:00
|
|
|
package shortener
|
|
|
|
|
|
|
|
import (
|
2019-05-02 05:54:06 +00:00
|
|
|
"database/sql"
|
2019-05-01 14:42:17 +00:00
|
|
|
"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 {
|
2019-05-02 05:54:06 +00:00
|
|
|
storage *sql.DB
|
2019-05-01 14:42:17 +00:00
|
|
|
authService *auth.Service
|
2019-05-02 05:54:06 +00:00
|
|
|
repo *repository
|
2019-05-01 14:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a bare service
|
2019-05-02 18:50:58 +00:00
|
|
|
func New(storage *sql.DB, auth *auth.Service) (*Service, error) {
|
|
|
|
log.Printf("[shortener] creating service")
|
2019-05-02 05:54:06 +00:00
|
|
|
|
2019-05-02 18:50:58 +00:00
|
|
|
log.Printf("[shortener] creating repo")
|
|
|
|
repo, err := newRepo(storage)
|
2019-05-02 05:54:06 +00:00
|
|
|
if err != nil {
|
2019-05-02 18:50:58 +00:00
|
|
|
return nil, err
|
2019-05-02 05:54:06 +00:00
|
|
|
}
|
2019-05-02 18:50:58 +00:00
|
|
|
log.Printf("[shortener] repo created")
|
2019-05-02 05:54:06 +00:00
|
|
|
|
2019-05-02 18:50:58 +00:00
|
|
|
log.Printf("[shortener] service created")
|
|
|
|
return &Service{
|
|
|
|
repo: repo,
|
|
|
|
authService: auth,
|
|
|
|
}, nil
|
2019-05-01 14:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Wire to the aicra server
|
|
|
|
func (svc *Service) Wire(server *aicra.Server) {
|
2019-05-02 18:50:58 +00:00
|
|
|
log.Printf("[shortener] service wired")
|
2019-05-02 18:55:36 +00:00
|
|
|
server.HandleFunc(http.MethodGet, "/", svc.redirect)
|
|
|
|
server.HandleFunc(http.MethodPost, "/", svc.authService.CheckToken(svc.register))
|
|
|
|
server.HandleFunc(http.MethodPut, "/", svc.authService.CheckToken(svc.update))
|
|
|
|
server.HandleFunc(http.MethodDelete, "/", svc.authService.CheckToken(svc.delete))
|
2019-05-01 14:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorInvalidParam(), "url", err.Error())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. check in db if exists
|
2019-05-02 18:50:58 +00:00
|
|
|
model := svc.repo.NewModel(tinyURL, "")
|
|
|
|
if model.Search() != nil {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorNoMatchFound())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. redirect
|
|
|
|
res.Status = http.StatusPermanentRedirect
|
2019-05-02 18:50:58 +00:00
|
|
|
res.Headers.Set("Location", model.long)
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorSuccess())
|
2019-05-01 14:42:17 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorInvalidParam(), "target", err.Error())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
tinyURL, err := req.Param.GetString("url")
|
|
|
|
if err != nil {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorInvalidParam(), "url", err.Error())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. fail if already used
|
2019-05-02 18:50:58 +00:00
|
|
|
model := svc.repo.NewModel(tinyURL, "")
|
|
|
|
if model.Search() == nil {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorAlreadyExists(), "url")
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. store association
|
2019-05-02 18:50:58 +00:00
|
|
|
model.long = longURL
|
|
|
|
if model.Create() != nil {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorFailure())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
2019-05-02 18:50:58 +00:00
|
|
|
log.Printf("[shortener] new %q -> %q", model.tiny, model.long)
|
2019-05-01 14:42:17 +00:00
|
|
|
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorSuccess())
|
2019-05-01 14:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorInvalidParam(), "target", err.Error())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
tinyURL, err := req.Param.GetString("url")
|
|
|
|
if err != nil {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorInvalidParam(), "url", err.Error())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. fail if not already existing
|
2019-05-02 18:50:58 +00:00
|
|
|
model := svc.repo.NewModel(tinyURL, "")
|
|
|
|
if model.Search() != nil {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorNoMatchFound())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. update association
|
2019-05-02 18:50:58 +00:00
|
|
|
model.long = longURL
|
|
|
|
if model.Update() != nil {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorFailure())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
2019-05-02 18:50:58 +00:00
|
|
|
log.Printf("[shortener] upd %q -> %q", model.tiny, model.long)
|
2019-05-01 14:42:17 +00:00
|
|
|
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorSuccess())
|
2019-05-01 14:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorInvalidParam(), "url", err.Error())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. fail if not already existing
|
2019-05-02 18:50:58 +00:00
|
|
|
model := svc.repo.NewModel(tinyURL, "")
|
|
|
|
if model.Search() != nil {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorNoMatchFound())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-02 18:50:58 +00:00
|
|
|
// 3. delete association
|
|
|
|
if model.Delete() != nil {
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorFailure())
|
2019-05-01 14:42:17 +00:00
|
|
|
return
|
|
|
|
}
|
2019-05-02 18:50:58 +00:00
|
|
|
log.Printf("[shortener] del %q", model.tiny)
|
2019-05-01 14:42:17 +00:00
|
|
|
|
2019-05-01 16:54:36 +00:00
|
|
|
res.SetError(api.ErrorSuccess())
|
2019-05-01 14:42:17 +00:00
|
|
|
}
|