diff --git a/cmd/hub/main.go b/cmd/hub/main.go index 5771c7e..3420936 100644 --- a/cmd/hub/main.go +++ b/cmd/hub/main.go @@ -15,15 +15,13 @@ import ( // main is the entry point of the application func main() { var ( - dataPath = "data/heros.json" // store to JSON file + dataPath = "data/heros.json" // Path to read superheros from JSON file port = 8080 // Port to serve - key = 5 // DeeSee encryption key - err error + key = 5 // Json encryption key + logger = log.Default() // Logger + err error // Error ) - // Create logger - logger := log.Default() - // Get port from environment variable if os.Getenv("PORT") != "" { port, err = strconv.Atoi(os.Getenv("PORT")) @@ -54,8 +52,8 @@ func main() { logger.Fatalf("Invalid data path: %s", dataPath) } - // Create DeeSee storage - s, err := storage.New(dataPath) + // Create Json storage + s, err := storage.NewJson(dataPath) if err != nil { logger.Fatalf("Invalid data format: %s", dataPath) } diff --git a/internal/router/router.go b/internal/router/router.go index 79256f0..37e84fa 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -3,7 +3,6 @@ package router import ( "encoding/json" "github.com/eslider/superherohub/pkg/deesee" - "github.com/eslider/superherohub/pkg/deesee/storage" "log" "net/http" "strings" @@ -13,10 +12,18 @@ import ( // Router is a custom router that encapsulates the mux.Router type Router struct { - store *storage.DeeSee // The store where superheroes are stored - router *mux.Router // Encapsulated mux router - logger *log.Logger // The logger to use for this router - key int // The key to use for DeeSee encryption + store Storage // The store where superheroes are stored + router *mux.Router // Encapsulated mux router + logger *log.Logger // The logger to use for this router + key int // The key to use for DeeSee encryption +} + +type Storage interface { + // List of superheroes + List() []*deesee.Superhero + + // Put stores a superhero in the store. + Put(*deesee.Superhero) error } // New creates a new encapsulated router. @@ -30,14 +37,14 @@ type Router struct { // require different resources and development progresses. // // And handlers shouldn't have prefix like "handle", course they are methods of the Router -func New(heros *storage.DeeSee, key int, logger *log.Logger) *Router { +func New(store Storage, key int, logger *log.Logger) *Router { // Set the logger if logger == nil { logger = log.Default() } r := &Router{ - store: heros, + store: store, key: key, logger: logger, router: mux.NewRouter(), @@ -90,7 +97,7 @@ func (r *Router) GetHandler() http.Handler { func (r *Router) GetSuperHeroes(w http.ResponseWriter, req *http.Request) { var ( // Resulting - heroes = *r.store + heroes = r.store.List() // Get query parameter values params = req.URL.Query() err error @@ -134,8 +141,8 @@ func (r *Router) StoreSuperHero(w http.ResponseWriter, req *http.Request) { return } - // Store superhero - if err = r.store.Store(hero); err != nil { + // Put superhero + if err = r.store.Put(hero); err != nil { http.Error(w, err.Error(), http.StatusConflict) return } diff --git a/internal/router/router_test.go b/internal/router/router_test.go index 374442a..ab17757 100644 --- a/internal/router/router_test.go +++ b/internal/router/router_test.go @@ -20,7 +20,7 @@ func TestWebserviceGetSuperHeros(t *testing.T) { } // Load heros - heros, err := storage.New(path + "/data/heros.json") + heros, err := storage.NewJson(path + "/data/heros.json") if err != nil { t.Fatalf("Error loading heros: %heros", err) @@ -128,7 +128,7 @@ func TestWebserviceStoreSuperhero(t *testing.T) { } // Load heros - heros, err := storage.New(path + "/data/heros.json") + heros, err := storage.NewJson(path + "/data/heros.json") if err != nil { t.Fatalf("Error loading heros: %heros", err) diff --git a/pkg/deesee/storage/json.go b/pkg/deesee/storage/json.go new file mode 100644 index 0000000..5ee9ca2 --- /dev/null +++ b/pkg/deesee/storage/json.go @@ -0,0 +1,65 @@ +package storage + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/eslider/superherohub/pkg/deesee" + "os" + "strings" +) + +// Json simple JSON memory storage. +type Json struct { + path string // Path to json file + list []*deesee.Superhero // List of loaded superheroes +} + +// NewJson Json storage. +func NewJson(path string) (d *Json, err error) { + d = &Json{ + path: path, + } + err = d.Load(path) + return +} + +// Load superheroes from JSON file. +func (s *Json) Load(path string) error { + f, err := os.OpenFile(path, os.O_RDONLY, 0644) + if err != nil { + return fmt.Errorf("unable to read f: %w", err) + } + + return json.NewDecoder(f).Decode(&s.list) +} + +// List superheros +func (s *Json) List() []*deesee.Superhero { + return s.list +} + +// Put superhero. +func (s *Json) Put(hero *deesee.Superhero) (err error) { + // Check if superhero superpower is acceptable + if !hero.IsAcceptable() { + return errors.New(fmt.Sprintf("Hero power is not acceptable: %s", hero.Name)) + } + + // Prevent to store duplicate superheroes + if deesee.FindByName(s.list, strings.TrimSpace(hero.Name)) != nil { + return errors.New(fmt.Sprintf("Hero already exists: %s", hero.Name)) + } + + s.list = append(s.list, hero) + return +} + +// Persist superheroes to JSON file. +func (s *Json) Persist() error { + f, err := os.OpenFile(s.path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + return fmt.Errorf("unable to write file: %w", err) + } + return json.NewEncoder(f).Encode(s.list) +} diff --git a/pkg/deesee/storage/storage.go b/pkg/deesee/storage/storage.go deleted file mode 100644 index 782b0e5..0000000 --- a/pkg/deesee/storage/storage.go +++ /dev/null @@ -1,46 +0,0 @@ -package storage - -import ( - "encoding/json" - "errors" - "fmt" - "github.com/eslider/superherohub/pkg/deesee" - "os" - "strings" -) - -type DeeSee []*deesee.Superhero - -// New DeeSee storage. -func New(path string) (d *DeeSee, err error) { - d = &DeeSee{} - err = d.Load(path) - return -} - -// Load superheroes from json file -func (s *DeeSee) Load(path string) (err error) { - // Read and unmarshal s from json f path - f, err := os.OpenFile(path, os.O_RDONLY, 0644) - if err != nil { - return fmt.Errorf("unable to read f: %w", err) - } - - return json.NewDecoder(f).Decode(s) -} - -// Store superhero -func (s *DeeSee) Store(hero *deesee.Superhero) (err error) { - // Check if superhero superpower is acceptable - if !hero.IsAcceptable() { - return errors.New(fmt.Sprintf("Hero power is not acceptable: %s", hero.Name)) - } - - // Prevent to store duplicate superheroes - if deesee.FindByName(*s, strings.TrimSpace(hero.Name)) != nil { - return errors.New(fmt.Sprintf("Hero already exists: %s", hero.Name)) - } - - *s = append(*s, hero) - return -}