Skip to content

Commit

Permalink
Merge pull request #38 from tapglue/invite-flow
Browse files Browse the repository at this point in the history
Invite flow
  • Loading branch information
xla authored Mar 28, 2017
2 parents b8e4ee4 + 47bbc60 commit d688dda
Show file tree
Hide file tree
Showing 14 changed files with 1,036 additions and 27 deletions.
23 changes: 23 additions & 0 deletions cmd/gateway-http/gateway-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/tapglue/snaas/service/connection"
"github.com/tapglue/snaas/service/device"
"github.com/tapglue/snaas/service/event"
"github.com/tapglue/snaas/service/invite"
"github.com/tapglue/snaas/service/object"
"github.com/tapglue/snaas/service/reaction"
"github.com/tapglue/snaas/service/session"
Expand Down Expand Up @@ -355,6 +356,17 @@ func main() {
// TODO: Implement write path to avoid stale counts.
// events = event.CacheServiceMiddleware(eventCountsCache)(events)

var invites invite.Service
invites = invite.PostgresService(pgClient)
invites = invite.InstrumentServiceMiddleware(
component,
storeService,
serviceErrCount,
serviceOpCount,
serviceOpLatency,
)(invites)
invites = invite.LogServiceMiddleware(logger, storeService)(invites)

var objects object.Service
objects = object.PostgresService(pgClient)
objects = object.InstrumentServiceMiddleware(
Expand Down Expand Up @@ -587,6 +599,16 @@ func main() {
),
)

// Invite routes.
current.Methods("POST").Path(`/me/invites`).Name("deviceCreate").HandlerFunc(
handler.Wrap(
withUser,
handler.InviteCreate(
core.InviteCreate(invites),
),
),
)

// Post routes.
current.Methods("POST").Path("/posts").Name("postCreate").HandlerFunc(
handler.Wrap(
Expand Down Expand Up @@ -867,6 +889,7 @@ func main() {
withApp,
handler.UserCreate(
core.UserCreate(sessions, users),
core.UserCreateWithInvite(connections, invites, sessions, users),
),
),
)
Expand Down
51 changes: 51 additions & 0 deletions core/invite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package core

import (
"github.com/tapglue/snaas/service/app"
"github.com/tapglue/snaas/service/invite"
)

// InviteCreateFunc stores the key and value for the users invite.
type InviteCreateFunc func(
currentApp *app.App,
origin Origin,
key, value string,
) error

func InviteCreate(invites invite.Service) InviteCreateFunc {
return func(
currentApp *app.App,
origin Origin,
key, value string,
) error {
is, err := invites.Query(currentApp.Namespace(), invite.QueryOptions{
Keys: []string{
key,
},
UserIDs: []uint64{
origin.UserID,
},
Values: []string{
value,
},
})
if err != nil {
return err
}

if len(is) == 1 {
return nil
}

_, err = invites.Put(currentApp.Namespace(), &invite.Invite{
Key: key,
UserID: origin.UserID,
Value: value,
})
if err != nil {
return err
}

return nil
}
}
76 changes: 76 additions & 0 deletions core/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/tapglue/snaas/platform/generate"
"github.com/tapglue/snaas/service/app"
"github.com/tapglue/snaas/service/connection"
"github.com/tapglue/snaas/service/invite"
"github.com/tapglue/snaas/service/session"
"github.com/tapglue/snaas/service/user"
)
Expand Down Expand Up @@ -60,6 +61,38 @@ func UserCreate(
}
}

// UserCreateWithInviteFunc stores the provided user, creates a session and
// sets up pending connections for open invites.
type UserCreateWithInviteFunc func(
currentApp *app.App,
origin Origin,
u *user.User,
conType connection.Type,
) (*user.User, error)

// UserCreateWithInvite stores the provided user and creates a session.
func UserCreateWithInvite(
connections connection.Service,
invites invite.Service,
sessions session.Service,
users user.Service,
) UserCreateWithInviteFunc {
return func(
currentApp *app.App,
origin Origin,
u *user.User,
conType connection.Type,
) (output *user.User, err error) {
defer func() {
if err == nil {
mapInvites(connections, invites, currentApp, u, conType)
}
}()

return UserCreate(sessions, users)(currentApp, origin, u)
}
}

