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(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)) } // 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()) }