Skip to content

Commit

Permalink
code refactoring; added more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Qiang Xue committed Aug 16, 2016
1 parent 58947b6 commit 1c9a3e6
Show file tree
Hide file tree
Showing 17 changed files with 990 additions and 680 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ _testmain.go
glide.lock
coverage.out
coverage-all.out
build
server
27 changes: 27 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
language: go

go:
- 1.6
- 1.7
- tip

cache:
directories:
- vendor

addons:
postgresql: "9.5"

install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- go get github.com/axw/gocov/gocov
- go get -u github.com/Masterminds/glide
- glide up -s -u

before_script:
- psql -U postgres -c 'CREATE DATABASE go_restful;';

script:
- glide up -u -s
- $HOME/gopath/bin/goveralls -service=travis-ci
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
MAIN_VERSION:=$(shell git describe --abbrev=0 --tags || echo "1.0")
MAIN_VERSION:=$(shell git describe --abbrev=0 --tags || echo "0.1")
VERSION:=${MAIN_VERSION}\#$(shell git log -n 1 --pretty=format:"%h")
PACKAGES:=$(shell go list ./... | sed -n '1!p' | grep -v /vendor/)
RELEASE_FILE:=server-${MAIN_VERSION}.tar.gz
LDFLAGS:=-ldflags "-X github.com/qiangxue/golang-restful-starter-kit/app.Version=${VERSION}"

default: run
Expand All @@ -20,7 +21,10 @@ run:
go run ${LDFLAGS} server.go

build: clean
GOOS=linux GOARCH=amd64 go build ${LDFLAGS} -a -o server server.go
mkdir -p build
GOOS=linux GOARCH=amd64 go build ${LDFLAGS} -a -o build/server server.go
cp -r config build
cd build && tar zcf ${RELEASE_FILE} *

clean:
rm -f server coverage.out coverage-all.out
rm -rf build server coverage.out coverage-all.out
23 changes: 15 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Go RESTful Application Starter Kit

