From 8fcb8c3f235d0b2df89b039151c8ae5004933bc4 Mon Sep 17 00:00:00 2001 From: kreativmonkey Date: Sat, 13 May 2017 00:38:00 +0200 Subject: [PATCH] First Restful API Version --- handlers.go | 73 +++++++++++---- main.go | 15 +--- routs.go | 24 ++++- short/shrt.go | 244 ++++++++++++++++++++++++++------------------------ 4 files changed, 206 insertions(+), 150 deletions(-) diff --git a/handlers.go b/handlers.go index 0c064ad..bd3b88c 100644 --- a/handlers.go +++ b/handlers.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" "text/template" + "net/url" + shorty "github.com/kreativmonkey/shrt/short" ) type Page struct { @@ -13,6 +15,11 @@ type Page struct { Body string } +type response struct { + Action string `json:"action"` + Result interface{} `json:"result"` +} + // Load the main Page with an Imput field for the URL to short. func index(w http.ResponseWriter, r *http.Request) { t, _ := template.ParseFiles("template/index.gohtml") @@ -44,35 +51,63 @@ func shorten(w http.ResponseWriter, r *http.Request){ // GET: http://example.com/api/action/shorten?key=API_KEY_HERE&url=https://google.com&custom_ending=CUSTOM_ENDING // Response: {"action": "shorten","result": "https://example.com/5kq"} func shortenJSON(w http.ResponseWriter, r *http.Request){ - decoder := json.NewDecoder(r.Body) - var response shrt - err := decoder.Decode(&response) - if err != nil { + query := r.URL.Query() + queryu, err := url.QueryUnescape(query.Get("url")) + check(err) + + var test string + err = short.Short(queryu, &test) + check(err) + + respond := response{ + Action: "shorten", + Result: r.Host + "/" + test, + } + + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusOK) + if err := json.NewEncoder(w).Encode(&respond); err != nil { panic(err) } - defer r.Body.Close() - fmt.Println(response) - /*response := map[string]string{ - "action": "shorten", - "result": "", - }*/ + } // GET: http://example.com/api/action/lookup?key=API_KEY_HERE&url_ending=5kq // Response: {"action":"lookup","result": {"long_url": "https:\/\/google.com","created_at": {"date":"2016-02-12 15:20:34.000000","timezone_type":3,"timezone":"UTC"},"clicks":"0"}} -func lookup(w http.ResponseWriter, r *http.Request){ - - /*response := map[string]interface{}{ - "action": "lookup", - "result": map[string]string{ - "long_url": "", - "created_at": "", - "clicks": "", +func lookupJSON(w http.ResponseWriter, r *http.Request){ + query := r.URL.Query() + queryu, err := url.QueryUnescape(query.Get("url_ending")) + check(err) + + var result shorty.Shrt + var respond response + if ok := short.GetAPI(queryu, &result); ok { + respond := response{ + Action: "shorten", + Result: result, } - }*/ + + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusOK) + if err := json.NewEncoder(w).Encode(&respond); err != nil { + panic(err) + } + } else { + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusNotFound) + if err := json.NewEncoder(w).Encode(&respond); err != nil { + panic(err) + } + } } func all(w http.ResponseWriter, r *http.Request){ t, _ := template.New("all").Parse("{{.}}") t.Execute(w, short.All()) +} + +func check(err error) { + if err != nil { + panic(err) + } } \ No newline at end of file diff --git a/main.go b/main.go index b0a9539..3612ae4 100644 --- a/main.go +++ b/main.go @@ -45,19 +45,12 @@ type shrt struct { } func main() { - http.HandleFunc("/", index) // render form for Input - http.HandleFunc("/{token}", redirect) // request token for redirect URL - http.HandleFunc("/shorten", shorten) // render Page for shorten URL - http.HandleFunc("/all", all) // render all in a json - http.HandleFunc("/api/action/shorten", shortenJSON) // setting router rule - http.HandleFunc("/api/action/lookup", lookup) // setting router rule + router := NewRouter() http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("template/css")))) http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("template/img")))) - err := http.ListenAndServe(":"+port, nil) // setting listening port - if err != nil { - log.Fatal("ListenAndServe: ", err) - } - fmt.Printf("Shrt %s started on Port: %s", version, port) + fmt.Printf("Shrt %s started on Port: %s \n", version, port) + log.Fatal(http.ListenAndServe(":"+port, router)) + } diff --git a/routs.go b/routs.go index ff58917..af34e18 100644 --- a/routs.go +++ b/routs.go @@ -37,15 +37,33 @@ var routes = Routes{ index, }, Route{ - "TodoIndex", + "ShortenAPI", + "GET", + "/api/v1/action/shorten", + shortenJSON, + }, + Route{ + "LookupAPI", + "GET", + "/api/v1/action/lookup", + lookupJSON, + }, + Route{ + "GetAll", + "GET", + "/all", + all, + }, + Route{ + "Shorten", "POST", "/shorten", shorten, }, Route{ - "TodoShow", + "Redirect", "GET", "/{token}", - lookup, + redirect, }, } \ No newline at end of file diff --git a/short/shrt.go b/short/shrt.go index 9e5c0ab..2232a31 100644 --- a/short/shrt.go +++ b/short/shrt.go @@ -1,154 +1,164 @@ package shrt import ( - "fmt" - "crypto/sha256" - "errors" - "encoding/json" - "net/url" - "os" - "io" - "bytes" - "time" + "bytes" + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "io" + "net/url" + "os" + "time" ) -type Storage struct { - Token map[string]*shrt `json:"token"` - Url map[string]string `json:"url"` +type Storage struct { + Token map[string]*Shrt `json:"token"` + Url map[string]string `json:"url"` } -type shrt struct { - URL string `json:"url"` - Datum string `json:"datum"` - Count int64 `json:"count"` +type Shrt struct { + URL string `json:"url"` + Date string `json:"datum"` + Clicks int64 `json:"clicks"` } var ( - ErrNotFound = errors.New("Url not Found") - ErrCreateToken = errors.New("There are some problems while creating Token") - ErrNoUrl = errors.New("String is no Url!") + ErrNotFound = errors.New("Url not Found") + ErrCreateToken = errors.New("There are some problems while creating Token") + ErrNoUrl = errors.New("String is no Url!") ) // Open up -func Open(path string) (*Storage, error){ - s := Storage{Token: make(map[string]*shrt), Url: make(map[string]string)} - // Open db ore create if not exist! - if db, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644); err == nil { - json.Unmarshal(StreamToByte(db), &s) - db.Close() - return &s, nil - } else { - return &s, err - } +func Open(path string) (*Storage, error) { + s := Storage{Token: make(map[string]*Shrt), Url: make(map[string]string)} + // Open db ore create if not exist! + if db, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644); err == nil { + json.Unmarshal(StreamToByte(db), &s) + db.Close() + return &s, nil + } else { + return &s, err + } } -func (s *Storage) Short(URL string, value *string) (string, error) { - // Check if it is a valide Url found on: - // http://stackoverflow.com/questions/31480710/validate-url-with-standard-package-in-go - _, err := url.ParseRequestURI(URL) - if err != nil { - return "", ErrNoUrl - } - - // Create a sha256 Hash from the URL - hash := fmt.Sprintf("%x", sha256.Sum256([]byte(URL))) - - // Test if the URL alraedy exist and return the key - if val, ok := s.Url[hash]; ok { - return val, nil - } - // Iterate to the length of hash to get the shortest output - for hashShortestLen := 1; hashShortestLen <= 32; hashShortestLen++ { - // Test if the Token not exist and return the new generated token - if _, ok := s.Token[hash[:hashShortestLen]]; !ok { - token := hash[:hashShortestLen] - t := time.Now() - s.Token[token] = &shrt{ - URL: URL, - Datum: t.String(), - } - s.Url[hash] = token - *value = s.Url[hash] - s.Save() - return token, nil - } - } - - return "", ErrCreateToken +func (s *Storage) Short(URL string, value *string) error { + // Check if it is a valide Url found on: + // http://stackoverflow.com/questions/31480710/validate-url-with-standard-package-in-go + _, err := url.ParseRequestURI(URL) + if err != nil { + return ErrNoUrl + } + + // Create a sha256 Hash from the URL + hash := fmt.Sprintf("%x", sha256.Sum256([]byte(URL))) + + // Test if the URL alraedy exist and return the key + if val, ok := s.Url[hash]; ok { + value = &val + return nil + } + // Iterate to the length of hash to get the shortest output + for hashShortestLen := 1; hashShortestLen <= 32; hashShortestLen++ { + // Test if the Token not exist and return the new generated token + if _, ok := s.Token[hash[:hashShortestLen]]; !ok { + token := hash[:hashShortestLen] + t := time.Now() + s.Token[token] = &Shrt{ + URL: URL, + Date: t.String(), + } + s.Url[hash] = token + *value = s.Url[hash] + s.Save() + return nil + } + } + + return ErrCreateToken } func (s *Storage) Add(URL string, value *string) (string, error) { - // Check if it is a valide Url found on: - // http://stackoverflow.com/questions/31480710/validate-url-with-standard-package-in-go - _, err := url.ParseRequestURI(URL) - if err != nil { - return "", ErrNoUrl - } - - // Create a sha256 Hash from the URL - hash := fmt.Sprintf("%x", sha256.Sum256([]byte(URL))) - - // Test if the URL alraedy exist and return the key - if val, ok := s.Url[hash]; ok { - return val, nil - } - // Iterate to the length of hash to get the shortest output - for hashShortestLen := 1; hashShortestLen <= 32; hashShortestLen++ { - // Test if the Token not exist and return the new generated token - if _, ok := s.Token[hash[:hashShortestLen]]; !ok { - token := hash[:hashShortestLen] - t := time.Now() - s.Token[token] = &shrt{ - URL: URL, - Datum: t.String(), - } - s.Url[hash] = token - *value = s.Url[hash] - s.Save() - return token, nil - } - } - - return "", ErrCreateToken + // Check if it is a valide Url found on: + // http://stackoverflow.com/questions/31480710/validate-url-with-standard-package-in-go + _, err := url.ParseRequestURI(URL) + if err != nil { + return "", ErrNoUrl + } + + // Create a sha256 Hash from the URL + hash := fmt.Sprintf("%x", sha256.Sum256([]byte(URL))) + + // Test if the URL alraedy exist and return the key + if val, ok := s.Url[hash]; ok { + return val, nil + } + // Iterate to the length of hash to get the shortest output + for hashShortestLen := 1; hashShortestLen <= 32; hashShortestLen++ { + // Test if the Token not exist and return the new generated token + if _, ok := s.Token[hash[:hashShortestLen]]; !ok { + token := hash[:hashShortestLen] + t := time.Now() + s.Token[token] = &Shrt{ + URL: URL, + Date: t.String(), + } + s.Url[hash] = token + *value = s.Url[hash] + s.Save() + return token, nil + } + } + + return "", ErrCreateToken } func (s *Storage) Remove(URL string) error { - return nil + return nil } // Get returns the URL for the given token func (s *Storage) Get(token string) (string, bool) { + if shrt, ok := s.Token[token]; ok { + fmt.Printf("Url mit dem Token %s gefunden: %s \n", token, shrt.URL) + s.Token[token].Clicks += 1 + s.Save() + return shrt.URL, true + } + return "", false +} + +// Get returns the URL for the given token +func (s *Storage) GetAPI(token string, value *Shrt) bool { if shrt, ok := s.Token[token]; ok { - fmt.Printf("Url mit dem Token %s gefunden: %s \n", token, shrt.URL) - s.Token[token].Count += 1 - s.Save() - return shrt.URL, true + *value = *shrt + return true } - return "", false + return false } // Get all entries -func (s *Storage) All() (string) { - b, err := json.Marshal(&s) - if err != nil { - return "" - } - return string(b) +func (s *Storage) All() string { + b, err := json.Marshal(&s) + if err != nil { + return "" + } + return string(b) } func (s *Storage) Save() error { - if db, err := os.OpenFile("./test.db", os.O_RDWR|os.O_CREATE, 0644); err == nil { - b, err := json.Marshal(&s) - db.Write(b) - db.Close() - return err - } else { - return err - } + if db, err := os.OpenFile("./test.db", os.O_RDWR|os.O_CREATE, 0644); err == nil { + b, err := json.Marshal(&s) + db.Write(b) + db.Close() + return err + } else { + return err + } } func StreamToByte(stream io.Reader) []byte { - buf := new(bytes.Buffer) - buf.ReadFrom(stream) - return buf.Bytes() + buf := new(bytes.Buffer) + buf.ReadFrom(stream) + return buf.Bytes() }