From c91f0920a056d4f6732ba802b34c8f940020a08f Mon Sep 17 00:00:00 2001 From: Johnny Steenbergen Date: Sat, 13 Jan 2024 21:26:41 -0600 Subject: [PATCH] chore(allsrv): refactor DB interface out of in-mem database type This paves the way for more interesting additions. With this DB interface in place, can you add metrics for the datastore without futzing with the in-mem database implmementation? Add/update tests to verify the db still exhibits the same behavior. --- allsrv/db_inmem.go | 51 +++++++++++++++++++++++++++++++++ allsrv/server.go | 70 +++++++++++----------------------------------- 2 files changed, 68 insertions(+), 53 deletions(-) create mode 100644 allsrv/db_inmem.go diff --git a/allsrv/db_inmem.go b/allsrv/db_inmem.go new file mode 100644 index 0000000..29d6093 --- /dev/null +++ b/allsrv/db_inmem.go @@ -0,0 +1,51 @@ +package allsrv + +import ( + "errors" +) + +// InmemDB is an in-memory store. +type InmemDB struct { + m []Foo // 12) +} + +func (db *InmemDB) CreateFoo(f Foo) error { + for _, existing := range db.m { + if f.Name == existing.Name { + return errors.New("foo " + f.Name + " exists") // 8) + } + } + + db.m = append(db.m, f) + + return nil +} + +func (db *InmemDB) ReadFoo(id string) (Foo, error) { + for _, f := range db.m { + if id == f.ID { + return f, nil + } + } + return Foo{}, errors.New("foo not found for id: " + id) // 8) +} + +func (db *InmemDB) UpdateFoo(f Foo) error { + for i, existing := range db.m { + if f.ID == existing.ID { + db.m[i] = f + return nil + } + } + return errors.New("foo not found for id: " + f.ID) // 8) +} + +func (db *InmemDB) DelFoo(id string) error { + for i, f := range db.m { + if id == f.ID { + db.m = append(db.m[:i], db.m[i+1:]...) + return nil // 13) + } + } + return errors.New("foo not found for id: " + id) // 8) +} diff --git a/allsrv/server.go b/allsrv/server.go index 6dd45d4..a36fff7 100644 --- a/allsrv/server.go +++ b/allsrv/server.go @@ -2,7 +2,6 @@ package allsrv import ( "encoding/json" - "errors" "log" "net/http" @@ -12,7 +11,7 @@ import ( /* Concerns: - 1) the server depends on a hard type, coupling to the exact inmem db + ✅1) the server depends on a hard type, coupling to the exact inmem db a) what happens if we want a different db? ✅2) auth is copy-pasted in each handler a) what happens if we forget that copy pasta? @@ -45,8 +44,19 @@ import ( 4) is trivial in scope */ +// Server dependencies +type ( + // DB represents the foo persistence layer. + DB interface { + CreateFoo(f Foo) error + ReadFoo(id string) (Foo, error) + UpdateFoo(f Foo) error + DelFoo(id string) error + } +) + type Server struct { - db *InmemDB // 1) + db DB // 1) mux *http.ServeMux // 4) authFn func(http.Handler) http.Handler // 3) @@ -68,7 +78,7 @@ func WithIDFn(fn func() string) func(*Server) { } } -func NewServer(db *InmemDB, opts ...func(*Server)) *Server { +func NewServer(db DB, opts ...func(*Server)) *Server { s := Server{ db: db, mux: http.NewServeMux(), // 4) @@ -134,7 +144,7 @@ func (s *Server) createFoo(w http.ResponseWriter, r *http.Request) { } func (s *Server) readFoo(w http.ResponseWriter, r *http.Request) { - f, err := s.db.readFoo(r.URL.Query().Get("id")) + f, err := s.db.ReadFoo(r.URL.Query().Get("id")) if err != nil { w.WriteHeader(http.StatusNotFound) // 9) return @@ -152,14 +162,14 @@ func (s *Server) updateFoo(w http.ResponseWriter, r *http.Request) { return } - if err := s.db.updateFoo(f); err != nil { + if err := s.db.UpdateFoo(f); err != nil { w.WriteHeader(http.StatusInternalServerError) // 9) return } } func (s *Server) delFoo(w http.ResponseWriter, r *http.Request) { - if err := s.db.delFoo(r.URL.Query().Get("id")); err != nil { + if err := s.db.DelFoo(r.URL.Query().Get("id")); err != nil { w.WriteHeader(http.StatusNotFound) // 9) return } @@ -178,49 +188,3 @@ func basicAuth(expectedUser, expectedPass string) func(http.Handler) http.Handle }) } } - -// InmemDB is an in-memory store. -type InmemDB struct { - m []Foo // 12) -} - -func (db *InmemDB) CreateFoo(f Foo) error { - for _, existing := range db.m { - if f.Name == existing.Name { - return errors.New("foo " + f.Name + " exists") // 8) - } - } - - db.m = append(db.m, f) - - return nil -} - -func (db *InmemDB) readFoo(id string) (Foo, error) { - for _, f := range db.m { - if id == f.ID { - return f, nil - } - } - return Foo{}, errors.New("foo not found for id: " + id) // 8) -} - -func (db *InmemDB) updateFoo(f Foo) error { - for i, existing := range db.m { - if f.ID == existing.ID { - db.m[i] = f - return nil - } - } - return errors.New("foo not found for id: " + f.ID) // 8) -} - -func (db *InmemDB) delFoo(id string) error { - for i, f := range db.m { - if id == f.ID { - db.m = append(db.m[:i], db.m[i+1:]...) - return nil // 13) - } - } - return errors.New("foo not found for id: " + id) // 8) -}