[![GoDoc](https://godoc.org/github.com/qiangxue/golang-restful-starter-kit?status.png)](http://godoc.org/github.com/qiangxue/golang-restful-starter-kit)
[![Build Status](https://travis-ci.org/qiangxue/golang-restful-starter-kit.svg?branch=master)](https://travis-ci.org/qiangxue/golang-restful-starter-kit)
[![Coverage Status](https://coveralls.io/repos/github/qiangxue/golang-restful-starter-kit/badge.svg?branch=master)](https://coveralls.io/github/qiangxue/golang-restful-starter-kit?branch=master)
[![Go Report](https://goreportcard.com/badge/github.com/qiangxue/golang-restful-starter-kit)](https://goreportcard.com/report/github.com/qiangxue/golang-restful-starter-kit)

This starter kit is designed to get you up and running with a project structure optimal for developing
RESTful services in Go. The kit promotes the best practices that follow the [SOLID principles](https://en.wikipedia.org/wiki/SOLID_(object-oriented_design))
and encourage writing clear and idiomatic Go code.
Expand Down Expand Up @@ -46,21 +51,23 @@ cd $GOPATH/qiangxue/golang-restful-starter-kit
glide up -u -s
```

Next, create a PostgreSQL database named `music` and execute the SQL statements given in the file `data/db.sql`.
Next, create a PostgreSQL database named `go_restful` and execute the SQL statements given in the file `data/db.sql`.
The starter kit uses the following default database connection information:
* server address: 127.0.0.1 (local machine)
* server port: 5432
* database name: music
* username: music
* password: music
* server address: `127.0.0.1` (local machine)
* server port: `5432`
* database name: `go_restful`
* username: `postgres`
* password: `postgres`

If your connection is different from the above, you should define an environment variable named
`MUSIC_DSN` whose value is in the following format:
If your connection is different from the above, you may modify the configuration file `config/app.yaml`, or
define an environment variable named `RESTFUL_DSN` like the following:

```
postgres://<username>:<password>@<server-address>:<server-port>/<db-name>
```

For more details about specifying a PostgreSQL DSN, please refer to [the documentation](https://godoc.org/github.com/lib/pq).

Now you can build and run the application by running the following command under the
`$GOPATH/qiangxue/golang-restful-starter-kit` directory:

Expand Down
43 changes: 43 additions & 0 deletions app/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package app

import (
"fmt"
"time"

"github.com/spf13/viper"
)

type Config interface {
Get(key string) interface{}
GetBool(key string) bool
GetFloat64(key string) float64
GetInt(key string) int
GetString(key string) string
GetStringMap(key string) map[string]interface{}
GetStringMapString(key string) map[string]string
GetStringSlice(key string) []string
GetTime(key string) time.Time
GetDuration(key string) time.Duration
IsSet(key string) bool
}

func LoadConfig(paths ...string) (Config, error) {
v := viper.New()
v.SetDefault("error_file", "config/errors.yaml")
v.SetDefault("server_port", "8080")

v.SetEnvPrefix("restful")
v.BindEnv("dsn")
v.BindEnv("server_port")

v.SetConfigType("yaml")
v.SetConfigName("app")
for _, path := range paths {
v.AddConfigPath(path)
}
if err := v.ReadInConfig(); err != nil {
return nil, fmt.Errorf("Failed to read the configuration file: %s", err)
}

return v, nil
}
6 changes: 3 additions & 3 deletions app/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ func newRequestScope(now time.Time, logger *logrus.Logger, request *http.Request
userID := authenticate(request)
return &requestScope{
Logger: NewLogger(logger, logrus.Fields{
"UserID": userID,
"UserID": userID,
}),
now: now,
userID: userID,
now: now,
userID: userID,
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/version.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package app

var Version = "1.0"
var Version = "v1.0"
1 change: 1 addition & 0 deletions config/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dsn: "postgres://postgres:postgres@127.0.0.1:5432/go_restful?sslmode=disable"
14 changes: 12 additions & 2 deletions daos/artist.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,29 @@ import (
"github.com/qiangxue/golang-restful-starter-kit/models"
)

// ArtistDAO persists artist data in database
type ArtistDAO struct{}

// NewArtistDAO creates a new ArtistDAO
func NewArtistDAO() *ArtistDAO {
return &ArtistDAO{}
}

// Get reads the artist with the specified ID from the database.
func (dao *ArtistDAO) Get(rs app.RequestScope, id int) (*models.Artist, error) {
var artist models.Artist
err := rs.Tx().Select().Model(id, &artist)
return &artist, err
}

// Create saves a new artist record in the database.
// The Artist.Id field will be populated with an automatically generated ID upon successful saving.
func (dao *ArtistDAO) Create(rs app.RequestScope, artist *models.Artist) error {
return rs.Tx().Model(artist).Exclude("Id").Insert()
artist.Id = 0
return rs.Tx().Model(artist).Insert()
}

// Update saves the changes to an artist in the database.
func (dao *ArtistDAO) Update(rs app.RequestScope, id int, artist *models.Artist) error {
if _, err := dao.Get(rs, id); err != nil {
return err
Expand All @@ -29,20 +36,23 @@ func (dao *ArtistDAO) Update(rs app.RequestScope, id int, artist *models.Artist)
return rs.Tx().Model(artist).Exclude("Id").Update()
}

// Delete deletes an artist with the specified ID from the database.
func (dao *ArtistDAO) Delete(rs app.RequestScope, id int) error {
artist, err := dao.Get(rs, id)
if err != nil {
return err
}
return rs.Tx().Model(&artist).Delete()
return rs.Tx().Model(artist).Delete()
}

// Count returns the number of the artist records in the database.
func (dao *ArtistDAO) Count(rs app.RequestScope) (int, error) {
var count int
err := rs.Tx().Select("COUNT(*)").From("artist").Row(&count)
return count, err
}

// Query retrieves the artist records with the specified offset and limit from the database.
func (dao *ArtistDAO) Query(rs app.RequestScope, offset, limit int) ([]models.Artist, error) {
artists := []models.Artist{}
err := rs.Tx().Select().OrderBy("id").Offset(int64(offset)).Limit(int64(limit)).All(&artists)
Expand Down
104 changes: 104 additions & 0 deletions daos/artist_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package daos

import (
"testing"

"github.com/qiangxue/golang-restful-starter-kit/app"
"github.com/qiangxue/golang-restful-starter-kit/models"
"github.com/qiangxue/golang-restful-starter-kit/testdata"
"github.com/stretchr/testify/assert"
)

func TestArtistDAO(t *testing.T) {
db := testdata.ResetDB()
dao := NewArtistDAO()

{
// Get
testDBCall(db, func(rs app.RequestScope) {
artist, err := dao.Get(rs, 2)
assert.Nil(t, err)
if assert.NotNil(t, artist) {
assert.Equal(t, 2, artist.Id)
}
})
}

{
// Create
testDBCall(db, func(rs app.RequestScope) {
artist := &models.Artist{
Id: 1000,
Name: "tester",
}
err := dao.Create(rs, artist)
assert.Nil(t, err)
assert.NotEqual(t, 1000, artist.Id)
assert.NotZero(t, artist.Id)
})
}

{
// Update
testDBCall(db, func(rs app.RequestScope) {
artist := &models.Artist{
Id: 2,
Name: "tester",
}
err := dao.Update(rs, artist.Id, artist)
assert.Nil(t, rs.Tx().Commit())
assert.Nil(t, err)
})
}

{
// Update with error
testDBCall(db, func(rs app.RequestScope) {
artist := &models.Artist{
Id: 2,
Name: "tester",
}
err := dao.Update(rs, 99999, artist)
assert.Nil(t, rs.Tx().Commit())
assert.NotNil(t, err)
})
}

{
// Delete
testDBCall(db, func(rs app.RequestScope) {
err := dao.Delete(rs, 2)
assert.Nil(t, rs.Tx().Commit())
assert.Nil(t, err)
})
}

{
// Delete with error
testDBCall(db, func(rs app.RequestScope) {
err := dao.Delete(rs, 99999)
assert.Nil(t, rs.Tx().Commit())
assert.NotNil(t, err)
})
}

{
// Query
testDBCall(db, func(rs app.RequestScope) {
artists, err := dao.Query(rs, 1, 3)
assert.Nil(t, rs.Tx().Commit())
assert.Nil(t, err)
assert.Equal(t, 3, len(artists))
})
}

{
// Count
testDBCall(db, func(rs app.RequestScope) {
count, err := dao.Count(rs)
assert.Nil(t, rs.Tx().Commit())
assert.Nil(t, err)
assert.NotZero(t, count)
})
}
}
54 changes: 54 additions & 0 deletions daos/mock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package daos

import (
"time"

"github.com/go-ozzo/ozzo-dbx"
"github.com/qiangxue/golang-restful-starter-kit/app"
)

func testDBCall(db *dbx.DB, f func(rs app.RequestScope)) {
rs := mockRequestScope(db)

defer func() {
rs.Tx().Rollback()
}()

f(rs)
}

type requestScope struct {
app.Logger
tx *dbx.Tx
}

func mockRequestScope(db *dbx.DB) app.RequestScope {
tx, _ := db.Begin()
return &requestScope{
tx: tx,
}
}

func (rs *requestScope) UserID() string {
return "tester"
}

func (rs *requestScope) Tx() *dbx.Tx {
return rs.tx
}

func (rs *requestScope) SetTx(tx *dbx.Tx) {
rs.tx = tx
}

func (rs *requestScope) Rollback() bool {
return false
}

func (rs *requestScope) SetRollback(v bool) {
}

func (rs *requestScope) Now() time.Time {
return time.Now()
}

Loading

0 comments on commit 1c9a3e6

Please sign in to comment.