Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

code review for justforfunc episode #2

Merged
merged 8 commits into from
Sep 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions base62/base62.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package base62

import (
"fmt"
"strings"
)

// All characters
const (
alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
length = int64(len(alphabet))
)

// Encode number to base62.
func Encode(n int64) string {
if n == 0 {
return string(alphabet[0])
}

s := ""
for ; n > 0; n = n / length {
s = string(alphabet[n%length]) + s
}
return s
}

// Decode converts a base62 token to int.
func Decode(key string) (int64, error) {
var n int64
for _, c := range []byte(key) {
i := strings.IndexByte(alphabet, c)
if i < 0 {
return 0, fmt.Errorf("unexpected character %c in base62 literal", c)
}
n = length*n + int64(i)
}
return n, nil
}
75 changes: 27 additions & 48 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,42 @@ package config

import (
"encoding/json"
"os"
"io"
"bytes"
"io/ioutil"
)

// Config contains the configuration of the url shortener.
type Config struct {
Server Server `json:"server"`
Redis Redis `json:"redis"`
Postgres Postgres `json:"postgres"`
Options Options `json:"options"`
Server struct {
Host string `json:"host"`
Port string `json:"port"`
} `json:"server"`
Redis struct {
Host string `json:"host"`
Password string `json:"password"`
DB string `json:"db"`
} `json:"redis"`
Postgres struct {
Host string `json:"host"`
User string `json:"user"`
Password string `json:"password"`
DB string `json:"db"`
} `json:"postgres"`
Options struct {
Prefix string `json:"prefix"`
} `json:"options"`
}

type Server struct {
Host string `json:"host"`
Port string `json:"port"`
}

type Redis struct {
Host string `json:"host"`
Password string `json:"password"`
DB string `json:"db"`
}

type Postgres struct {
Host string `json:"host"`
User string `json:"user"`
Password string `json:"password"`
DB string `json:"db"`
}

type Options struct {
Prefix string `json:"prefix"`
}

func ReadConfig() (*Config, error) {
var objectConfig *Config
var buf bytes.Buffer

// open input file
file, err := os.Open("./config/config.json")
// FromFile returns a configuration parsed from the given file.
func FromFile(path string) (*Config, error) {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}

// close file
defer file.Close()

// copy to buffer
_, err = io.Copy(&buf, file)
if err != nil {
var cfg Config
if err := json.Unmarshal(b, &cfg); err != nil {
return nil, err
}

// Unmarshal data
if err := json.Unmarshal(buf.Bytes(), &objectConfig); err != nil {
return nil, err
}

return objectConfig, nil
}
return &cfg, nil
}
52 changes: 0 additions & 52 deletions enconding/base62.go

This file was deleted.

103 changes: 103 additions & 0 deletions handler/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Package handlers provides HTTP request handlers.
package handler

import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"

"github.com/douglasmakey/ursho/storage"
)

// New returns an http handler for the url shortener.
func New(prefix string, storage storage.Service) http.Handler {
mux := http.NewServeMux()
h := handler{prefix, storage}
mux.HandleFunc("/encode/", responseHandler(h.encode))
mux.HandleFunc("/", h.redirect)
mux.HandleFunc("/info/", responseHandler(h.decode))
return mux
}

type response struct {
Success bool `json:"success"`
Data interface{} `json:"response"`
}

type handler struct {
prefix string
storage storage.Service
}

func responseHandler(h func(io.Writer, *http.Request) (interface{}, int, error)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data, status, err := h(w, r)
if err != nil {
data = err.Error()
}
w.WriteHeader(status)
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(response{Data: data, Success: err == nil})
if err != nil {
log.Printf("could not encode response to output: %v", err)
Copy link

@vieiralucas vieiralucas Sep 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't status become 500 if it fails to encode?

}
}
}

func (h handler) encode(w io.Writer, r *http.Request) (interface{}, int, error) {
if r.Method != http.MethodPost {
return nil, http.StatusMethodNotAllowed, fmt.Errorf("method %s not allowed", r.Method)
}

var input struct{ URL string }
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
return nil, http.StatusBadRequest, fmt.Errorf("Unable to decode JSON request body: %v", err)
}

url := strings.TrimSpace(input.URL)
if url == "" {
return nil, http.StatusBadRequest, fmt.Errorf("URL is empty")
}

c, err := h.storage.Save(url)
if err != nil {
return nil, http.StatusBadRequest, fmt.Errorf("Could not store in database: %v", err)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If database fails, it's not users fault.
Shouldn't this be sending 500?

}

return h.prefix + c, http.StatusCreated, nil
}

func (h handler) decode(w io.Writer, r *http.Request) (interface{}, int, error) {
if r.Method != http.MethodGet {
return nil, http.StatusMethodNotAllowed, fmt.Errorf("Method %s not allowed", r.Method)
}

code := r.URL.Path[len("/info/"):]

model, err := h.storage.LoadInfo(code)
if err != nil {
return nil, http.StatusNotFound, fmt.Errorf("URL not found")
}

return model, http.StatusOK, nil
}

func (h handler) redirect(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
code := r.URL.Path[len("/"):]

model, err := h.storage.Load(code)
if err != nil {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("URL Not Found"))
return
}

http.Redirect(w, r, string(model.URL), http.StatusMovedPermanently)
}
39 changes: 0 additions & 39 deletions handlers/base.go

This file was deleted.

Loading