Skip to content

Commit

Permalink
add db adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
rema424 committed Nov 16, 2019
1 parent 0272149 commit c07ce95
Show file tree
Hide file tree
Showing 12 changed files with 400 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export DB_HOST="127.0.0.1"
export DB_PORT="3306"
export DB_USER="devuser"
export DB_PASSWORD="Passw0rd!"
export DB_NAME="hexample"
62 changes: 62 additions & 0 deletions cmd/http/controller/controller2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package controller

import (
"net/http"
"strconv"

"github.com/labstack/echo/v4"
"github.com/rema424/hexample/internal/service2"
)

// Controller2 ...
type Controller2 struct {
p *service2.Provider
}

// NewController2 ...
func NewController2(p *service2.Provider) *Controller2 {
return &Controller2{p}
}

// HandlePersonRegister ...
// curl -X POST -H 'Content-type: application/json' -d '{"name": "Alice", "email": "[email protected]"}' localhost:8080/people
func (ctrl *Controller2) HandlePersonRegister(c echo.Context) error {
in := struct {
Name string `json:"name"`
Email string `json:"email"`
}{}

if err := c.Bind(&in); err != nil {
return c.JSON(http.StatusBadRequest, err.Error())
}

// TODO: implement
// if err := c.Validate(&in); err != nil {
// return c.JSON(http.StatusUnprocessableEntity, err.Error())
// }

ctx := c.Request().Context()
psn, err := ctrl.p.RegisterPerson(ctx, in.Name, in.Email)
if err != nil {
return c.JSON(http.StatusInternalServerError, err.Error())
}

return c.JSON(http.StatusOK, psn)
}

// HandlePersonGet ...
// curl localhost:8080/people/999
func (ctrl *Controller2) HandlePersonGet(c echo.Context) error {
id, err := strconv.Atoi(c.Param("personID"))
if err != nil {
return c.JSON(http.StatusUnprocessableEntity, err.Error())
}

ctx := c.Request().Context()
psn, err := ctrl.p.GetPersonByID(ctx, int64(id))
if err != nil {
return c.JSON(http.StatusInternalServerError, err.Error())
}

return c.JSON(http.StatusOK, psn)
}
25 changes: 25 additions & 0 deletions cmd/http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/rema424/hexample/cmd/http/controller"
"github.com/rema424/hexample/internal/service2"
)

var e = createMux()
Expand All @@ -27,9 +28,33 @@ func main() {
}

func init() {
// Mysql
// c := mysql.Config{
// Host: os.Getenv("DB_HOST"),
// Port: os.Getenv("DB_PORT"),
// User: os.Getenv("DB_USER"),
// DBName: os.Getenv("DB_NAME"),
// Passwd: os.Getenv("DB_PASSWORD"),
// AllowNativePasswords: true,
// }
// db, err := mysql.Connect(c)
// if err != nil {
// log.Fatalln(err)
// }
// svc2Repo := service2.NewRepositoryImpl(db)
// svc2Pvdr := service2.NewProvider(svc2Repo)

// MockDB
mockDB := service2.NewMockDB()
svc2RepoMock := service2.NewRepositoryImplMock(mockDB)
svc2Pvdr := service2.NewProvider(svc2RepoMock)

ctrl := &controller.Controller{}
ctrl2 := controller.NewController2(svc2Pvdr)

e.GET("/:message", ctrl.HandleMessage)
e.GET("/people/:personID", ctrl2.HandlePersonGet)
e.POST("/people", ctrl2.HandlePersonRegister)
}

