Skip to content

Commit 8decfa6

Browse files
committedJul 10, 2017
Ban users and create groups from within the Runtime environment. Merged heroiclabs#88
1 parent 9bd122f commit 8decfa6

11 files changed

+669
-211
lines changed
 

‎CHANGELOG.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ The format is based on [keep a changelog](http://keepachangelog.com/) and this p
66
## [Unreleased]
77
### Added
88
- New storage list feature.
9+
- Ban users and create groups from within the Runtime environment.
910

1011
### Changed
1112
- Run Facebook friends import after registration completes.
12-
- Streamline command line flags to be inline with the config file.
13-
14-
### Changed
13+
- Streamline command line flags to be inline with the config file.
1514
- Restructure and stabilize API messages.
16-
- Add batch operations Friends social graph.
15+
- Update Runtime modules to use plural function names for batch operations. (`users_fetch_id` and `users_fetch_handle`)
1716

1817
### Fixed
1918
- Invocation type was always set to "Before" in After Runtime scripts.

‎server/api.proto

+43-52
Original file line numberDiff line numberDiff line change
@@ -213,50 +213,48 @@ message Envelope {
213213
TGroupUsersAdd group_users_add = 26;
214214
TGroupUsersKick group_users_kick = 27;
215215
TGroupUsersPromote group_users_promote = 28;
216-
TGroup group = 29;
217-
TGroups groups = 30;
218-
TGroupUsers group_users = 31;
219-
220-
TTopicsJoin topics_join = 32;
221-
TTopicsLeave topics_leave = 33;
222-
TTopicMessageSend topic_message_send = 34;
223-
TTopicMessagesList topic_messages_list = 35;
224-
TTopics topics = 36;
225-
TTopicMessageAck topic_message_ack = 37;
226-
TopicMessage topic_message = 38;
227-
TTopicMessages topic_messages = 39;
228-
TopicPresence topic_presence = 40;
229-
230-
TMatchCreate match_create = 41;
231-
TMatchesJoin matches_join = 42;
232-
TMatchesLeave matches_leave = 43;
233-
MatchDataSend match_data_send = 44;
234-
TMatch match = 45;
235-
TMatches matches = 46;
236-
MatchData match_data = 47;
237-
MatchPresence match_presence = 48;
238-
239-
TStorageList storage_list = 49;
240-
TStorageFetch storage_fetch = 50;
241-
TStorageWrite storage_write = 51;
242-
TStorageRemove storage_remove = 52;
243-
TStorageData storage_data = 53;
244-
TStorageKeys storage_keys = 54;
245-
246-
TLeaderboardsList leaderboards_list = 55;
247-
TLeaderboardRecordsWrite leaderboard_records_write = 56;
248-
TLeaderboardRecordsFetch leaderboard_records_fetch = 57;
249-
TLeaderboardRecordsList leaderboard_records_list = 58;
250-
TLeaderboards leaderboards = 59;
251-
252-
TLeaderboardRecords leaderboard_records = 60;
253-
254-
TMatchmakeAdd matchmake_add = 61;
255-
TMatchmakeRemove matchmake_remove = 62;
256-
TMatchmakeTicket matchmake_ticket = 63;
257-
MatchmakeMatched matchmake_matched = 64;
258-
259-
TRpc rpc = 65;
216+
TGroups groups = 29;
217+
TGroupUsers group_users = 30;
218+
219+
TTopicsJoin topics_join = 31;
220+
TTopicsLeave topics_leave = 32;
221+
TTopicMessageSend topic_message_send = 33;
222+
TTopicMessagesList topic_messages_list = 34;
223+
TTopics topics = 35;
224+
TTopicMessageAck topic_message_ack = 36;
225+
TopicMessage topic_message = 37;
226+
TTopicMessages topic_messages = 38;
227+
TopicPresence topic_presence = 39;
228+
229+
TMatchCreate match_create = 40;
230+
TMatchesJoin matches_join = 41;
231+
TMatchesLeave matches_leave = 42;
232+
MatchDataSend match_data_send = 43;
233+
TMatch match = 44;
234+
TMatches matches = 45;
235+
MatchData match_data = 46;
236+
MatchPresence match_presence = 47;
237+
238+
TStorageList storage_list = 48;
239+
TStorageFetch storage_fetch = 49;
240+
TStorageWrite storage_write = 50;
241+
TStorageRemove storage_remove = 51;
242+
TStorageData storage_data = 52;
243+
TStorageKeys storage_keys = 53;
244+
245+
TLeaderboardsList leaderboards_list = 54;
246+
TLeaderboardRecordsWrite leaderboard_records_write = 55;
247+
TLeaderboardRecordsFetch leaderboard_records_fetch = 56;
248+
TLeaderboardRecordsList leaderboard_records_list = 57;
249+
TLeaderboards leaderboards = 58;
250+
TLeaderboardRecords leaderboard_records = 59;
251+
252+
TMatchmakeAdd matchmake_add = 60;
253+
TMatchmakeRemove matchmake_remove = 61;
254+
TMatchmakeTicket matchmake_ticket = 62;
255+
MatchmakeMatched matchmake_matched = 63;
256+
257+
TRpc rpc = 64;
260258
}
261259
}
262260

