feat: add password and fix errors

This commit is contained in:
Elton Minetto
2020-06-29 13:38:13 -03:00
parent 328b4e428c
commit d4b9b713e4
12 changed files with 246 additions and 21 deletions
+22 -4
View File
@@ -29,7 +29,7 @@ curl -X "POST" "http://localhost:8080/v1/book" \
"title": "I Am Ozzy", "title": "I Am Ozzy",
"author": "Ozzy Osbourne", "author": "Ozzy Osbourne",
"pages": 294, "pages": 294,
"quantity":1 "quantity":10
}' }'
``` ```
### Search book ### Search book
@@ -56,10 +56,9 @@ curl -X "POST" "http://localhost:8080/v1/user" \
-H 'Accept: application/json' \ -H 'Accept: application/json' \
-d $'{ -d $'{
"email": "[email protected]", "email": "[email protected]",
"fist_name": "Ozzy", "first_name": "Ozzy",
"last_name": "Osbourne", "last_name": "Osbourne",
"password": "bateater666", "password": "bateater666"
"quantity":1
}' }'
``` ```
@@ -79,6 +78,25 @@ curl "http://localhost:8080/v1/user" \
-H 'Accept: application/json' -H 'Accept: application/json'
``` ```
### Borrow a book
```
curl "http://localhost:8080/v1/loan/borrow/be8b1757-b043-4dbd-b873-63fa9ecd8bb1/282885d7-5d5e-4205-87eb-edc2b2ac5022" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json'
```
### Return a book
```
curl "http://localhost:8080/v1/loan/return/be8b1757-b043-4dbd-b873-63fa9ecd8bb1" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json'
```
## CMD ## CMD
### Search for a book ### Search for a book
+2
View File
@@ -1,6 +1,7 @@
package handler package handler
import ( import (
"fmt"
"net/http" "net/http"
"github.com/eminetto/clean-architecture-go-v2/domain/entity/user" "github.com/eminetto/clean-architecture-go-v2/domain/entity/user"
@@ -56,6 +57,7 @@ func borrowBook(bService book.UseCase, uService user.UseCase, loanService loan.U
} }
err = loanService.Borrow(u, b) err = loanService.Borrow(u, b)
if err != nil { if err != nil {
fmt.Println(err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(errorMessage)) w.Write([]byte(errorMessage))
return return
+3 -1
View File
@@ -9,6 +9,8 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/eminetto/clean-architecture-go-v2/pkg/password"
"github.com/eminetto/clean-architecture-go-v2/domain/loan" "github.com/eminetto/clean-architecture-go-v2/domain/loan"
"github.com/eminetto/clean-architecture-go-v2/domain/entity/user" "github.com/eminetto/clean-architecture-go-v2/domain/entity/user"
@@ -39,7 +41,7 @@ func main() {
bookService := book.NewService(bookRepo) bookService := book.NewService(bookRepo)
userRepo := user.NewMySQLRepoRepository(db) userRepo := user.NewMySQLRepoRepository(db)
userService := user.NewService(userRepo) userService := user.NewService(userRepo, password.NewService())
loanService := loan.NewService(userService, bookService) loanService := loan.NewService(userService, bookService)
+28 -7
View File
@@ -21,7 +21,7 @@ func NewMySQLRepoRepository(db *sql.DB) *MySQLRepo {
} }
} }
//Create an user //Create a book
func (r *MySQLRepo) Create(e *Book) (entity.ID, error) { func (r *MySQLRepo) Create(e *Book) (entity.ID, error) {
stmt, err := r.db.Prepare(` stmt, err := r.db.Prepare(`
insert into book (id, title, author, pages, quantity, created_at) insert into book (id, title, author, pages, quantity, created_at)
@@ -47,17 +47,34 @@ func (r *MySQLRepo) Create(e *Book) (entity.ID, error) {
return e.ID, nil return e.ID, nil
} }
//Get an user //Get a book
func (r *MySQLRepo) Get(id entity.ID) (*Book, error) { func (r *MySQLRepo) Get(id entity.ID) (*Book, error) {
return nil, nil stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book where id = ?`)
if err != nil {
return nil, err
}
var b Book
rows, err := stmt.Query(id)
if err != nil {
return nil, err
}
for rows.Next() {
err = rows.Scan(&b.ID, &b.Title, &b.Author, &b.Pages, &b.Quantity, &b.CreatedAt)
}
return &b, nil
} }
//Update an user //Update a book
func (r *MySQLRepo) Update(e *Book) error { func (r *MySQLRepo) Update(e *Book) error {
e.UpdatedAt = time.Now()
_, err := r.db.Exec("update book set title = ?, author = ?, pages = ?, quantity = ?, updated_at = ? where id = ?", e.Title, e.Author, e.Pages, e.Quantity, e.UpdatedAt.Format("2006-01-02"), e.ID)
if err != nil {
return err
}
return nil return nil
} }
//Search users //Search books
func (r *MySQLRepo) Search(query string) ([]*Book, error) { func (r *MySQLRepo) Search(query string) ([]*Book, error) {
stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book where title like ?`) stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book where title like ?`)
if err != nil { if err != nil {
@@ -82,7 +99,7 @@ func (r *MySQLRepo) Search(query string) ([]*Book, error) {
return books, nil return books, nil
} }
//List users //List books
func (r *MySQLRepo) List() ([]*Book, error) { func (r *MySQLRepo) List() ([]*Book, error) {
stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book`) stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book`)
if err != nil { if err != nil {
@@ -107,7 +124,11 @@ func (r *MySQLRepo) List() ([]*Book, error) {
return books, nil return books, nil
} }
//Delete an user //Delete a book
func (r *MySQLRepo) Delete(id entity.ID) error { func (r *MySQLRepo) Delete(id entity.ID) error {
_, err := r.db.Exec("delete from book where id = ?", id)
if err != nil {
return err
}
return nil return nil
} }
+111 -4
View File
@@ -4,6 +4,8 @@ import (
"database/sql" "database/sql"
"time" "time"
"github.com/eminetto/clean-architecture-go-v2/domain"
"github.com/eminetto/clean-architecture-go-v2/domain/entity" "github.com/eminetto/clean-architecture-go-v2/domain/entity"
) )
@@ -47,26 +49,131 @@ func (r *MySQLRepo) Create(e *User) (entity.ID, error) {
//Get an user //Get an user
func (r *MySQLRepo) Get(id entity.ID) (*User, error) { func (r *MySQLRepo) Get(id entity.ID) (*User, error) {
return nil, nil return getUser(id, r.db)
}
func getUser(id entity.ID, db *sql.DB) (*User, error) {
stmt, err := db.Prepare(`select id, email, first_name, last_name, created_at from user where id = ?`)
if err != nil {
return nil, err
}
var u User
rows, err := stmt.Query(id)
if err != nil {
return nil, err
}
for rows.Next() {
err = rows.Scan(&u.ID, &u.Email, &u.FirstName, &u.LastName, &u.CreatedAt)
}
stmt, err = db.Prepare(`select book_id from book_user where user_id = ?`)
if err != nil {
return nil, err
}
rows, err = stmt.Query(id)
if err != nil {
return nil, err
}
for rows.Next() {
var i entity.ID
err = rows.Scan(&i)
u.Books = append(u.Books, i)
}
return &u, nil
} }
//Update an user //Update an user
func (r *MySQLRepo) Update(e *User) error { func (r *MySQLRepo) Update(e *User) error {
e.UpdatedAt = time.Now()
_, err := r.db.Exec("update user set email = ?, password = ?, first_name = ?, last_name = ?, updated_at = ? where id = ?", e.Email, e.Password, e.FirstName, e.LastName, e.UpdatedAt.Format("2006-01-02"), e.ID)
if err != nil {
return err
}
_, err = r.db.Exec("delete from book_user where user_id = ?", e.ID)
if err != nil {
return err
}
for _, b := range e.Books {
_, err := r.db.Exec("insert into book_user values(?,?,?)", e.ID, b, time.Now().Format("2006-01-02"))
if err != nil {
return err
}
}
return nil return nil
} }
//Search users //Search users
func (r *MySQLRepo) Search(query string) ([]*User, error) { func (r *MySQLRepo) Search(query string) ([]*User, error) {
stmt, err := r.db.Prepare(`select id from user where name like ?`)
return nil, nil if err != nil {
return nil, err
}
defer stmt.Close()
var ids []entity.ID
rows, err := stmt.Query()
if err != nil {
return nil, err
}
for rows.Next() {
var i entity.ID
err = rows.Scan(&i)
if err != nil {
return nil, err
}
ids = append(ids, i)
}
if len(ids) == 0 {
return nil, domain.ErrNotFound
}
var users []*User
for _, id := range ids {
u, err := getUser(id, r.db)
if err != nil {
return nil, err
}
users = append(users, u)
}
return users, nil
} }
//List users //List users
func (r *MySQLRepo) List() ([]*User, error) { func (r *MySQLRepo) List() ([]*User, error) {
return nil, nil stmt, err := r.db.Prepare(`select id from user`)
if err != nil {
return nil, err
}
defer stmt.Close()
var ids []entity.ID
rows, err := stmt.Query()
if err != nil {
return nil, err
}
for rows.Next() {
var i entity.ID
err = rows.Scan(&i)
if err != nil {
return nil, err
}
ids = append(ids, i)
}
if len(ids) == 0 {
return nil, domain.ErrNotFound
}
var users []*User
for _, id := range ids {
u, err := getUser(id, r.db)
if err != nil {
return nil, err
}
users = append(users, u)
}
return users, nil
} }
//Delete an user //Delete an user
func (r *MySQLRepo) Delete(id entity.ID) error { func (r *MySQLRepo) Delete(id entity.ID) error {
_, err := r.db.Exec("delete from user where id = ?", id)
if err != nil {
return err
}
return nil return nil
} }
+10 -1
View File
@@ -4,18 +4,22 @@ import (
"strings" "strings"
"time" "time"
"github.com/eminetto/clean-architecture-go-v2/pkg/password"
"github.com/eminetto/clean-architecture-go-v2/domain/entity" "github.com/eminetto/clean-architecture-go-v2/domain/entity"
) )
//Service service interface //Service service interface
type Service struct { type Service struct {
repo Repository repo Repository
pwd password.UseCase
} }
//NewService create new use case //NewService create new use case
func NewService(r Repository) *Service { func NewService(r Repository, pwd password.UseCase) *Service {
return &Service{ return &Service{
repo: r, repo: r,
pwd: pwd,
} }
} }
@@ -23,6 +27,11 @@ func NewService(r Repository) *Service {
func (s *Service) Create(e *User) (entity.ID, error) { func (s *Service) Create(e *User) (entity.ID, error) {
e.ID = entity.NewID() e.ID = entity.NewID()
e.CreatedAt = time.Now() e.CreatedAt = time.Now()
pwd, err := s.pwd.Generate(e.Password)
if err != nil {
return e.ID, err
}
e.Password = pwd
return s.repo.Create(e) return s.repo.Create(e)
} }
+6 -4
View File
@@ -3,6 +3,8 @@ package user
import ( import (
"testing" "testing"
"github.com/eminetto/clean-architecture-go-v2/pkg/password"
"github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/domain"
"github.com/eminetto/clean-architecture-go-v2/domain/entity" "github.com/eminetto/clean-architecture-go-v2/domain/entity"
@@ -11,7 +13,7 @@ import (
func Test_Create(t *testing.T) { func Test_Create(t *testing.T) {
repo := NewInmemRepository() repo := NewInmemRepository()
service := NewService(repo) service := NewService(repo, password.NewFakeService())
u := NewFixtureUser() u := NewFixtureUser()
id, err := service.Create(u) id, err := service.Create(u)
assert.Nil(t, err) assert.Nil(t, err)
@@ -22,7 +24,7 @@ func Test_Create(t *testing.T) {
func Test_SearchAndFind(t *testing.T) { func Test_SearchAndFind(t *testing.T) {
repo := NewInmemRepository() repo := NewInmemRepository()
service := NewService(repo) service := NewService(repo, password.NewFakeService())
u1 := NewFixtureUser() u1 := NewFixtureUser()
u2 := NewFixtureUser() u2 := NewFixtureUser()
u2.FirstName = "Lemmy" u2.FirstName = "Lemmy"
@@ -55,7 +57,7 @@ func Test_SearchAndFind(t *testing.T) {
func Test_Update(t *testing.T) { func Test_Update(t *testing.T) {
repo := NewInmemRepository() repo := NewInmemRepository()
service := NewService(repo) service := NewService(repo, password.NewFakeService())
u := NewFixtureUser() u := NewFixtureUser()
id, err := service.Create(u) id, err := service.Create(u)
assert.Nil(t, err) assert.Nil(t, err)
@@ -72,7 +74,7 @@ func Test_Update(t *testing.T) {
func TestDelete(t *testing.T) { func TestDelete(t *testing.T) {
repo := NewInmemRepository() repo := NewInmemRepository()
service := NewService(repo) service := NewService(repo, password.NewFakeService())
u1 := NewFixtureUser() u1 := NewFixtureUser()
u2 := NewFixtureUser() u2 := NewFixtureUser()
u2ID, _ := service.Create(u2) u2ID, _ := service.Create(u2)
+1
View File
@@ -15,5 +15,6 @@ require (
github.com/juju/mgosession v0.0.0-20170206150231-9ae6df2882cd github.com/juju/mgosession v0.0.0-20170206150231-9ae6df2882cd
github.com/prometheus/client_golang v1.7.1 github.com/prometheus/client_golang v1.7.1
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.6.1
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce
) )
+1
View File
@@ -112,6 +112,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+24
View File
@@ -0,0 +1,24 @@
package password
import "errors"
//FakePassword password
type FakePassword struct{}
//NewFakeService create a new fake password
func NewFakeService() *FakePassword {
return &FakePassword{}
}
//Generate a new password
func (p *FakePassword) Generate(raw string) (string, error) {
return raw, nil
}
//Compare compare two passwords
func (p *FakePassword) Compare(p1, p2 string) error {
if p1 == p2 {
return nil
}
return errors.New("Invalid password")
}
+7
View File
@@ -0,0 +1,7 @@
package password
//UseCase interface
type UseCase interface {
Generate(raw string) (string, error)
Compare(p1, p2 string) error
}
+31
View File
@@ -0,0 +1,31 @@
package password
import (
"golang.org/x/crypto/bcrypt"
)
//Password password
type Password struct{}
//NewService create a new fake password
func NewService() *Password {
return &Password{}
}
//Generate a new password
func (p *Password) Generate(raw string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(raw), 10)
if err != nil {
return "", err
}
return string(hash), nil
}
//Compare compare two passwords
func (p *Password) Compare(p1, p2 string) error {
err := bcrypt.CompareHashAndPassword([]byte(p1), []byte(p2))
if err != nil {
return err
}
return nil
}