func createMux() *echo.Echo {
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ module github.com/rema424/hexample
go 1.13

require (
github.com/go-sql-driver/mysql v1.4.0
github.com/jmoiron/sqlx v1.2.0
github.com/labstack/echo/v4 v4.1.11
github.com/spf13/cobra v0.0.5
google.golang.org/appengine v1.6.5 // indirect
)
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,29 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
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/labstack/echo/v4 v4.1.11 h1:z0BZoArY4FqdpUEl+wlHp4hnr/oSR6MTmQmv8OHSoww=
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
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/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
Expand Down Expand Up @@ -61,6 +70,8 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49N
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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/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=
Expand All @@ -69,6 +80,11 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
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=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
Expand Down
8 changes: 8 additions & 0 deletions internal/service2/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package service2

// Person ...
type Person struct {
ID int64 `db:"kokoha"`
Name string `db:"tekitode"` // sql.NullString はインフラに結合するので使わない
Email string `db:"yoiyo"`
}
38 changes: 38 additions & 0 deletions internal/service2/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package service2

import "context"

// Provider ...
type Provider struct {
r Repository
}

// NewProvider ...
func NewProvider(r Repository) *Provider {
return &Provider{r}
}

// RegisterPerson ...
func (p *Provider) RegisterPerson(ctx context.Context, name, email string) (Person, error) {
psn := Person{
Name: name,
Email: email,
}

psn, err := p.r.RegisterPerson(ctx, psn)
if err != nil {
return Person{}, err
}

return psn, nil
}

// GetPersonByID ...
func (p *Provider) GetPersonByID(ctx context.Context, id int64) (Person, error) {
psn, err := p.r.GetPersonByID(ctx, id)
if err != nil {
return Person{}, err
}

return psn, nil
}
9 changes: 9 additions & 0 deletions internal/service2/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package service2

import "context"

// Repository ...
type Repository interface {
RegisterPerson(context.Context, Person) (Person, error)
GetPersonByID(context.Context, int64) (Person, error)
}
50 changes: 50 additions & 0 deletions internal/service2/repository_impl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package service2

import (
"context"

"github.com/jmoiron/sqlx"
)

// RepositoryImpl ...
type RepositoryImpl struct {
db *sqlx.DB
}

// NewRepositoryImpl ...
func NewRepositoryImpl(db *sqlx.DB) *RepositoryImpl {
return &RepositoryImpl{db}
}

// RegisterPerson ...
func (r *RepositoryImpl) RegisterPerson(ctx context.Context, p Person) (Person, error) {
q := `INSERT INTO person (name, email) VALUES (:tekitode, :yoiyo);`
res, err := r.db.NamedExecContext(ctx, q, p)
if err != nil {
return Person{}, err
}

id, err := res.LastInsertId()
if err != nil {
return Person{}, err
}

p.ID = id
return p, nil
}

// GetPersonByID ...
func (r *RepositoryImpl) GetPersonByID(ctx context.Context, id int64) (Person, error) {
// DB上のnull対策はここで実装する
q := `
SELECT
COALESCE(id, 0) AS 'kokoha',
COALESCE(name, '') AS 'tekitode',
COALESCE(email, '') AS 'yoiyo'
FROM person
WHERE id = ?;
`
var p Person
err := r.db.GetContext(ctx, &p, q, id)
return p, err
}
64 changes: 64 additions & 0 deletions internal/service2/repository_impl_mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package service2

import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)

var src = rand.NewSource(time.Now().UnixNano())

// RepositoryImplMock ...
type RepositoryImplMock struct {
db *MockDB
}

// MockDB ...
type MockDB struct {
mu sync.RWMutex
data map[int64]Person
}

// NewMockDB ...s
func NewMockDB() *MockDB {
return &MockDB{data: make(map[int64]Person)}
}

// NewRepositoryImplMock ...
func NewRepositoryImplMock(db *MockDB) *RepositoryImplMock {
return &RepositoryImplMock{db}
}

// RegisterPerson ...
func (r *RepositoryImplMock) RegisterPerson(ctx context.Context, p Person) (Person, error) {
r.db.mu.Lock()
defer r.db.mu.Unlock()

// 割り当て可能なIDを探す
var id int64
for {
id = src.Int63()
_, ok := r.db.data[id]
if !ok {
break
}
}

p.ID = id
r.db.data[p.ID] = p

return p, nil
}

// GetPersonByID ...
func (r *RepositoryImplMock) GetPersonByID(ctx context.Context, id int64) (Person, error) {
r.db.mu.RLock()
defer r.db.mu.RUnlock()

if p, ok := r.db.data[id]; ok {
return p, nil
}
return Person{}, fmt.Errorf("person not found - id: %d", id)
}
Loading

0 comments on commit c07ce95

Please sign in to comment.