diff --git a/.gitignore b/.gitignore
index daf913b..0d412bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,6 @@ _testmain.go
*.exe
*.test
*.prof
+
+# exclude our compiled binary
+gophurl
diff --git a/data/json_shorturl_repo.go b/data/json_shorturl_repo.go
index bf7cf86..804032e 100644
--- a/data/json_shorturl_repo.go
+++ b/data/json_shorturl_repo.go
@@ -1,43 +1,102 @@
package data
import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+
"github.com/BelfastGophers/gophurl/models"
)
-type JsonShortURLRepository struct {
+type JSONShortURLRepository struct {
Filepath string
URLList *models.URLList
}
-func NewJsonShortURLRepository(filePath string) (*JsonShortURLRepository, error) {
- repo := &JsonShortURLRepository{}
+func NewJSONShortURLRepository(filePath string) (*JSONShortURLRepository, error) {
+ f, err := os.Open(filePath)
+ if err != nil {
+ return nil, err
+ }
+
+ data, err := ioutil.ReadAll(f)
+ if err != nil {
+ return nil, err
+ }
+
+ list := &models.URLList{}
+
+ if err := json.Unmarshal(data, &list); err != nil {
+ return nil, err
+ }
+
+ repo := &JSONShortURLRepository{
+ Filepath: filePath,
+ URLList: list,
+ }
+
return repo, nil
}
-func (repo *JsonShortURLRepository) Get(code string) (*models.ShortURL, error) {
- return nil, nil
+func (repo *JSONShortURLRepository) Get(code string) (*models.ShortURL, error) {
+ shortUrls := repo.URLList.GetShortUrls()
+
+ for _, shortUrl := range shortUrls {
+ if code == shortUrl.Code {
+ return shortUrl, nil
+ }
+ }
+ return nil, errors.New(fmt.Sprintf("Unable to locate url for '%v'", code))
}
-func (repo *JsonShortURLRepository) Add(url *models.ShortURL) error {
+func (repo *JSONShortURLRepository) Add(url *models.ShortURL) error {
return nil
}
-func (repo *JsonShortURLRepository) Delete(url *models.ShortURL) error {
+func (repo *JSONShortURLRepository) Delete(url *models.ShortURL) error {
return nil
}
-func (repo *JsonShortURLRepository) Update(url *models.ShortURL) error {
+func (repo *JSONShortURLRepository) Update(url *models.ShortURL) error {
return nil
}
-func (repo *JsonShortURLRepository) All() (map[string]*models.ShortURL, error) {
- return map[string]*models.ShortURL{}, nil
+func (repo *JSONShortURLRepository) All() (map[string]*models.ShortURL, error) {
+ return repo.URLList.ShortURLs, nil
}
-func (repo *JsonShortURLRepository) IncrementAccessed(code string) error {
- return nil
+func (repo *JSONShortURLRepository) IncrementAccessed(code string) error {
+ shortUrls := repo.URLList.GetShortUrls()
+
+ for _, shortUrl := range shortUrls {
+ if code == shortUrl.Code {
+ shortUrl.AccessCount++
+ err := repo.write()
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }
+ }
+ return errors.New(fmt.Sprintf("Unable to locate ShortUrl with code '%v'", code))
}
-func (repo *JsonShortURLRepository) write() error {
+func (repo *JSONShortURLRepository) write() error {
+ list := repo.URLList
+
+ jsonList, err := json.MarshalIndent(list, "", " ")
+ if err != nil {
+ return err
+ }
+
+ err = ioutil.WriteFile(repo.Filepath, jsonList, 0644)
+ if err != nil {
+ return err
+ }
+
return nil
}
diff --git a/demo.json b/demo.json
index 5ae1640..329fc16 100644
--- a/demo.json
+++ b/demo.json
@@ -1,15 +1,15 @@
{
"updated": "2016-08-25T09:39:48+00:00",
"urls": {
- "asdf43": {
- "accessed": 7,
- "code": "asdf43",
+ "google": {
+ "accessed": 0,
+ "code": "goog",
"created": "2016-08-25T09:39:48+00:00",
"url": "https://www.google.com/"
},
"belfast-gophers": {
- "accessed": 1,
- "code": "gitgo",
+ "accessed": 0,
+ "code": "belgo",
"created": "2016-08-25T09:43:48+00:00",
"url": "https://twitter.com/belfast_gopher"
}
diff --git a/handlers/html/index.go b/handlers/html/index.go
index 97da5a1..d9f69a2 100644
--- a/handlers/html/index.go
+++ b/handlers/html/index.go
@@ -13,8 +13,9 @@ const (
{{ range $key, $url := . }}
{{ $key }} |
- {{ $url.Url }} |
- {{ $url.Accessed }} |
+ {{ $url.URL }} |
+ {{ $url.Code }} |
+ {{ $url.AccessCount }} |
{{ $url.Created }} |
{{ end }}
diff --git a/handlers/web.go b/handlers/web.go
index 0bff675..9aa83ec 100644
--- a/handlers/web.go
+++ b/handlers/web.go
@@ -13,7 +13,9 @@ import (
// with a collection of ShortURLs. The implmentation of the interactor is
// where all business rules can be implemented
type ShortURLInteractor interface {
- GetAll() ([]*models.ShortURL, error)
+ GetAll() (map[string]*models.ShortURL, error)
+ GetUrl(string) (string, error)
+ IncrementAccessCount(string) error
}
// WebShortener provides a number of HTTP Handlers.
@@ -27,19 +29,39 @@ type WebShortener struct {
// * If a path is provided, try to redirect to it
func (handler *WebShortener) Index(w http.ResponseWriter, req *http.Request) {
// TODO: Check if a path is provided. If it is follow it
+ shortUrlCode := req.URL.Path[1:]
// Get all of the URL's from the Repository
- _, err := handler.ShortURLInteractor.GetAll()
+ urls, err := handler.ShortURLInteractor.GetAll()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
+ // if a shortUrlCode is present in the URL, redirect to it
+ if shortUrlCode != "" {
+ redirectUrl, err := handler.GetRedirectUrl(shortUrlCode)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+
+ err = handler.ShortURLInteractor.IncrementAccessCount(shortUrlCode)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+
+ http.Redirect(w, req, redirectUrl, http.StatusFound)
+ }
+
tmpl, err := template.New("name").Parse(html.Index)
- if err = tmpl.Execute(w, nil); err != nil {
+ if err = tmpl.Execute(w, urls); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
+func (handler *WebShortener) GetRedirectUrl(urlCode string) (string, error) {
+ return handler.ShortURLInteractor.GetUrl(urlCode)
+}
+
// NewShortenerWeb returns a struct which implements the WebShortener interface. It
// populates fields to usable values
func NewWebShortener(urlRepo models.ShortURLRepository) *WebShortener {
diff --git a/main.go b/main.go
index 6a8dc77..b5ffe22 100644
--- a/main.go
+++ b/main.go
@@ -9,13 +9,13 @@ import (
const (
// Path to a JSON file
- pathToJsonFile = "demo.json" // Eeewwwwwww, code smell
+ pathToJSONFile = "demo.json" // Eeewwwwwww, code smell
)
// run is called by main so we can actually test this logic
// TODO: Write tests....
func run() error {
- repo, err := data.NewJsonShortURLRepository(pathToJsonFile)
+ repo, err := data.NewJSONShortURLRepository(pathToJSONFile)
if err != nil {
return err
}
diff --git a/models/url.go b/models/url.go
index 142cc7f..1e8d1b5 100644
--- a/models/url.go
+++ b/models/url.go
@@ -10,11 +10,15 @@ type URLList struct {
ShortURLs map[string]*ShortURL `json:"urls"`
}
+func (list *URLList) GetShortUrls() map[string]*ShortURL {
+ return list.ShortURLs
+}
+
// A ShortUrl is a structure which captures the
type ShortURL struct {
URL string `json:"url"`
- Code string `json:"short_code"`
- AccessCount int `json:"access_count"`
+ Code string `json:"code"`
+ AccessCount int `json:"accessed"`
Created *time.Time `json:"created"`
}
diff --git a/services/shortener.go b/services/shortener.go
index 773456e..e801e81 100644
--- a/services/shortener.go
+++ b/services/shortener.go
@@ -11,6 +11,7 @@ import (
// Shortener provides a collection of web handlers
type WebShortener interface {
Index(http.ResponseWriter, *http.Request)
+ GetRedirectUrl(string) (string, error)
}
// Shortener is a service which provides a web accessible URL shortener.
diff --git a/usecases/shorturl_interactor.go b/usecases/shorturl_interactor.go
index 5916cfd..e8a1cd6 100644
--- a/usecases/shorturl_interactor.go
+++ b/usecases/shorturl_interactor.go
@@ -1,13 +1,25 @@
package usecases
-import (
- "github.com/BelfastGophers/gophurl/models"
-)
+import "github.com/BelfastGophers/gophurl/models"
type ShortURLInteractor struct {
URLRepository models.ShortURLRepository
}
-func (interactor *ShortURLInteractor) GetAll() ([]*models.ShortURL, error) {
- return []*models.ShortURL{}, nil
+func (interactor *ShortURLInteractor) GetAll() (map[string]*models.ShortURL, error) {
+ return interactor.URLRepository.All()
+}
+
+func (interactor *ShortURLInteractor) GetUrl(code string) (string, error) {
+ result, err := interactor.URLRepository.Get(code)
+
+ if err != nil {
+ return "", err
+ }
+ return result.URL, nil
+}
+
+func (interactor *ShortURLInteractor) IncrementAccessCount(code string) error {
+ err := interactor.URLRepository.IncrementAccessed(code)
+ return err
}