// UserDeleteFunc disables the user.
type UserDeleteFunc func(
currentApp *app.App,
Expand Down Expand Up @@ -454,6 +487,7 @@ func UserUpdate(
// UsersFetchFunc retrieves the users for the given ids.
type UsersFetchFunc func(currentApp *app.App, ids ...uint64) (user.List, error)

// UsersFetch retrieves the users for the given ids.
func UsersFetch(users user.Service) UsersFetchFunc {
return func(currentApp *app.App, ids ...uint64) (user.List, error) {
if len(ids) == 0 {
Expand Down Expand Up @@ -664,6 +698,48 @@ func login(
return u, nil
}

func mapInvites(
connections connection.Service,
invites invite.Service,
currentApp *app.App,
u *user.User,
t connection.Type,
) {
for k, v := range u.SocialIDs {
is, err := invites.Query(currentApp.Namespace(), invite.QueryOptions{
Deleted: &defaultDeleted,
Keys: []string{
k,
},
Values: []string{
v,
},
})
if err != nil {
continue
}

for _, i := range is {
_, err := connections.Put(currentApp.Namespace(), &connection.Connection{
FromID: i.UserID,
State: connection.StatePending,
Type: t,
ToID: u.ID,
})
if err != nil {
continue
}

i.Deleted = true

_, err = invites.Put(currentApp.Namespace(), i)
if err != nil {
continue
}
}
}
}

func passwordCompare(dec, enc string) (bool, error) {
d, err := base64.StdEncoding.DecodeString(enc)
if err != nil {
Expand Down
40 changes: 40 additions & 0 deletions handler/http/invite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package http

import (
"encoding/json"
"net/http"

"golang.org/x/net/context"

"github.com/tapglue/snaas/core"
)

// InviteCreate stores the key and value for a users invite.
func InviteCreate(fn core.InviteCreateFunc) Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
var (
currentApp = appFromContext(ctx)
origin = originFromContext(ctx)
p = payloadInvite{}
)

err := json.NewDecoder(r.Body).Decode(&p)
if err != nil {
respondError(w, 0, wrapError(ErrBadRequest, err.Error()))
return
}

err = fn(currentApp, origin, p.Key, p.Value)
if err != nil {
respondError(w, 0, err)
return
}

respondJSON(w, http.StatusNoContent, nil)
}
}

type payloadInvite struct {
Key string `json:"key"`
Value string `json:"value"`
}
42 changes: 30 additions & 12 deletions handler/http/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,19 @@ const (

headerForwardedProto = "X-Forwarded-Proto"

keyAppID = "appID"
keyCommentID = "commentID"
keyCursorAfter = "after"
keyCursorBefore = "before"
keyLimit = "limit"
keyPostID = "postID"
keyReactionType = "reactionType"
keyRuleID = "ruleID"
keyState = "state"
keyUserID = "userID"
keyUserQuery = "q"
keyWhere = "where"
keyAppID = "appID"
keyCommentID = "commentID"
keyCursorAfter = "after"
keyCursorBefore = "before"
keyInviteConnections = "invite-connections"
keyLimit = "limit"
keyPostID = "postID"
keyReactionType = "reactionType"
keyRuleID = "ruleID"
keyState = "state"
keyUserID = "userID"
keyUserQuery = "q"
keyWhere = "where"

limitDefault = 25
limitMax = 50
Expand Down Expand Up @@ -241,6 +242,23 @@ func extractIDCursorBefore(r *http.Request) (uint64, error) {
return strconv.ParseUint(string(cursor), 10, 64)
}

func extractInviteConnections(r *http.Request) (bool, connection.Type) {
param := r.URL.Query().Get(keyInviteConnections)

if param == "" {
return false, ""
}

switch connection.Type(param) {
case connection.TypeFollow:
return true, connection.TypeFollow
case connection.TypeFriend:
return true, connection.TypeFriend
default:
return false, ""
}
}

func extractLikeOpts(r *http.Request) (event.QueryOptions, error) {
return event.QueryOptions{}, nil
}
Expand Down
25 changes: 17 additions & 8 deletions handler/http/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ import (
)

// UserCreate stores the provided user and returns it with a valid session.
func UserCreate(fn core.UserCreateFunc) Handler {
func UserCreate(
createFn core.UserCreateFunc,
createWithInviteFn core.UserCreateWithInviteFunc,
) Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
var (
currentApp = appFromContext(ctx)
deviceID = deviceIDFromContext(ctx)
p = payloadUser{}
tokenType = tokenTypeFromContext(ctx)

origin = createOrigin(deviceID, tokenType, 0)
currentApp = appFromContext(ctx)
deviceID = deviceIDFromContext(ctx)
invite, conType = extractInviteConnections(r)
p = payloadUser{}
tokenType = tokenTypeFromContext(ctx)
origin = createOrigin(deviceID, tokenType, 0)
)

err := json.NewDecoder(r.Body).Decode(&p)
Expand All @@ -32,7 +35,13 @@ func UserCreate(fn core.UserCreateFunc) Handler {
return
}

u, err := fn(currentApp, origin, p.user)
var u *user.User

if invite {
u, err = createWithInviteFn(currentApp, origin, p.user, conType)
} else {
u, err = createFn(currentApp, origin, p.user)
}
if err != nil {
respondError(w, 0, err)
return
Expand Down
8 changes: 8 additions & 0 deletions platform/flake/flake.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package flake

import (
"fmt"
"time"

"github.com/sony/sonyflake"
)

const fmtNamespace = "%s_%s"

var flakes = map[string]*sonyflake.Sonyflake{}

// Namespace returns the prefixed entity path.
func Namespace(prefix, entity string) string {
return fmt.Sprintf(fmtNamespace, prefix, entity)
}

// NextID returns the next safe to use ID for the given namespace.
func NextID(namespace string) (uint64, error) {
if _, ok := flakes[namespace]; !ok {
Expand Down
Loading

0 comments on commit d688dda

Please sign in to comment.