-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Alexander Simmerl
committed
Mar 28, 2017
1 parent
3abd66b
commit a02b0d9
Showing
4 changed files
with
290 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package invite | ||
|
||
import ( | ||
"math/rand" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/tapglue/snaas/platform/generate" | ||
) | ||
|
||
type prepareFunc func(t *testing.T, namespace string) Service | ||
|
||
func testServicePut(t *testing.T, p prepareFunc) { | ||
var ( | ||
invite = testInvite() | ||
namespace = "service_put" | ||
service = p(t, namespace) | ||
) | ||
|
||
created, err := service.Put(namespace, invite) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
list, err := service.Query(namespace, QueryOptions{ | ||
IDs: []uint64{ | ||
created.ID, | ||
}, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if have, want := len(list), 1; have != want { | ||
t.Fatalf("have %v, want %v", have, want) | ||
} | ||
if have, want := list[0], created; !reflect.DeepEqual(have, want) { | ||
t.Errorf("have %v, want %v", have, want) | ||
} | ||
|
||
created.Deleted = true | ||
|
||
updated, err := service.Put(namespace, created) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
list, err = service.Query(namespace, QueryOptions{ | ||
IDs: []uint64{ | ||
updated.ID, | ||
}, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if have, want := list[0], updated; !reflect.DeepEqual(have, want) { | ||
t.Errorf("have %v, want %v", have, want) | ||
} | ||
} | ||
|
||
func testServiceQuery(t *testing.T, p prepareFunc) { | ||
t.Errorf("testServiceQuery not implementd") | ||
} | ||
|
||
func testInvite() *Invite { | ||
return &Invite{ | ||
Deleted: false, | ||
Key: generate.RandomStringSafe(24), | ||
UserID: uint64(rand.Int63()), | ||
Value: generate.RandomStringSafe(24), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package invite | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/tapglue/snaas/platform/service" | ||
) | ||
|
||
// Invite is a loose promise to create a conection if the person assoicated with | ||
// the social id key-value signs up. | ||
type Invite struct { | ||
Deleted bool | ||
ID uint64 | ||
Key string | ||
UserID uint64 | ||
Value string | ||
CreatedAt time.Time | ||
UpdatedAt time.Time | ||
} | ||
|
||
// List is a collection of Invite. | ||
type List []*Invite | ||
|
||
// QueryOptions to narrow-down Invite queries. | ||
type QueryOptions struct { | ||
Deleted *bool | ||
IDs []uint64 | ||
Keys []string | ||
UserIDs []uint64 | ||
Values []string | ||
} | ||
|
||
// Service for Invite interactions. | ||
type Service interface { | ||
service.Lifecycle | ||
|
||
Put(namespace string, i *Invite) (*Invite, error) | ||
Query(namespace string, opts QueryOptions) (List, error) | ||
} | ||
|
||
// ServiceMiddleware is a chainable behaviour modifier for Service. | ||
type ServiceMiddleware func(Service) Service |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package invite | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/jmoiron/sqlx" | ||
"github.com/tapglue/snaas/platform/flake" | ||
"github.com/tapglue/snaas/platform/pg" | ||
) | ||
|
||
const ( | ||
pgInsertInvite = `INSERT INTO | ||
%s.invites(deleted, id, key, user_id, value, created_at, updated_at) | ||
VALUES($1, $2, $3, $4, $5, $6, $7)` | ||
pgUpdateInvite = ` | ||
UPDATE | ||
%s.invites | ||
SET | ||
deleted = $2 | ||
updated_at = $3 | ||
WHERE | ||
id = $1` | ||
|
||
pgClauseDeleted = `deleted = ?` | ||
pgClauseKeys = `key IN (?)` | ||
pgClauseUserIDs = `user_id IN (?)` | ||
pgClauseValues = `value IN (?)` | ||
|
||
pgListInvites = ` | ||
SELECT | ||
deleted, id, key, user_id, value, created_at, updated_at | ||
FROM | ||
%s.invites | ||
%s` | ||
|
||
pgOrderCreatedAt = `ORDER BY created_at DESC` | ||
|
||
pgCreateScheme = `CREATE SCHEMA IF NOT EXISTS %s` | ||
pgCreateTable = `CREATE TABLE IF NOT EXISTS %s.invites( | ||
deleted BOOL DEFAULT false, | ||
id BIGINT NOT NULL UNIQUE, | ||
key TEXT NOT NULL, | ||
uesr_id BIGINT NOT NULL, | ||
value TEXT NOT NULL, | ||
created_at TIMESTAMP NOT NULL, | ||
updated_at TIMESTAMP NOT NULL | ||
)` | ||
pgDropTable = `DROP TABLE IF EXISTS %s.invites` | ||
) | ||
|
||
type pgService struct { | ||
db *sqlx.DB | ||
} | ||
|
||
// PostgresService returns a Postgres based Service implementation. | ||
func PostgresService(db *sqlx.DB) Service { | ||
return &pgService{ | ||
db: db, | ||
} | ||
} | ||
|
||
func (s *pgService) Put(ns string, i *Invite) (*Invite, error) { | ||
if i.ID == 0 { | ||
return s.insert(ns, i) | ||
} | ||
|
||
return nil, fmt.Errorf("invite.Put/update not implemented") | ||
} | ||
|
||
func (s *pgService) Query(ns string, opts QueryOptions) (List, error) { | ||
return nil, fmt.Errorf("invite.Query not implemented") | ||
} | ||
|
||
func (s *pgService) Setup(ns string) error { | ||
qs := []string{ | ||
fmt.Sprintf(pgCreateScheme, ns), | ||
fmt.Sprintf(pgCreateTable, ns), | ||
} | ||
|
||
for _, q := range qs { | ||
_, err := s.db.Exec(q) | ||
if err != nil { | ||
return fmt.Errorf("setup '%s': %s", q, err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (s *pgService) Teardown(ns string) error { | ||
qs := []string{ | ||
fmt.Sprintf(pgDropTable, ns), | ||
} | ||
|
||
for _, q := range qs { | ||
_, err := s.db.Exec(q) | ||
if err != nil { | ||
return fmt.Errorf("teardown '%s': %s", q, err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (s *pgService) insert(ns string, i *Invite) (*Invite, error) { | ||
if i.CreatedAt.IsZero() { | ||
i.CreatedAt = time.Now().UTC() | ||
} | ||
|
||
ts, err := time.Parse(pg.TimeFormat, i.CreatedAt.UTC().Format(pg.TimeFormat)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
i.CreatedAt = ts | ||
i.UpdatedAt = ts | ||
|
||
id, err := flake.NextID(flakeNamespace(ns)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// +build integration | ||
|
||
package invite | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"os/user" | ||
"testing" | ||
|
||
"github.com/jmoiron/sqlx" | ||
"github.com/tapglue/snaas/platform/pg" | ||
) | ||
|
||
var pgTestURL string | ||
|
||
func TestPostgresPut(t *testing.T) { | ||
testServicePut(t, preparePostgres) | ||
} | ||
|
||
func TestPostgresQuery(t *testing.T) { | ||
testServiceQuery(t, preparePostgres) | ||
} | ||
|
||
func preparePostgres(t *testing.T, namespace string) Service { | ||
db, err := sqlx.Connect("postgres", pgTestURL) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
s := PostgresService(db) | ||
|
||
if err := s.Teardown(namespace); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
return s | ||
} | ||
|
||
func init() { | ||
u, err := user.Current() | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
d := fmt.Sprintf(pg.URLTest, u.Username) | ||
|
||
url := flag.String("postgres.url", d, "Postgres test connection URL") | ||
flag.Parse() | ||
|
||
pgTestURL = *url | ||
} |