diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index c03e5ef..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index 4c49bd7..3323b34 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .env +.DS_Store diff --git a/bookish-server/Dockerfile b/bookish-server/Dockerfile index 3bb37e4..0d7b5b1 100644 --- a/bookish-server/Dockerfile +++ b/bookish-server/Dockerfile @@ -6,4 +6,4 @@ COPY . . RUN go get -d -v ./... RUN go install -v ./... -CMD ["api"] \ No newline at end of file +CMD ["api"] diff --git a/bookish-server/cmd/api/main.go b/bookish-server/cmd/api/main.go index e13f549..3f12252 100644 --- a/bookish-server/cmd/api/main.go +++ b/bookish-server/cmd/api/main.go @@ -10,9 +10,10 @@ import ( "time" "github.com/davidlick/bookish/bookish-server/cmd/http" - "github.com/davidlick/bookish/bookish-server/inventory" "github.com/davidlick/bookish/bookish-server/mysql" + "github.com/davidlick/bookish/bookish-server/rental" "github.com/davidlick/bookish/bookish-server/renter" + _ "github.com/go-sql-driver/mysql" "github.com/sirupsen/logrus" ) @@ -43,7 +44,7 @@ func init() { func main() { // Connect to application database and create store. - appDSN := fmt.Sprintf("%s:%s@tcp(%s:%s)/bookish?parseTime=True&loc=Local", + appDSN := fmt.Sprintf("mysql://%s:%s@tcp(%s:%s)/bookish?parseTime=true&loc=local&multiStatements=true", cfg.DBUser, cfg.DBPass, cfg.DBHost, @@ -56,15 +57,15 @@ func main() { // Build domain services. renterService := renter.NewService(appDB) - inventoryService := inventory.NewService(appDB) + rentalService := rental.NewService(appDB) // Initialize server. server := http.Server{ Port: cfg.APIPort, BooksHost: cfg.BooksHost, Logger: logger, - Renter: renterService, - Inventory: inventoryService, + Renters: renterService, + Rentals: rentalService, } // Create channels to listen for OS signals. diff --git a/bookish-server/cmd/http/http.go b/bookish-server/cmd/http/http.go index 71e0e90..5da8326 100644 --- a/bookish-server/cmd/http/http.go +++ b/bookish-server/cmd/http/http.go @@ -9,7 +9,7 @@ import ( "time" bookish "github.com/davidlick/bookish/bookish-server" - "github.com/davidlick/bookish/bookish-server/inventory" + "github.com/davidlick/bookish/bookish-server/rental" "github.com/davidlick/bookish/bookish-server/renter" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" @@ -21,8 +21,8 @@ type Server struct { Port string BooksHost string Logger *logrus.Logger - Renter renter.Service - Inventory inventory.Service + Renters renter.Service + Rentals rental.Service // LibraryMap holds a map of values that are returned by the books API. This is used to check // if a book exists in the library. LibraryMap struct { diff --git a/bookish-server/cmd/http/middleware.go b/bookish-server/cmd/http/middleware.go index f05b196..78a13a0 100644 --- a/bookish-server/cmd/http/middleware.go +++ b/bookish-server/cmd/http/middleware.go @@ -6,7 +6,6 @@ import ( "errors" "io/ioutil" "net/http" - "strconv" "github.com/davidlick/bookish/bookish-server/internal" "github.com/davidlick/bookish/bookish-server/mysql" @@ -36,13 +35,7 @@ func (s *Server) renterCtx(next http.Handler) http.Handler { ctx := r.Context() id := chi.URLParam(r, "renterId") - renterId, err := strconv.Atoi(id) - if err != nil { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - return - } - - renter, err := s.Renter.FetchRenter(renterId) + renter, err := s.Renters.FetchRenter(id) if err != nil && errors.Is(mysql.ErrNotFound, err) { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return diff --git a/bookish-server/cmd/http/inventory_handlers.go b/bookish-server/cmd/http/rental_handlers.go similarity index 64% rename from bookish-server/cmd/http/inventory_handlers.go rename to bookish-server/cmd/http/rental_handlers.go index ce049ca..98c53c0 100644 --- a/bookish-server/cmd/http/inventory_handlers.go +++ b/bookish-server/cmd/http/rental_handlers.go @@ -2,12 +2,13 @@ package http import ( "errors" - "fmt" "net/http" bookish "github.com/davidlick/bookish/bookish-server" "github.com/davidlick/bookish/bookish-server/internal" - "github.com/davidlick/bookish/bookish-server/inventory" + "github.com/davidlick/bookish/bookish-server/mysql" + "github.com/davidlick/bookish/bookish-server/rental" + "github.com/gobuffalo/uuid" ) // checkoutBook is used to checkout a book for a renter. @@ -25,10 +26,16 @@ func (s *Server) checkoutBook(w http.ResponseWriter, r *http.Request) { return } - err := s.Inventory.CheckoutBook(renter.ID, bookTitle) + id, err := uuid.FromString(renter.ID) if err != nil { - if errors.Is(inventory.ErrUnavailableBook, err) { - http.Error(w, err.Error(), http.StatusForbidden) + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } + + err = s.Rentals.CheckoutBook(id, bookTitle) + if err != nil { + if errors.Is(rental.ErrUnavailableBook, err) { + http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) return } @@ -52,9 +59,17 @@ func (s *Server) returnBook(w http.ResponseWriter, r *http.Request) { return } - err := s.Inventory.ReturnBook(renter.ID, bookTitle) + id, err := uuid.FromString(renter.ID) if err != nil { - fmt.Println(err) + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } + + err = s.Rentals.ReturnBook(id, bookTitle) + if err != nil && errors.Is(mysql.ErrNotFound, err) { + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } else if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } diff --git a/bookish-server/cmd/http/renter_handlers.go b/bookish-server/cmd/http/renter_handlers.go index 3117d00..4c504b6 100644 --- a/bookish-server/cmd/http/renter_handlers.go +++ b/bookish-server/cmd/http/renter_handlers.go @@ -7,6 +7,7 @@ import ( bookish "github.com/davidlick/bookish/bookish-server" "github.com/davidlick/bookish/bookish-server/internal" + "github.com/gofrs/uuid" ) // registerRenter creates a record of a new renter. @@ -24,14 +25,14 @@ func (s *Server) registerRenter(w http.ResponseWriter, r *http.Request) { return } - id, err := s.Renter.RegisterRenter(renter.Name, renter.Address, renter.Email, renter.PhoneNumber) + id, err := s.Renters.RegisterRenter(renter.Name, renter.Address, renter.Email, renter.PhoneNumber) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } resp := struct { - ID int `json:"renterId"` + ID uuid.UUID `json:"renterId"` }{ID: id} json.NewEncoder(w).Encode(resp) @@ -39,7 +40,7 @@ func (s *Server) registerRenter(w http.ResponseWriter, r *http.Request) { // listRenters lists all renters registered in the API. func (s *Server) listRenters(w http.ResponseWriter, r *http.Request) { - rr, err := s.Renter.ListRenters() + rr, err := s.Renters.ListRenters() if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return diff --git a/bookish-server/config/database.yml b/bookish-server/config/database.yml new file mode 100644 index 0000000..8ce9509 --- /dev/null +++ b/bookish-server/config/database.yml @@ -0,0 +1,6 @@ +development: + dialect: mysql + database: bookish + user: root + password: 123pass + host: db diff --git a/bookish-server/go.mod b/bookish-server/go.mod index 98097e2..0f1acfa 100644 --- a/bookish-server/go.mod +++ b/bookish-server/go.mod @@ -3,13 +3,28 @@ module github.com/davidlick/bookish/bookish-server go 1.14 require ( + github.com/cockroachdb/cockroach-go v0.0.0-20200528011702-6cc4ff37a11e // indirect + github.com/fatih/color v1.9.0 // indirect github.com/go-chi/chi v4.1.1+incompatible github.com/go-sql-driver/mysql v1.5.0 + github.com/gobuffalo/envy v1.9.0 // indirect + github.com/gobuffalo/fizz v1.9.10 // indirect + github.com/gobuffalo/flect v0.2.1 // indirect + github.com/gobuffalo/genny v0.6.0 // indirect + github.com/gobuffalo/nulls v0.4.0 + github.com/gobuffalo/packd v1.0.0 // indirect + github.com/gobuffalo/pop v4.13.1+incompatible + github.com/gobuffalo/pop/v5 v5.1.3 + github.com/gobuffalo/uuid v2.0.5+incompatible + github.com/gobuffalo/validate v2.0.4+incompatible // indirect + github.com/gobuffalo/validate/v3 v3.1.0 + github.com/gofrs/uuid v3.3.0+incompatible github.com/golang/mock v1.4.3 github.com/jmoiron/sqlx v1.2.0 github.com/kelseyhightower/envconfig v1.4.0 + github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.6.0 - github.com/stretchr/testify v1.5.1 - golang.org/x/net v0.0.0-20190603091049-60506f45cf65 // indirect + github.com/stretchr/testify v1.6.0 + golang.org/x/net v0.0.0-20200528225125-3c3fba18258b // indirect golang.org/x/text v0.3.2 // indirect ) diff --git a/bookish-server/go.sum b/bookish-server/go.sum index 067d4d2..a818924 100644 --- a/bookish-server/go.sum +++ b/bookish-server/go.sum @@ -1,41 +1,301 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14= +github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= +github.com/cockroachdb/cockroach-go v0.0.0-20200528011702-6cc4ff37a11e h1:U/LmtPG8RXOORWPJbJWJeSMW1DrnpkovQTuW/Rya/SA= +github.com/cockroachdb/cockroach-go v0.0.0-20200528011702-6cc4ff37a11e/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidlick/bookish v0.0.0-20200529020827-a06b4d70abb9 h1:4xzRnQvEl1tG3ypktRZHO6zoifkZefklaiBv4e1cTZc= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-chi/chi v4.1.1+incompatible h1:MmTgB0R8Bt/jccxp+t6S/1VGIKdJw5J74CK/c9tTfA4= github.com/go-chi/chi v4.1.1+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.1.0/go.mod h1:fmNpaWyHM0tRm8gCZWKx8yY9fvaNLo2PyzBNSrBZ5Hw= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/envy v1.8.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE= +github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/fizz v1.9.8/go.mod h1:w1FEn1yKNVCc49KnADGyYGRPH7jFON3ak4Bj1yUudHo= +github.com/gobuffalo/fizz v1.9.10 h1:B/p28Qy2VxFq6zr1EXWuxwI4dl0PUA/xW2dY+XdaDMY= +github.com/gobuffalo/fizz v1.9.10/go.mod h1:J2XGPO0AfJ1zKw7+2BA+6FEGAkyEsdCOLvN93WCT2WI= +github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= +github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= +github.com/gobuffalo/flect v0.2.1 h1:GPoRjEN0QObosV4XwuoWvSd5uSiL0N3e91/xqyY4crQ= +github.com/gobuffalo/flect v0.2.1/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= +github.com/gobuffalo/genny v0.2.0/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.3.0/go.mod h1:ywJ2CoXrTZj7rbS8HTbzv7uybnLKlsNSBhEQ+yFI3E8= +github.com/gobuffalo/genny v0.6.0 h1:d7c6d66ZrTHHty01hDX1/TcTWvAJQxRZl885KWX5kHY= +github.com/gobuffalo/genny v0.6.0/go.mod h1:Vigx9VDiNscYpa/LwrURqGXLSIbzTfapt9+K6gF1kTA= +github.com/gobuffalo/genny/v2 v2.0.5/go.mod h1:kRkJuAw9mdI37AiEYjV4Dl+TgkBDYf8HZVjLkqe5eBg= +github.com/gobuffalo/github_flavored_markdown v1.0.7/go.mod h1:w93Pd9Lz6LvyQXEG6DktTPHkOtCbr+arAD5mkwMzXLI= +github.com/gobuffalo/github_flavored_markdown v1.1.0 h1:8Zzj4fTRl/OP2R7sGerzSf6g2nEJnaBEJe7UAOiEvbQ= +github.com/gobuffalo/github_flavored_markdown v1.1.0/go.mod h1:TSpTKWcRTI0+v7W3x8dkSKMLJSUpuVitlptCkpeY8ic= +github.com/gobuffalo/gogen v0.2.0/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/helpers v0.2.2/go.mod h1:xYbzUdCUpVzLwLnqV8HIjT6hmG0Cs7YIBCJkNM597jw= +github.com/gobuffalo/helpers v0.2.4/go.mod h1:NX7v27yxPDOPTgUFYmJ5ow37EbxdoLraucOGvMNawyk= +github.com/gobuffalo/helpers v0.5.0/go.mod h1:stpgxJ2C7T99NLyAxGUnYMM2zAtBk5NKQR0SIbd05j4= +github.com/gobuffalo/helpers v0.6.0 h1:CL1xOSGeKCaKD1IUpo4RfrkDU83kmkMG4H3dXAS7dw0= +github.com/gobuffalo/helpers v0.6.0/go.mod h1:pncVrer7x/KRvnL5aJABLAuT/RhKRR9klL6dkUOhyv8= +github.com/gobuffalo/helpers v0.6.1 h1:LLcL4BsiyDQYtMRUUpyFdBFvFXQ6hNYOpwrcYeilVWM= +github.com/gobuffalo/helpers v0.6.1/go.mod h1:wInbDi0vTJKZBviURTLRMFLE4+nF2uRuuL2fnlYo7w4= +github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc= +github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.1.0/go.mod h1:pqQ1XAqvpy/JYtRwoieNps2yU8MFiMxBUpAm2FBtQ50= +github.com/gobuffalo/nulls v0.2.0/go.mod h1:w4q8RoSCEt87Q0K0sRIZWYeIxkxog5mh3eN3C/n+dUc= +github.com/gobuffalo/nulls v0.4.0 h1:xi+JHGWIetYqLmS520dSWc8Ifj1P0aNXKTVDMVsPXmw= +github.com/gobuffalo/nulls v0.4.0/go.mod h1:2KmsoLnMrxpwPLN5LmBbm6tmttHSIZr/v/OdGsATM3M= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.2.0/go.mod h1:k2CkHP3bjbqL2GwxwhxUy1DgnlbW644hkLC9iIUvZwY= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= +github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= +github.com/gobuffalo/packr/v2 v2.4.0/go.mod h1:ra341gygw9/61nSjAbfwcwh8IrYL4WmR4IsPkPBhQiY= +github.com/gobuffalo/packr/v2 v2.5.2/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08= +github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= +github.com/gobuffalo/plush v3.8.2+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plush v3.8.3+incompatible h1:kzvUTnFPhwyfPEsx7U7LI05/IIslZVGnAlMA1heWub8= +github.com/gobuffalo/plush v3.8.3+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= +github.com/gobuffalo/plush/v4 v4.0.0 h1:ZHdmfr2R7DQ77XzWZK2PGKJOXm9NRy21EZ6Rw7FhuNw= +github.com/gobuffalo/plush/v4 v4.0.0/go.mod h1:ErFS3UxKqEb8fpFJT7lYErfN/Nw6vHGiDMTjxpk5bQ0= +github.com/gobuffalo/plushgen v0.1.2/go.mod h1:3U71v6HWZpVER1nInTXeAwdoRNsRd4W8aeIa1Lyp+Bk= +github.com/gobuffalo/pop v4.13.1+incompatible h1:AhbqPxNOBN/DBb2DBaiBqzOXIBQXxEYzngHHJ+ytP4g= +github.com/gobuffalo/pop v4.13.1+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg= +github.com/gobuffalo/pop/v5 v5.1.3 h1:XwXm2sJScNqc6dJOaMTAILECw6nqsf3cooNdleEcoWw= +github.com/gobuffalo/pop/v5 v5.1.3/go.mod h1:fzUpBhQE48+kPczDbuJIuOCSS7OMqChoaGR6wj2j7Nc= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobuffalo/tags v2.1.0+incompatible/go.mod h1:9XmhOkyaB7UzvuY4UoZO4s67q8/xRMVJEaakauVQYeY= +github.com/gobuffalo/tags v2.1.7+incompatible h1:GUxxh34f9SI4U0Pj3ZqvopO9SlzuqSf+g4ZGSPSszt4= +github.com/gobuffalo/tags v2.1.7+incompatible/go.mod h1:9XmhOkyaB7UzvuY4UoZO4s67q8/xRMVJEaakauVQYeY= +github.com/gobuffalo/tags/v3 v3.0.2 h1:gxE6c6fA5radwQeg59aPIeYgCG8YA8AZd3Oh6fh5UXA= +github.com/gobuffalo/tags/v3 v3.0.2/go.mod h1:ZQeN6TCTiwAFnS0dNcbDtSgZDwNKSpqajvVtt6mlYpA= +github.com/gobuffalo/tags/v3 v3.1.0 h1:mzdCYooN2VsLRr8KIAdEZ1lh1Py7JSMsiEGCGata2AQ= +github.com/gobuffalo/tags/v3 v3.1.0/go.mod h1:ZQeN6TCTiwAFnS0dNcbDtSgZDwNKSpqajvVtt6mlYpA= +github.com/gobuffalo/uuid v2.0.5+incompatible h1:c5uWRuEnYggYCrT9AJm0U2v1QTG7OVDAvxhj8tIV5Gc= +github.com/gobuffalo/uuid v2.0.5+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= +github.com/gobuffalo/validate v1.0.0 h1:Xdf2irctxenMaXZrgA13Aeo1sUD9+sgaKKVOaMauZ2U= +github.com/gobuffalo/validate v2.0.3+incompatible/go.mod h1:N+EtDe0J8252BgfzQUChBgfd6L93m9weay53EWFVsMM= +github.com/gobuffalo/validate v2.0.4+incompatible h1:ZTxozrIw8qQ5nfhShmc4izjYPTsPhfdXTdhXOd5OS9o= +github.com/gobuffalo/validate v2.0.4+incompatible/go.mod h1:N+EtDe0J8252BgfzQUChBgfd6L93m9weay53EWFVsMM= +github.com/gobuffalo/validate/v3 v3.0.0 h1:dF7Bg8NMF9Zv8bZvUMXYJXxZdj+eSZ8z/lGM7/jVFUE= +github.com/gobuffalo/validate/v3 v3.0.0/go.mod h1:HFpjq+AIiA2RHoQnQVTFKF/ZpUPXwyw82LgyDPxQ9r0= +github.com/gobuffalo/validate/v3 v3.1.0 h1:/QQN920PciCfBs3aywtJTvDTHmBFMKoiwkshUWa/HLQ= +github.com/gobuffalo/validate/v3 v3.1.0/go.mod h1:HFpjq+AIiA2RHoQnQVTFKF/ZpUPXwyw82LgyDPxQ9r0= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84= +github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.3.2/go.mod h1:LvCquS3HbBKwgl7KbX9KyqEIumJAbm1UMcTvGaIf3bM= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY= +github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= +github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9Ev6lojP2XaIshpT4ymkqhMeSghO5Ps00E= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= +github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200219183655-46282727080f/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200528225125-3c3fba18258b h1:IYiJPiJfzktmDAO1HQiwjMjwjlYKHAL7KzeD544RJPs= +golang.org/x/net v0.0.0-20200528225125-3c3fba18258b/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -43,9 +303,37 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190613204242-ed0dc450797f/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191224055732-dd894d0a8a40/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/bookish-server/models/rental.go b/bookish-server/models/rental.go new file mode 100644 index 0000000..957515c --- /dev/null +++ b/bookish-server/models/rental.go @@ -0,0 +1,19 @@ +package models + +import ( + "time" + + "github.com/gobuffalo/nulls" + "github.com/gobuffalo/uuid" +) + +type Rental struct { + ID uuid.UUID `db:"id"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` + BookTitle string `db:"book_title"` + RenterID uuid.UUID `db:"renter_id"` + RentalDate time.Time `db:"rental_date"` + ReturnDate nulls.Time `db:"return_date"` + Renter *Renter `belongs_to:"renter"` +} diff --git a/bookish-server/models/renter.go b/bookish-server/models/renter.go new file mode 100644 index 0000000..2a51424 --- /dev/null +++ b/bookish-server/models/renter.go @@ -0,0 +1,18 @@ +package models + +import ( + "time" + + "github.com/gobuffalo/uuid" +) + +type Renter struct { + ID uuid.UUID `json:"id" db:"id"` + CreatedAt time.Time `json:"-" db:"created_at"` + UpdatedAt time.Time `json:"-" db:"updated_at"` + Name string `json:"name" db:"name"` + Address string `json:"address" db:"address"` + Email string `json:"email" db:"email"` + PhoneNumber string `json:"phoneNumber" db:"phone_number"` + Rentals []Rental `json:"rentals" has_many:"rentals"` +} diff --git a/bookish-server/mysql/errors.go b/bookish-server/mysql/errors.go index ac783b7..940c67b 100644 --- a/bookish-server/mysql/errors.go +++ b/bookish-server/mysql/errors.go @@ -5,6 +5,6 @@ import ( ) var ( - ErrNotFound = errors.New("no renter found") + ErrNotFound = errors.New("empty result") ErrAlreadyExists = errors.New("the provided value already exists") ) diff --git a/bookish-server/mysql/inventory.go b/bookish-server/mysql/inventory.go deleted file mode 100644 index afb97db..0000000 --- a/bookish-server/mysql/inventory.go +++ /dev/null @@ -1,111 +0,0 @@ -package mysql - -import ( - "database/sql" - "errors" - "time" -) - -// IsBookAvailable queries the database for available books. -func (s *store) IsBookAvailable(title string) (available bool, err error) { - q := ` - SELECT - * - FROM - rentals - WHERE - book_title = ? - AND return_date IS NULL; - ` - - var rr []struct { - ID int `db:"id"` - RenterID int `db:"renter_id"` - RentalDate time.Time `db:"rental_date"` - ReturnDate sql.NullTime `db:"return_date"` - BookTitle string `db:"book_title"` - } - - err = s.Select(&rr, q, title) - if err != nil { - // If no records are returned then the book is available for rent. - if errors.Is(sql.ErrNoRows, err) { - return true, nil - } - - return false, err - } - - // If 5 books are rented out this book is not available. - if len(rr) > 4 { - return false, nil - } - - return true, nil -} - -// RenterAlreadyCheckedOut queries the database for the given renterId and title. If a record is returned checkedOut will be true. -func (s *store) RenterAlreadyCheckedOut(renterId int, title string) (checkedOut bool, err error) { - q := ` - SELECT - * - FROM - rentals - WHERE - renter_id = ? - AND book_title = ? - AND return_date IS NULL; - ` - - var rr []struct { - ID int `db:"id"` - RenterID int `db:"renter_id"` - RentalDate time.Time `db:"rental_date"` - ReturnDate sql.NullTime `db:"return_date"` - BookTitle string `db:"book_title"` - } - - err = s.Select(&rr, q, renterId, title) - if err != nil { - if errors.Is(sql.ErrNoRows, err) { - return false, nil - } - - return false, err - } - - if len(rr) == 0 { - return false, nil - } - - return true, nil -} - -// CheckoutBook inserts a record indicating the book is checked out to the renter. -func (s *store) CheckoutBook(renterId int, title string) error { - q := ` - INSERT INTO - rentals (renter_id, rental_date, book_title) - VALUES - (?, ?, ?); - ` - - _, err := s.Exec(q, renterId, time.Now(), title) - return err -} - -// ReturnBook sets the return_date field to now indicating the book has been returned. -func (s *store) ReturnBook(renterId int, title string) error { - q := ` - UPDATE - rentals - SET - return_date = ? - WHERE - renter_id = ? - AND book_title = ?; - ` - - _, err := s.Exec(q, time.Now(), renterId, title) - return err -} diff --git a/bookish-server/mysql/mysql.go b/bookish-server/mysql/mysql.go index 88d0d61..9a8001b 100644 --- a/bookish-server/mysql/mysql.go +++ b/bookish-server/mysql/mysql.go @@ -1,19 +1,19 @@ package mysql import ( - "github.com/jmoiron/sqlx" + "github.com/gobuffalo/pop" ) // store holds a connection to the application database. type store struct { - *sqlx.DB + *pop.Connection } // NewStore creates a new connection to the database and returns a store containing the connection. func NewStore(dsn string) (s *store, err error) { - db, err := sqlx.Open("mysql", dsn) + db, err := pop.Connect("development") if err != nil { - return s, err + return &store{}, err } return &store{db}, nil diff --git a/bookish-server/mysql/rental.go b/bookish-server/mysql/rental.go new file mode 100644 index 0000000..6b09796 --- /dev/null +++ b/bookish-server/mysql/rental.go @@ -0,0 +1,81 @@ +package mysql + +import ( + "database/sql" + "errors" + "fmt" + "time" + + "github.com/davidlick/bookish/bookish-server/models" + "github.com/gobuffalo/nulls" + "github.com/gobuffalo/uuid" +) + +// IsBookAvailable queries the database for available books. +func (s *store) IsBookAvailable(title string) (available bool, err error) { + rr := []models.Rental{} + err = s.Where(fmt.Sprintf("book_title = '%s'", title)).Where("return_date IS NULL").All(&rr) + if err != nil { + // If no records are returned then the book is available for rent. + if errors.Is(sql.ErrNoRows, err) { + return true, nil + } + + return false, err + } + + // If 5 books are rented out this book is not available. + if len(rr) > 4 { + return false, nil + } + + return true, nil +} + +// RenterAlreadyCheckedOut queries the database for the given renterId and title. If a record is returned checkedOut will be true. +func (s *store) RenterAlreadyCheckedOut(renterId uuid.UUID, title string) (checkedOut bool, err error) { + rr := []models.Rental{} + err = s.Where(fmt.Sprintf("renter_id = '%s'", renterId)). + Where(fmt.Sprintf("book_title = '%s'", title)). + Where("return_date IS NULL"). + All(&rr) + if err != nil { + // If no records are returned then the book is available for rent. + if errors.Is(sql.ErrNoRows, err) { + return false, nil + } + + return false, err + } + + // If 5 books are rented out this book is not available. + if len(rr) == 0 { + return false, nil + } + + return true, nil +} + +// CheckoutBook inserts a record indicating the book is checked out to the renter. +func (s *store) CheckoutBook(renterId uuid.UUID, title string) error { + r := models.Rental{BookTitle: title, RenterID: renterId, RentalDate: time.Now()} + err := s.Create(&r) + return err +} + +// ReturnBook sets the return_date field to now indicating the book has been returned. +func (s *store) ReturnBook(renterId uuid.UUID, title string) error { + r := models.Rental{} + err := s.Where(fmt.Sprintf("renter_id = '%s'", renterId)). + Where(fmt.Sprintf("book_title = '%s'", title)). + Where("return_date IS NULL"). + First(&r) + if err != nil { + return ErrNotFound + } + + r.ReturnDate = nulls.Time{Time: time.Now(), Valid: true} + + err = s.Save(&r) + return err +} diff --git a/bookish-server/mysql/renter.go b/bookish-server/mysql/renter.go index 3045a6d..7738485 100644 --- a/bookish-server/mysql/renter.go +++ b/bookish-server/mysql/renter.go @@ -4,63 +4,43 @@ import ( "database/sql" "errors" - bookish "github.com/davidlick/bookish/bookish-server" - "github.com/go-sql-driver/mysql" + "github.com/davidlick/bookish/bookish-server/models" + "github.com/gofrs/uuid" ) // ListAll returns all renter records. -func (s *store) ListAll() (rr []bookish.Renter, err error) { - q := ` - SELECT - * - FROM - renters - ` - - err = s.Select(&rr, q) +func (s *store) ListAll() (rr []models.Renter, err error) { + err = s.Eager().All(&rr) if err != nil && errors.Is(sql.ErrNoRows, err) { return nil, ErrNotFound } + return } // FetchDetails queries the database for a specific renter. -func (s *store) FetchDetails(id int) (r bookish.Renter, err error) { - q := ` - SELECT - * - FROM - renters - WHERE - id = ? - ` - - err = s.Get(&r, q, id) - if err != nil && errors.Is(sql.ErrNoRows, err) { - return bookish.Renter{}, ErrNotFound - } +func (s *store) FetchDetails(id string) (r models.Renter, err error) { + err = s.Eager().Find(&r, id) return } // New inserts a record for a new renter. If that renter already exists it returns an ErrAlreadyExists. -func (s *store) New(name string, address string, email string, phoneNumber string) (id int, err error) { - q := ` - INSERT INTO - renters (name, address, email, phone_number) - VALUES - (?, ?, ?, ?) - ` - - result, err := s.Exec(q, name, address, email, phoneNumber) - if err != nil { - if _, ok := err.(*mysql.MySQLError); ok { - return 0, ErrAlreadyExists - } +func (s *store) New(name string, address string, email string, phoneNumber string) (id uuid.UUID, err error) { + r := models.Renter{ + Name: name, + Address: address, + Email: email, + PhoneNumber: phoneNumber, } - lastId, err := result.LastInsertId() + // Create the new renter. + err = s.Create(&r) if err != nil { - return id, err + return } - return int(lastId), nil + + // Get the last renter created. + r = models.Renter{} + err = s.Last(&r) + return r.ID, err } diff --git a/bookish-server/inventory/errors.go b/bookish-server/rental/errors.go similarity index 90% rename from bookish-server/inventory/errors.go rename to bookish-server/rental/errors.go index 6d3379c..b1fab7e 100644 --- a/bookish-server/inventory/errors.go +++ b/bookish-server/rental/errors.go @@ -1,4 +1,4 @@ -package inventory +package rental import "errors" diff --git a/bookish-server/inventory/inventory.go b/bookish-server/rental/rental.go similarity index 68% rename from bookish-server/inventory/inventory.go rename to bookish-server/rental/rental.go index 7fea191..28b7001 100644 --- a/bookish-server/inventory/inventory.go +++ b/bookish-server/rental/rental.go @@ -1,11 +1,17 @@ -package inventory +package rental + +import ( + "fmt" + + "github.com/gofrs/uuid" +) // Storage defines behavior for interacting with the inventory store. type Storage interface { IsBookAvailable(title string) (available bool, err error) - RenterAlreadyCheckedOut(renterId int, title string) (checkedOut bool, err error) - CheckoutBook(renterId int, title string) error - ReturnBook(renterId int, title string) error + RenterAlreadyCheckedOut(renterId uuid.UUID, title string) (checkedOut bool, err error) + CheckoutBook(renterId uuid.UUID, title string) error + ReturnBook(renterId uuid.UUID, title string) error } type service struct { @@ -15,8 +21,8 @@ type service struct { // Service defines behavior for interacting with the inventory service. type Service interface { IsBookAvailable(title string) (available bool, err error) - CheckoutBook(renterId int, title string) error - ReturnBook(renterId int, title string) error + CheckoutBook(renterId uuid.UUID, title string) error + ReturnBook(renterId uuid.UUID, title string) error } // NewService creates a new inventory service. @@ -32,7 +38,7 @@ func (s *service) IsBookAvailable(title string) (available bool, err error) { } // CheckoutBook checks a book out to a renter. -func (s *service) CheckoutBook(renterId int, title string) error { +func (s *service) CheckoutBook(renterId uuid.UUID, title string) error { available, err := s.IsBookAvailable(title) if err != nil { return err @@ -46,6 +52,7 @@ func (s *service) CheckoutBook(renterId int, title string) error { // If there was an error or the book is checked out by the renter already we'll // say the book is unavailable. if err != nil || checkedOut { + fmt.Println("unavailable book", err, checkedOut) return ErrUnavailableBook } @@ -53,6 +60,6 @@ func (s *service) CheckoutBook(renterId int, title string) error { } // ReturnBook returns a book for a renter. -func (s *service) ReturnBook(renterId int, title string) error { +func (s *service) ReturnBook(renterId uuid.UUID, title string) error { return s.store.ReturnBook(renterId, title) } diff --git a/bookish-server/inventory/inventory_mock.go b/bookish-server/rental/rental_mock.go similarity index 90% rename from bookish-server/inventory/inventory_mock.go rename to bookish-server/rental/rental_mock.go index 1a1c7c3..c0a3f54 100644 --- a/bookish-server/inventory/inventory_mock.go +++ b/bookish-server/rental/rental_mock.go @@ -1,10 +1,11 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: inventory.go +// Source: rental.go -// Package inventory is a generated GoMock package. -package inventory +// Package rental is a generated GoMock package. +package rental import ( + uuid "github.com/gofrs/uuid" gomock "github.com/golang/mock/gomock" reflect "reflect" ) @@ -48,7 +49,7 @@ func (mr *MockStorageMockRecorder) IsBookAvailable(title interface{}) *gomock.Ca } // RenterAlreadyCheckedOut mocks base method -func (m *MockStorage) RenterAlreadyCheckedOut(renterId int, title string) (bool, error) { +func (m *MockStorage) RenterAlreadyCheckedOut(renterId uuid.UUID, title string) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RenterAlreadyCheckedOut", renterId, title) ret0, _ := ret[0].(bool) @@ -63,7 +64,7 @@ func (mr *MockStorageMockRecorder) RenterAlreadyCheckedOut(renterId, title inter } // CheckoutBook mocks base method -func (m *MockStorage) CheckoutBook(renterId int, title string) error { +func (m *MockStorage) CheckoutBook(renterId uuid.UUID, title string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CheckoutBook", renterId, title) ret0, _ := ret[0].(error) @@ -77,7 +78,7 @@ func (mr *MockStorageMockRecorder) CheckoutBook(renterId, title interface{}) *go } // ReturnBook mocks base method -func (m *MockStorage) ReturnBook(renterId int, title string) error { +func (m *MockStorage) ReturnBook(renterId uuid.UUID, title string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReturnBook", renterId, title) ret0, _ := ret[0].(error) @@ -129,7 +130,7 @@ func (mr *MockServiceMockRecorder) IsBookAvailable(title interface{}) *gomock.Ca } // CheckoutBook mocks base method -func (m *MockService) CheckoutBook(renterId int, title string) error { +func (m *MockService) CheckoutBook(renterId uuid.UUID, title string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CheckoutBook", renterId, title) ret0, _ := ret[0].(error) @@ -143,7 +144,7 @@ func (mr *MockServiceMockRecorder) CheckoutBook(renterId, title interface{}) *go } // ReturnBook mocks base method -func (m *MockService) ReturnBook(renterId int, title string) error { +func (m *MockService) ReturnBook(renterId uuid.UUID, title string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReturnBook", renterId, title) ret0, _ := ret[0].(error) diff --git a/bookish-server/inventory/inventory_test.go b/bookish-server/rental/rental_test.go similarity index 79% rename from bookish-server/inventory/inventory_test.go rename to bookish-server/rental/rental_test.go index 22fc0d0..0c2c903 100644 --- a/bookish-server/inventory/inventory_test.go +++ b/bookish-server/rental/rental_test.go @@ -1,18 +1,18 @@ -package inventory +package rental import ( "errors" "testing" + "github.com/gofrs/uuid" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) -func TestInventory_IsBookAvailable(t *testing.T) { +func TestRental_IsBookAvailable(t *testing.T) { testCases := []struct { TestName string Title string - RenterId int Available bool Error error }{ @@ -50,10 +50,9 @@ func TestInventory_IsBookAvailable(t *testing.T) { } } -func TestInventory_CheckoutBook(t *testing.T) { +func TestRental_CheckoutBook(t *testing.T) { testCases := []struct { TestName string - RenterId int Title string Available bool AvailableError error @@ -63,27 +62,23 @@ func TestInventory_CheckoutBook(t *testing.T) { }{ { TestName: "success", - RenterId: 1, Title: "Book #1", Available: true, }, { TestName: "unavailable", - RenterId: 2, Title: "Book #2", Available: false, Error: ErrUnavailableBook, }, { TestName: "availability_error", - RenterId: 3, Title: "Book #3", AvailableError: errors.New("test error"), Error: errors.New("test error"), }, { TestName: "checked-out", - RenterId: 4, Title: "Book #4", Available: true, CheckedOut: true, @@ -91,7 +86,6 @@ func TestInventory_CheckoutBook(t *testing.T) { }, { TestName: "failure", - RenterId: 4, Title: "Book #4", Available: true, Error: errors.New("test error"), @@ -103,38 +97,40 @@ func TestInventory_CheckoutBook(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() + renterId, err := uuid.NewV4() + if err != nil { + t.Error(err) + } + mockStorage := NewMockStorage(ctrl) mockStorage.EXPECT().IsBookAvailable(tc.Title).Return(tc.Available, tc.AvailableError) if tc.Available && tc.AvailableError == nil { - mockStorage.EXPECT().RenterAlreadyCheckedOut(tc.RenterId, tc.Title).Return(tc.CheckedOut, tc.CheckedOutError) + mockStorage.EXPECT().RenterAlreadyCheckedOut(renterId, tc.Title).Return(tc.CheckedOut, tc.CheckedOutError) if !tc.CheckedOut && tc.CheckedOutError == nil { - mockStorage.EXPECT().CheckoutBook(tc.RenterId, tc.Title).Return(tc.Error) + mockStorage.EXPECT().CheckoutBook(renterId, tc.Title).Return(tc.Error) } } service := NewService(mockStorage) - err := service.CheckoutBook(tc.RenterId, tc.Title) + err = service.CheckoutBook(renterId, tc.Title) assert.Equal(t, tc.Error, err) }) } } -func TestInventory_ReturnBook(t *testing.T) { +func TestRental_ReturnBook(t *testing.T) { testCases := []struct { TestName string - RenterId int Title string Error error }{ { TestName: "success", - RenterId: 1, Title: "Book #1", }, { TestName: "failure", - RenterId: 2, Title: "Book #2", Error: errors.New("test error"), }, @@ -145,11 +141,16 @@ func TestInventory_ReturnBook(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() + renterId, err := uuid.NewV4() + if err != nil { + t.Error(err) + } + mockStorage := NewMockStorage(ctrl) - mockStorage.EXPECT().ReturnBook(tc.RenterId, tc.Title).Return(tc.Error) + mockStorage.EXPECT().ReturnBook(renterId, tc.Title).Return(tc.Error) service := NewService(mockStorage) - err := service.ReturnBook(tc.RenterId, tc.Title) + err = service.ReturnBook(renterId, tc.Title) assert.Equal(t, tc.Error, err) }) diff --git a/bookish-server/renter.go b/bookish-server/renter.go index 6ced311..5b28ce3 100644 --- a/bookish-server/renter.go +++ b/bookish-server/renter.go @@ -2,9 +2,10 @@ package bookish_server // Renter is an entity in the Bookish API wish rents books. type Renter struct { - ID int `db:"id" json:"id"` - Name string `db:"name" json:"name"` - Address string `db:"address" json:"address"` - Email string `db:"email" json:"email"` - PhoneNumber string `db:"phone_number" json:"phoneNumber"` + ID string `db:"id" json:"id"` + Name string `db:"name" json:"name"` + Address string `db:"address" json:"address"` + Email string `db:"email" json:"email"` + PhoneNumber string `db:"phone_number" json:"phoneNumber"` + Rentals []string `json:"rentals"` } diff --git a/bookish-server/renter/renter.go b/bookish-server/renter/renter.go index 2fd3363..c3ddefc 100644 --- a/bookish-server/renter/renter.go +++ b/bookish-server/renter/renter.go @@ -2,13 +2,15 @@ package renter import ( bookish "github.com/davidlick/bookish/bookish-server" + "github.com/davidlick/bookish/bookish-server/models" + "github.com/gobuffalo/uuid" ) // Storage defines behavior for interacting with the renter store. type Storage interface { - ListAll() (rr []bookish.Renter, err error) - FetchDetails(id int) (r bookish.Renter, err error) - New(name string, address string, email string, phoneNumber string) (id int, err error) + ListAll() (rr []models.Renter, err error) + FetchDetails(id string) (r models.Renter, err error) + New(name string, address string, email string, phoneNumber string) (id uuid.UUID, err error) } type service struct { @@ -18,8 +20,8 @@ type service struct { // Service defines behavior for interacting with the renter service. type Service interface { ListRenters() (rr []bookish.Renter, err error) - FetchRenter(id int) (r bookish.Renter, err error) - RegisterRenter(fullName string, address string, email string, phoneNumber string) (id int, err error) + FetchRenter(id string) (r bookish.Renter, err error) + RegisterRenter(fullName string, address string, email string, phoneNumber string) (id uuid.UUID, err error) } // NewService creates a new renter service. @@ -31,15 +33,50 @@ func NewService(s Storage) *service { // ListRenters returns all renters from the renter store. func (s *service) ListRenters() (rr []bookish.Renter, err error) { - return s.store.ListAll() + renters, err := s.store.ListAll() + if err != nil { + return nil, err + } + + for _, renter := range renters { + r := MutateRenterModel(renter) + rr = append(rr, r) + } + + return rr, nil } // FetchRenter returns a specific renter from the renter store. -func (s *service) FetchRenter(id int) (r bookish.Renter, err error) { - return s.store.FetchDetails(id) +func (s *service) FetchRenter(id string) (r bookish.Renter, err error) { + renter, err := s.store.FetchDetails(id) + if err != nil { + return r, err + } + + r = MutateRenterModel(renter) + return r, nil } // RegisterRenter registers a new renter in the renter store. -func (s *service) RegisterRenter(fullName string, address string, email string, phoneNumber string) (id int, err error) { +func (s *service) RegisterRenter(fullName string, address string, email string, phoneNumber string) (id uuid.UUID, err error) { return s.store.New(fullName, address, email, phoneNumber) } + +// MutateRenterModel is a convenience function to mutate a models.Renter into a bookish.Renter. +func MutateRenterModel(m models.Renter) bookish.Renter { + r := bookish.Renter{} + + r.ID = m.ID.String() + r.Name = m.Name + r.Address = m.Address + r.Email = m.Email + r.PhoneNumber = m.PhoneNumber + + for _, rental := range m.Rentals { + if !rental.ReturnDate.Valid { + r.Rentals = append(r.Rentals, rental.BookTitle) + } + } + + return r +} diff --git a/bookish-server/renter/renter_mock.go b/bookish-server/renter/renter_mock.go index bad9578..56de75e 100644 --- a/bookish-server/renter/renter_mock.go +++ b/bookish-server/renter/renter_mock.go @@ -1,11 +1,13 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: ./renter.go +// Source: renter.go // Package renter is a generated GoMock package. package renter import ( bookish_server "github.com/davidlick/bookish/bookish-server" + models "github.com/davidlick/bookish/bookish-server/models" + uuid "github.com/gobuffalo/uuid" gomock "github.com/golang/mock/gomock" reflect "reflect" ) @@ -34,10 +36,10 @@ func (m *MockStorage) EXPECT() *MockStorageMockRecorder { } // ListAll mocks base method -func (m *MockStorage) ListAll() ([]bookish_server.Renter, error) { +func (m *MockStorage) ListAll() ([]models.Renter, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAll") - ret0, _ := ret[0].([]bookish_server.Renter) + ret0, _ := ret[0].([]models.Renter) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -49,10 +51,10 @@ func (mr *MockStorageMockRecorder) ListAll() *gomock.Call { } // FetchDetails mocks base method -func (m *MockStorage) FetchDetails(id int) (bookish_server.Renter, error) { +func (m *MockStorage) FetchDetails(id string) (models.Renter, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FetchDetails", id) - ret0, _ := ret[0].(bookish_server.Renter) + ret0, _ := ret[0].(models.Renter) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -64,10 +66,10 @@ func (mr *MockStorageMockRecorder) FetchDetails(id interface{}) *gomock.Call { } // New mocks base method -func (m *MockStorage) New(name, address, email, phoneNumber string) (int, error) { +func (m *MockStorage) New(name, address, email, phoneNumber string) (uuid.UUID, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "New", name, address, email, phoneNumber) - ret0, _ := ret[0].(int) + ret0, _ := ret[0].(uuid.UUID) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -77,3 +79,71 @@ func (mr *MockStorageMockRecorder) New(name, address, email, phoneNumber interfa mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "New", reflect.TypeOf((*MockStorage)(nil).New), name, address, email, phoneNumber) } + +// MockService is a mock of Service interface +type MockService struct { + ctrl *gomock.Controller + recorder *MockServiceMockRecorder +} + +// MockServiceMockRecorder is the mock recorder for MockService +type MockServiceMockRecorder struct { + mock *MockService +} + +// NewMockService creates a new mock instance +func NewMockService(ctrl *gomock.Controller) *MockService { + mock := &MockService{ctrl: ctrl} + mock.recorder = &MockServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockService) EXPECT() *MockServiceMockRecorder { + return m.recorder +} + +// ListRenters mocks base method +func (m *MockService) ListRenters() ([]bookish_server.Renter, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListRenters") + ret0, _ := ret[0].([]bookish_server.Renter) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListRenters indicates an expected call of ListRenters +func (mr *MockServiceMockRecorder) ListRenters() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRenters", reflect.TypeOf((*MockService)(nil).ListRenters)) +} + +// FetchRenter mocks base method +func (m *MockService) FetchRenter(id string) (bookish_server.Renter, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchRenter", id) + ret0, _ := ret[0].(bookish_server.Renter) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchRenter indicates an expected call of FetchRenter +func (mr *MockServiceMockRecorder) FetchRenter(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchRenter", reflect.TypeOf((*MockService)(nil).FetchRenter), id) +} + +// RegisterRenter mocks base method +func (m *MockService) RegisterRenter(fullName, address, email, phoneNumber string) (uuid.UUID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RegisterRenter", fullName, address, email, phoneNumber) + ret0, _ := ret[0].(uuid.UUID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RegisterRenter indicates an expected call of RegisterRenter +func (mr *MockServiceMockRecorder) RegisterRenter(fullName, address, email, phoneNumber interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRenter", reflect.TypeOf((*MockService)(nil).RegisterRenter), fullName, address, email, phoneNumber) +} diff --git a/bookish-server/renter/renter_test.go b/bookish-server/renter/renter_test.go index b4cf6aa..6f9bde3 100644 --- a/bookish-server/renter/renter_test.go +++ b/bookish-server/renter/renter_test.go @@ -5,6 +5,8 @@ import ( "testing" bookish "github.com/davidlick/bookish/bookish-server" + "github.com/davidlick/bookish/bookish-server/models" + "github.com/gofrs/uuid" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) @@ -12,12 +14,12 @@ import ( func TestRenter_ListRenters(t *testing.T) { testCases := []struct { TestName string - Renters []bookish.Renter + Renters []models.Renter Error error }{ { TestName: "success", - Renters: []bookish.Renter{ + Renters: []models.Renter{ { Name: "John Doe", Address: "123 Main Way", @@ -49,7 +51,13 @@ func TestRenter_ListRenters(t *testing.T) { service := NewService(mockStorage) renters, err := service.ListRenters() - assert.Equal(t, tc.Renters, renters) + var expectedRenters []bookish.Renter + for _, renter := range tc.Renters { + r := MutateRenterModel(renter) + expectedRenters = append(expectedRenters, r) + } + + assert.Equal(t, expectedRenters, renters) assert.Equal(t, tc.Error, err) }) } @@ -59,13 +67,12 @@ func TestRenter_FetchRenter(t *testing.T) { testCases := []struct { TestName string ID int - Renter bookish.Renter + Renter models.Renter Error error }{ { TestName: "success", - ID: 1, - Renter: bookish.Renter{ + Renter: models.Renter{ Name: "John Smith", Address: "123 Main Way", Email: "john@smith.com", @@ -74,7 +81,6 @@ func TestRenter_FetchRenter(t *testing.T) { }, { TestName: "error", - ID: 1, Error: errors.New("test error"), }, } @@ -84,13 +90,23 @@ func TestRenter_FetchRenter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() + renterId, err := uuid.NewV4() + if err != nil { + t.Error(err) + } + mockStorage := NewMockStorage(ctrl) - mockStorage.EXPECT().FetchDetails(tc.ID).Return(tc.Renter, tc.Error) + mockStorage.EXPECT().FetchDetails(renterId.String()).Return(tc.Renter, tc.Error) service := NewService(mockStorage) - renter, err := service.FetchRenter(tc.ID) + renter, err := service.FetchRenter(renterId.String()) - assert.Equal(t, tc.Renter, renter) + expectedRenter := MutateRenterModel(tc.Renter) + if tc.Error != nil { + renter.ID = "00000000-0000-0000-0000-000000000000" + } + + assert.Equal(t, expectedRenter, renter) assert.Equal(t, tc.Error, err) }) } @@ -103,7 +119,6 @@ func TestRenter_RegisterRenter(t *testing.T) { Address string Email string PhoneNumber string - ReturnID int Error error }{ { @@ -116,13 +131,18 @@ func TestRenter_RegisterRenter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() + returnUuid, err := uuid.NewV4() + if err != nil { + t.Error(err) + } + mockStorage := NewMockStorage(ctrl) - mockStorage.EXPECT().New(tc.Name, tc.Address, tc.Email, tc.PhoneNumber).Return(tc.ReturnID, tc.Error) + mockStorage.EXPECT().New(tc.Name, tc.Address, tc.Email, tc.PhoneNumber).Return(returnUuid, tc.Error) service := NewService(mockStorage) id, err := service.RegisterRenter(tc.Name, tc.Address, tc.Email, tc.PhoneNumber) - assert.Equal(t, tc.ReturnID, id) + assert.Equal(t, returnUuid, id) assert.Equal(t, tc.Error, err) }) } diff --git a/docker-compose.yml b/docker-compose.yml index bdbf9ff..4fcc7b0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,14 +14,14 @@ services: - DBUSER=root - DBPASS=123pass - DBPORT=3306 - - BOOKSHOST= + - BOOKSHOST=https://servicepros-test-api.herokuapp.com db: - image: mysql - ports: - - 3306:3306 - restart: always - volumes: - - ./init:/docker-entrypoint-initdb.d - environment: - MYSQL_DATABASE: bookish - MYSQL_ROOT_PASSWORD: 123pass + image: mysql + ports: + - 3306:3306 + restart: always + volumes: + - ./init:/docker-entrypoint-initdb.d + environment: + MYSQL_DATABASE: bookish + MYSQL_ROOT_PASSWORD: 123pass diff --git a/init/bookish-ddl.sql b/init/bookish-ddl.sql deleted file mode 100644 index e664c34..0000000 --- a/init/bookish-ddl.sql +++ /dev/null @@ -1,21 +0,0 @@ -create table renters -( - id int auto_increment primary key, - name varchar(255) not null, - address varchar(255) not null, - email varchar(255) not null, - phone_number varchar(255) not null -); - -create unique index renters_id_uindex - on renters (id); - -create table rentals -( - id int auto_increment primary key, - renter_id int not null, - rental_date datetime not null, - return_date datetime null, - book_title varchar(255) not null, - foreign key (renter_id) references renters(id) -); diff --git a/init/schema.sql b/init/schema.sql new file mode 100644 index 0000000..53e095c --- /dev/null +++ b/init/schema.sql @@ -0,0 +1,22 @@ +create table if not exists renters +( + id varchar(255) not null primary key, + created_at datetime not null, + updated_at datetime not null, + name varchar(255) not null, + address varchar(255) not null, + email varchar(255) not null, + phone_number varchar(255) not null +); + +create table if not exists rentals +( + id varchar(255) not null primary key, + created_at datetime not null, + updated_at datetime not null, + book_title varchar(255) not null, + renter_id varchar(255) not null, + rental_date datetime not null, + return_date datetime null, + foreign key (renter_id) references renters(id) +);