@@ -509,7 +507,7 @@ message Group {
509507
/**
510508
* TGroupsCreate creates a new group.
511509
*
512-
* @returns TGroup
510+
* @returns TGroups
513511
*
514512
* NOTE: The server only processes the first item of the list, and will ignore and logs a warning message for other items.
515513
*/
@@ -529,13 +527,6 @@ message TGroupsCreate {
529527
repeated GroupCreate groups = 1;
530528
}
531529

532-
/**
533-
* TGroup contains the group information.
534-
*/
535-
message TGroup {
536-
Group group = 1;
537-
}
538-
539530
/**
540531
* TGroupsUpdate updates the group with matching Group ID.
541532
* Only group admins can update group information.

‎server/core_group.go

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// Copyright 2017 The Nakama Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package server
16+
17+
import (
18+
"database/sql"
19+
"errors"
20+
"strconv"
21+
"strings"
22+
23+
"github.com/satori/go.uuid"
24+
"go.uber.org/zap"
25+
)
26+
27+
type GroupCreateParam struct {
28+
Name string // mandatory
29+
Creator uuid.UUID // mandatory
30+
Description string
31+
AvatarURL string
32+
Lang string
33+
Metadata []byte
34+
Private bool
35+
}
36+
37+
func extractGroup(r scanner) (*Group, error) {
38+
var id []byte
39+
var creatorID []byte
40+
var name sql.NullString
41+
var description sql.NullString
42+
var avatarURL sql.NullString
43+
var lang sql.NullString
44+
var utcOffsetMs sql.NullInt64
45+
var metadata []byte
46+
var state sql.NullInt64
47+
var count sql.NullInt64
48+
var createdAt sql.NullInt64
49+
var updatedAt sql.NullInt64
50+
51+
err := r.Scan(&id, &creatorID, &name,
52+
&description, &avatarURL, &lang,
53+
&utcOffsetMs, &metadata, &state,
54+
&count, &createdAt, &updatedAt)
55+
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
desc := ""
61+
if description.Valid {
62+
desc = description.String
63+
}
64+
65+
avatar := ""
66+
if avatarURL.Valid {
67+
avatar = avatarURL.String
68+
}
69+
70+
private := state.Int64 == 1
71+
72+
return &Group{
73+
Id: id,
74+
CreatorId: creatorID,
75+
Name: name.String,
76+
Description: desc,
77+
AvatarUrl: avatar,
78+
Lang: lang.String,
79+
UtcOffsetMs: utcOffsetMs.Int64,
80+
Metadata: metadata,
81+
Private: private,
82+
Count: count.Int64,
83+
CreatedAt: createdAt.Int64,
84+
UpdatedAt: updatedAt.Int64,
85+
}, nil
86+
}
87+
88+
func GroupsCreate(logger *zap.Logger, db *sql.DB, groupCreateParams []*GroupCreateParam) ([]*Group, error) {
89+
if groupCreateParams == nil || len(groupCreateParams) == 0 {
90+
return nil, errors.New("Could not create groups. At least one group param must be supplied")
91+
}
92+
93+
groups := make([]*Group, 0)
94+
tx, err := db.Begin()
95+
if err != nil {
96+
logger.Error("Could not create groups", zap.Error(err))
97+
return nil, err
98+
}
99+
100+
defer func() {
101+
if err != nil {
102+
logger.Error("Could not create groups", zap.Error(err))
103+
if tx != nil {
104+
txErr := tx.Rollback()
105+
if txErr != nil {
106+
logger.Error("Could not rollback transaction", zap.Error(txErr))
107+
}
108+
}
109+
} else {
110+
err = tx.Commit()
111+
if err != nil {
112+
logger.Error("Could not commit transaction", zap.Error(err))
113+
} else {
114+
groupNames := make([]string, 0)
115+
for _, p := range groups {
116+
groupNames = append(groupNames, p.Name)
117+
}
118+
logger.Debug("Created new groups", zap.Strings("names", groupNames))
119+
}
120+
}
121+
}()
122+
123+
for _, g := range groupCreateParams {
124+
newGroup, err := groupCreate(tx, g)
125+
if err != nil {
126+
logger.Warn("Could not create group", zap.String("name", g.Name), zap.Error(err))
127+
return nil, err
128+
}
129+
130+
groups = append(groups, newGroup)
131+
}
132+
133+
return groups, err
134+
}
135+
136+
func groupCreate(tx *sql.Tx, g *GroupCreateParam) (*Group, error) {
137+
if g.Name == "" {
138+
return nil, errors.New("Group name must not be empty")
139+
}
140+
if uuid.Equal(uuid.Nil, g.Creator) {
141+
return nil, errors.New("Group creator must be set")
142+
}
143+
144+
state := 0
145+
if g.Private {
146+
state = 1
147+
}
148+
149+
columns := make([]string, 0)
150+
params := make([]string, 0)
151+
152+
values := []interface{}{
153+
uuid.NewV4().Bytes(),
154+
g.Creator.Bytes(),
155+
g.Name,
156+
state,
157+
nowMs(), // updated_at
158+
}
159+
160+
if g.Description != "" {
161+
columns = append(columns, "description")
162+
params = append(params, "$"+strconv.Itoa(len(values)+1))
163+
values = append(values, g.Description)
164+
}
165+
166+
if g.AvatarURL != "" {
167+
columns = append(columns, "avatar_url")
168+
params = append(params, "$"+strconv.Itoa(len(values)+1))
169+
values = append(values, g.AvatarURL)
170+
}
171+
172+
if g.Lang != "" {
173+
columns = append(columns, "lang")
174+
params = append(params, "$"+strconv.Itoa(len(values)+1))
175+
values = append(values, g.Lang)
176+
}
177+
178+
if g.Metadata != nil {
179+
columns = append(columns, "metadata")
180+
params = append(params, "$"+strconv.Itoa(len(values)+1))
181+
values = append(values, g.Metadata)
182+
}
183+
184+
r := tx.QueryRow(`
185+
INSERT INTO groups (id, creator_id, name, state, count, created_at, updated_at, `+strings.Join(columns, ", ")+")"+`
186+
VALUES ($1, $2, $3, $4, 1, $5, $5, `+strings.Join(params, ",")+")"+`
187+
RETURNING id, creator_id, name, description, avatar_url, lang, utc_offset_ms, metadata, state, count, created_at, updated_at
188+
`, values...)
189+
190+
group, err := extractGroup(r)
191+
if err != nil {
192+
return nil, err
193+
}
194+
195+
res, err := tx.Exec(`
196+
INSERT INTO group_edge (source_id, position, updated_at, destination_id, state)
197+
VALUES ($1, $2, $2, $3, 0), ($3, $2, $2, $1, 0)`,
198+
group.Id, updatedAt, g.Creator.Bytes())
199+
200+
if err != nil {
201+
return nil, err
202+
}
203+
204+
rowAffected, err := res.RowsAffected()
205+
if err != nil {
206+
return nil, err
207+
}
208+
if rowAffected == 0 {
209+
err = errors.New("Could not insert into group_edge table")
210+
return nil, err
211+
}
212+
213+
return group, nil
214+
}

‎server/core_user.go

+46-5
Original file line numberDiff line numberDiff line change
@@ -128,33 +128,34 @@ func UsersFetchHandle(logger *zap.Logger, db *sql.DB, handles []string) ([]*User
128128
}
129129

130130
func UsersFetchIdsHandles(logger *zap.Logger, db *sql.DB, userIds [][]byte, handles []string) ([]*User, error) {
131-
statements := make([]string, 0)
131+
idStatements := make([]string, 0)
132+
handleStatements := make([]string, 0)
132133
params := make([]interface{}, 0)
133134

134135
counter := 1
135136
for _, userID := range userIds {
136137
statement := "$" + strconv.Itoa(counter)
137138
counter += 1
138-
statements = append(statements, statement)
139+
idStatements = append(idStatements, statement)
139140
params = append(params, userID)
140141
}
141142
for _, handle := range handles {
142143
statement := "$" + strconv.Itoa(counter)
143144
counter += 1
144-
statements = append(statements, statement)
145+
handleStatements = append(handleStatements, statement)
145146
params = append(params, handle)
146147
}
147148

148149
query := "WHERE "
149150
if len(userIds) > 0 {
150-
query += "users.id IN (" + strings.Join(statements, ", ") + ")"
151+
query += "users.id IN (" + strings.Join(idStatements, ", ") + ")"
151152
}
152153

153154
if len(handles) > 0 {
154155
if len(userIds) > 0 {
155156
query += " OR "
156157
}
157-
query += "users.handle IN (" + strings.Join(statements, ", ") + ")"
158+
query += "users.handle IN (" + strings.Join(handleStatements, ", ") + ")"
158159
}
159160

160161
users, err := querySocialGraph(logger, db, query, params)
@@ -164,3 +165,43 @@ func UsersFetchIdsHandles(logger *zap.Logger, db *sql.DB, userIds [][]byte, hand
164165

165166
return users, nil
166167
}
168+
169+
func UsersBan(logger *zap.Logger, db *sql.DB, userIds [][]byte, handles []string) error {
170+
idStatements := make([]string, 0)
171+
handleStatements := make([]string, 0)
172+
params := []interface{}{nowMs()} // $1
173+
174+
counter := 2
175+
for _, userID := range userIds {
176+
statement := "$" + strconv.Itoa(counter)
177+
idStatements = append(idStatements, statement)
178+
params = append(params, userID)
179+
counter++
180+
}
181+
for _, handle := range handles {
182+
statement := "$" + strconv.Itoa(counter)
183+
handleStatements = append(handleStatements, statement)
184+
params = append(params, handle)
185+
counter++
186+
}
187+
188+
query := "UPDATE users SET disabled_at = $1 WHERE "
189+
if len(userIds) > 0 {
190+
query += "users.id IN (" + strings.Join(idStatements, ", ") + ")"
191+
}
192+
193+
if len(handles) > 0 {
194+
if len(userIds) > 0 {
195+
query += " OR "
196+
}
197+
query += "users.handle IN (" + strings.Join(handleStatements, ", ") + ")"
198+
}
199+
200+
logger.Debug("ban user query", zap.String("query", query))
201+
_, err := db.Exec(query, params...)
202+
if err != nil {
203+
logger.Error("Failed to ban users", zap.Error(err))
204+
}
205+
206+
return err
207+
}

‎server/pipeline_group.go

+6-57
Original file line numberDiff line numberDiff line change
@@ -38,57 +38,6 @@ type groupCursor struct {
3838
GroupID []byte
3939
}
4040

41-
func (p *pipeline) extractGroup(r scanner) (*Group, error) {
42-
var id []byte
43-
var creatorID []byte
44-
var name sql.NullString
45-
var description sql.NullString
46-
var avatarURL sql.NullString
47-
var lang sql.NullString
48-
var utcOffsetMs sql.NullInt64
49-
var metadata []byte
50-
var state sql.NullInt64
51-
var count sql.NullInt64
52-
var createdAt sql.NullInt64
53-
var updatedAt sql.NullInt64
54-
55-
err := r.Scan(&id, &creatorID, &name,
56-
&description, &avatarURL, &lang,
57-
&utcOffsetMs, &metadata, &state,
58-
&count, &createdAt, &updatedAt)
59-
60-
if err != nil {
61-
return &Group{}, err
62-
}
63-
64-
desc := ""
65-
if description.Valid {
66-
desc = description.String
67-
}
68-
69-
avatar := ""
70-
if avatarURL.Valid {
71-
avatar = avatarURL.String
72-
}
73-
74-
private := state.Int64 == 1
75-
76-
return &Group{
77-
Id: id,
78-
CreatorId: creatorID,
79-
Name: name.String,
80-
Description: desc,
81-
AvatarUrl: avatar,
82-
Lang: lang.String,
83-
UtcOffsetMs: utcOffsetMs.Int64,
84-
Metadata: metadata,
85-
Private: private,
86-
Count: count.Int64,
87-
CreatedAt: createdAt.Int64,
88-
UpdatedAt: updatedAt.Int64,
89-
}, nil
90-
}
91-
9241
func (p *pipeline) groupCreate(logger *zap.Logger, session *session, envelope *Envelope) {
9342
e := envelope.GetGroupsCreate()
9443

@@ -135,7 +84,7 @@ func (p *pipeline) groupCreate(logger *zap.Logger, session *session, envelope *E
13584
session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not create group"))
13685
} else {
13786
logger.Info("Created new group", zap.String("name", group.Name))
138-
session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Group{Group: &TGroup{Group: group}}})
87+
session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Groups{&TGroups{Groups: []*Group{group}}}})
13988
}
14089
}
14190
}()
@@ -184,7 +133,7 @@ func (p *pipeline) groupCreate(logger *zap.Logger, session *session, envelope *E
184133
}
185134

186135
columns = append(columns, "metadata")
187-
params = append(params, "$"+strconv.Itoa(len(values)))
136+
params = append(params, "$"+strconv.Itoa(len(values)+1))
188137
values = append(values, g.Metadata)
189138
}
190139

@@ -194,7 +143,7 @@ VALUES ($1, $2, $3, $4, 1, $5, $5, `+strings.Join(params, ",")+")"+`
194143
RETURNING id, creator_id, name, description, avatar_url, lang, utc_offset_ms, metadata, state, count, created_at, updated_at
195144
`, values...)
196145

197-
group, err = p.extractGroup(r)
146+
group, err = extractGroup(r)
198147
if err != nil {
199148
return
200149
}
@@ -421,7 +370,7 @@ FROM groups WHERE disabled_at = 0 AND ( `+strings.Join(statements, " OR ")+" )",
421370

422371
groups := make([]*Group, 0)
423372
for rows.Next() {
424-
group, err := p.extractGroup(rows)
373+
group, err := extractGroup(rows)
425374
if err != nil {
426375
logger.Error("Could not get groups", zap.Error(err))
427376
session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not get groups"))
@@ -530,7 +479,7 @@ LIMIT $` + strconv.Itoa(len(params))
530479
cursor = cursorBuf.Bytes()
531480
break
532481
}
533-
lastGroup, err = p.extractGroup(rows)
482+
lastGroup, err = extractGroup(rows)
534483
if err != nil {
535484
logger.Error("Could not list groups", zap.Error(err))
536485
session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list groups"))
@@ -564,7 +513,7 @@ WHERE group_edge.destination_id = $1 AND disabled_at = 0 AND (group_edge.state =
564513
groups := make([]*Group, 0)
565514
var lastGroup *Group
566515
for rows.Next() {
567-
lastGroup, err = p.extractGroup(rows)
516+
lastGroup, err = extractGroup(rows)
568517
if err != nil {
569518
logger.Error("Could not list joined groups", zap.Error(err))
570519
session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list joined groups"))

‎server/runtime_nakama_module.go

+120-4
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,15 @@ func (n *NakamaModule) Loader(l *lua.LState) int {
6969
"register_before": n.registerBefore,
7070
"register_after": n.registerAfter,
7171
"register_http": n.registerHTTP,
72-
"user_fetch_id": n.userFetchId,
73-
"user_fetch_handle": n.userFetchHandle,
72+
"users_fetch_id": n.usersFetchId,
73+
"users_fetch_handle": n.usersFetchHandle,
74+
"users_ban": n.usersBan,
7475
"storage_list": n.storageList,
7576
"storage_fetch": n.storageFetch,
7677
"storage_write": n.storageWrite,
7778
"storage_remove": n.storageRemove,
7879
"leaderboard_create": n.leaderboardCreate,
80+
"groups_create": n.groupsCreate,
7981
})
8082

8183
l.Push(mod)
@@ -214,7 +216,7 @@ func (n *NakamaModule) registerHTTP(l *lua.LState) int {
214216
return 0
215217
}
216218

217-
func (n *NakamaModule) userFetchId(l *lua.LState) int {
219+
func (n *NakamaModule) usersFetchId(l *lua.LState) int {
218220
lt := l.CheckTable(1)
219221
userIds, ok := convertLuaValue(lt).([]interface{})
220222
if !ok {
@@ -256,7 +258,7 @@ func (n *NakamaModule) userFetchId(l *lua.LState) int {
256258
return 1
257259
}
258260

259-
func (n *NakamaModule) userFetchHandle(l *lua.LState) int {
261+
func (n *NakamaModule) usersFetchHandle(l *lua.LState) int {
260262
lt := l.CheckTable(1)
261263
handles, ok := convertLuaValue(lt).([]interface{})
262264
if !ok {
@@ -293,6 +295,41 @@ func (n *NakamaModule) userFetchHandle(l *lua.LState) int {
293295
return 1
294296
}
295297

298+
func (n *NakamaModule) usersBan(l *lua.LState) int {
299+
usersTable := l.CheckTable(1)
300+
if usersTable == nil || usersTable.Len() == 0 {
301+
l.ArgError(1, "expects a valid set of users")
302+
return 0
303+
}
304+
usersRaw, ok := convertLuaValue(usersTable).([]interface{})
305+
if !ok {
306+
l.ArgError(1, "expects a valid set of users")
307+
return 0
308+
}
309+
310+
ids := make([][]byte, 0)
311+
handles := make([]string, 0)
312+
for _, d := range usersRaw {
313+
if m, ok := d.(string); !ok {
314+
l.ArgError(1, "expects a valid set of user IDs or handles")
315+
return 0
316+
} else {
317+
uid, err := uuid.FromString(m)
318+
if err == nil {
319+
ids = append(ids, uid.Bytes())
320+
} else {
321+
handles = append(handles, m) // assume that they've passed in a handle
322+
}
323+
}
324+
}
325+
326+
if err := UsersBan(n.logger, n.db, ids, handles); err != nil {
327+
l.RaiseError(fmt.Sprintf("failed to ban users: %s", err.Error()))
328+
}
329+
330+
return 0
331+
}
332+
296333
func (n *NakamaModule) storageList(l *lua.LState) int {
297334
var userID []byte
298335
if us := l.OptString(1, ""); us != "" {
@@ -726,3 +763,82 @@ func (n *NakamaModule) leaderboardCreate(l *lua.LState) int {
726763

727764
return 0
728765
}
766+
767+
func (n *NakamaModule) groupsCreate(l *lua.LState) int {
768+
groupsTable := l.CheckTable(1)
769+
if groupsTable == nil || groupsTable.Len() == 0 {
770+
l.ArgError(1, "expects a valid set of groups")
771+
return 0
772+
}
773+
774+
conversionError := false
775+
groupParams := make([]*GroupCreateParam, 0)
776+
777+
groupsTable.ForEach(func(i lua.LValue, g lua.LValue) {
778+
groupTable, ok := g.(*lua.LTable)
779+
if !ok {
780+
conversionError = true
781+
return
782+
}
783+
784+
groupTable.ForEach(func(k lua.LValue, v lua.LValue) {
785+
p := &GroupCreateParam{}
786+
switch k.String() {
787+
case "Name":
788+
p.Name = v.String()
789+
case "Description":
790+
p.Description = v.String()
791+
case "AvatarUrl":
792+
p.AvatarURL = v.String()
793+
case "Lang":
794+
p.Lang = v.String()
795+
case "Private":
796+
p.Private = lua.LVAsBool(v)
797+
case "Metadata":
798+
j := []byte(v.String())
799+
maybeJson := make(map[string]interface{})
800+
if jsonErr := json.Unmarshal(j, &maybeJson); jsonErr != nil {
801+
conversionError = true
802+
return
803+
}
804+
p.Metadata = j
805+
case "CreatorId":
806+
u, err := uuid.FromString(v.String())
807+
if err != nil {
808+
conversionError = true
809+
return
810+
}
811+
p.Creator = u
812+
}
813+
814+
if p.Name == "" || len(p.Creator) == 0 { // mandatory items
815+
conversionError = true
816+
return
817+
}
818+
819+
groupParams = append(groupParams, p)
820+
})
821+
})
822+
823+
if conversionError {
824+
l.ArgError(1, "expects a valid set of groups")
825+
return 0
826+
}
827+
828+
groups, err := GroupsCreate(n.logger, n.db, groupParams)
829+
if err != nil {
830+
l.RaiseError(fmt.Sprintf("failed to create groups: %s", err.Error()))
831+
}
832+
833+
//translate uuid to string bytes
834+
lv := l.NewTable()
835+
for i, g := range groups {
836+
uid, _ := uuid.FromBytes(g.Id)
837+
g.Id = []byte(uid.String())
838+
gm := structs.Map(g)
839+
lv.RawSetInt(i+1, convertValue(l, gm))
840+
}
841+
842+
l.Push(lv)
843+
return 1
844+
}

‎tests/core_storage_test.go

+3-76
Large diffs are not rendered by default.

‎tests/group_test.go

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright 2017 The Nakama Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package tests
16+
17+
import (
18+
"nakama/server"
19+
"testing"
20+
21+
"github.com/satori/go.uuid"
22+
)
23+
24+
func TestGroupCreateEmpty(t *testing.T) {
25+
db, err := setupDB()
26+
if err != nil {
27+
t.Error(err)
28+
}
29+
defer db.Close()
30+
_, err = server.GroupsCreate(logger, db, nil)
31+
if err == nil {
32+
t.Error("Expected error but was nil")
33+
}
34+
}
35+
36+
func TestGroupCreateMissingName(t *testing.T) {
37+
db, err := setupDB()
38+
if err != nil {
39+
t.Error(err)
40+
}
41+
defer db.Close()
42+
43+
_, err = server.GroupsCreate(logger, db, []*server.GroupCreateParam{{
44+
Creator: uuid.NewV4(),
45+
Private: true,
46+
Lang: "en",
47+
Description: "desc",
48+
}})
49+
if err == nil {
50+
t.Error("Expected error but was nil")
51+
}
52+
}
53+
54+
func TestGroupCreateMissingCreator(t *testing.T) {
55+
db, err := setupDB()
56+
if err != nil {
57+
t.Error(err)
58+
}
59+
defer db.Close()
60+
61+
_, err = server.GroupsCreate(logger, db, []*server.GroupCreateParam{{
62+
Name: "name1",
63+
Private: true,
64+
Lang: "en",
65+
Description: "desc",
66+
}})
67+
if err == nil {
68+
t.Error("Expected error but was nil")
69+
}
70+
}
71+
72+
func TestGroupCreate(t *testing.T) {
73+
db, err := setupDB()
74+
if err != nil {
75+
t.Error(err)
76+
}
77+
defer db.Close()
78+
79+
_, err = server.GroupsCreate(logger, db, []*server.GroupCreateParam{{
80+
Name: "name3",
81+
Creator: uuid.NewV4(),
82+
Private: true,
83+
Lang: "en",
84+
Description: "desc",
85+
Metadata: []byte("{\"key\":\"value\"}"),
86+
}})
87+
if err != nil {
88+
t.Error(err)
89+
}
90+
}
91+
92+
func TestGroupCreateMultipleSameName(t *testing.T) {
93+
db, err := setupDB()
94+
if err != nil {
95+
t.Error(err)
96+
}
97+
defer db.Close()
98+
99+
_, err = server.GroupsCreate(logger, db, []*server.GroupCreateParam{
100+
{
101+
Name: "group",
102+
Creator: uuid.NewV4(),
103+
Private: true,
104+
Lang: "en",
105+
Description: "desc",
106+
},
107+
{
108+
Name: "group",
109+
Creator: uuid.NewV4(),
110+
Private: true,
111+
Lang: "en",
112+
Description: "desc",
113+
},
114+
})
115+
if err == nil {
116+
t.Error("Expected error but was nil")
117+
}
118+
}
119+
120+
func TestGroupCreateMultiple(t *testing.T) {
121+
db, err := setupDB()
122+
if err != nil {
123+
t.Error(err)
124+
}
125+
defer db.Close()
126+
127+
_, err = server.GroupsCreate(logger, db, []*server.GroupCreateParam{
128+
{
129+
Name: "group1",
130+
Creator: uuid.NewV4(),
131+
Private: true,
132+
Lang: "en",
133+
Description: "desc",
134+
Metadata: []byte("{\"key\":\"value\"}"),
135+
},
136+
{
137+
Name: "group2",
138+
Creator: uuid.NewV4(),
139+
Private: true,
140+
Lang: "en",
141+
Description: "desc",
142+
Metadata: []byte("{\"key\":\"value\"}"),
143+
},
144+
})
145+
if err != nil {
146+
t.Error(err)
147+
}
148+
}

‎tests/modules/e2e_runtime.lua

+14-10
Original file line numberDiff line numberDiff line change
@@ -73,27 +73,31 @@ do
7373
assert(message == "\"WARN logger.\"")
7474
end
7575

76-
-- logger_error
77-
do
78-
local message = nk.logger_error(("%q"):format("ERROR logger."))
79-
assert(message == "\"ERROR logger.\"")
80-
end
81-
82-
-- user_fetch_id
76+
-- users_fetch_id
8377
do
8478
local user_ids = {"4c2ae592-b2a7-445e-98ec-697694478b1c"}
85-
local users = nk.user_fetch_id(user_ids)
79+
local users = nk.users_fetch_id(user_ids)
8680
assert(#users == 1)
8781
assert(user_ids[1] == users[1].Id)
8882
end
8983

90-
-- user_fetch_handle
84+
-- users_fetch_handle
9185
do
9286
local user_handles = {"02ebb2c8"}
93-
local users = nk.user_fetch_handle(user_handles)
87+
local users = nk.users_fetch_handle(user_handles)
9488
assert(user_handles[1] == users[1].Handle)
9589
end
9690

91+
-- users_ban
92+
do
93+
local user_ids = {"4c2ae592-b2a7-445e-98ec-697694478b1c"}
94+
local status, res = pcall(nk.users_ban, user_ids)
95+
if not status then
96+
print(res)
97+
end
98+
assert(status == true)
99+
end
100+
97101
--[[
98102
Nakamax module
99103
]]--

‎tests/runtime_test.go

+25-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131

3232
"github.com/gogo/protobuf/jsonpb"
3333
"github.com/satori/go.uuid"
34-
"go.uber.org/zap"
3534
)
3635

3736
const DATA_PATH = "/tmp/nakama/data/"
@@ -41,7 +40,6 @@ func newRuntime() (*server.Runtime, error) {
4140
if err != nil {
4241
return nil, err
4342
}
44-
logger, _ := zap.NewDevelopment(zap.AddStacktrace(zap.ErrorLevel))
4543
c := server.NewRuntimeConfig()
4644
c.Path = filepath.Join(DATA_PATH, "modules")
4745
return server.NewRuntime(logger, logger, db, c)
@@ -448,7 +446,31 @@ local user_ids = {
448446
"fd8db1fc-6f79-4302-a54c-5960c99601a1"
449447
}
450448
451-
local users = nk.user_fetch_id(user_ids)
449+
local users = nk.users_fetch_id(user_ids)
450+
`)
451+
452+
setupDB()
453+
r, err := newRuntime()
454+
defer r.Stop()
455+
if err != nil {
456+
t.Error(err)
457+
}
458+
}
459+
460+
func TestRuntimeUsersBan(t *testing.T) {
461+
defer os.RemoveAll(DATA_PATH)
462+
writeLuaModule("userid.lua", `
463+
local nk = require("nakama")
464+
465+
local user_handles = {
466+
"02ebb2c8"
467+
}
468+
469+
local status, res = pcall(nk.users_ban, user_handles)
470+
if not status then
471+
print(res)
472+
end
473+
assert(status == true)
452474
`)
453475

454476
setupDB()

‎tests/util.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2017 The Nakama Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package tests
16+
17+
import (
18+
"database/sql"
19+
"fmt"
20+
"net/url"
21+
"strconv"
22+
"time"
23+
24+
"go.uber.org/zap"
25+
)
26+
27+
var (
28+
logger, _ = zap.NewDevelopment(zap.AddStacktrace(zap.ErrorLevel))
29+
)
30+
31+
func setupDB() (*sql.DB, error) {
32+
rawurl := fmt.Sprintf("postgresql://%s?sslmode=disable", "root@localhost:26257/nakama")
33+
url, err := url.Parse(rawurl)
34+
if err != nil {
35+
return nil, err
36+
}
37+
db, err := sql.Open("postgres", url.String())
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
return db, nil
43+
}
44+
45+
func generateString() string {
46+
return strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
47+
}

0 commit comments

Comments
 (0)
Please sign in to comment.