Loading CHANGELOG.md +3 −4 Original line number Diff line number Diff line Loading @@ -6,14 +6,13 @@ The format is based on [keep a changelog](http://keepachangelog.com/) and this p ## [Unreleased] ### Added - New storage list feature. - Ban users and create groups from within the Runtime environment. ### Changed - Run Facebook friends import after registration completes. - Streamline command line flags to be inline with the config file. ### Changed - Restructure and stabilize API messages. - Add batch operations Friends social graph. - Update Runtime modules to use plural function names for batch operations. (`users_fetch_id` and `users_fetch_handle`) ### Fixed - Invocation type was always set to "Before" in After Runtime scripts. Loading server/api.proto +43 −52 Original line number Diff line number Diff line Loading @@ -213,50 +213,48 @@ message Envelope { TGroupUsersAdd group_users_add = 26; TGroupUsersKick group_users_kick = 27; TGroupUsersPromote group_users_promote = 28; TGroup group = 29; TGroups groups = 30; TGroupUsers group_users = 31; TTopicsJoin topics_join = 32; TTopicsLeave topics_leave = 33; TTopicMessageSend topic_message_send = 34; TTopicMessagesList topic_messages_list = 35; TTopics topics = 36; TTopicMessageAck topic_message_ack = 37; TopicMessage topic_message = 38; TTopicMessages topic_messages = 39; TopicPresence topic_presence = 40; TMatchCreate match_create = 41; TMatchesJoin matches_join = 42; TMatchesLeave matches_leave = 43; MatchDataSend match_data_send = 44; TMatch match = 45; TMatches matches = 46; MatchData match_data = 47; MatchPresence match_presence = 48; TStorageList storage_list = 49; TStorageFetch storage_fetch = 50; TStorageWrite storage_write = 51; TStorageRemove storage_remove = 52; TStorageData storage_data = 53; TStorageKeys storage_keys = 54; TLeaderboardsList leaderboards_list = 55; TLeaderboardRecordsWrite leaderboard_records_write = 56; TLeaderboardRecordsFetch leaderboard_records_fetch = 57; TLeaderboardRecordsList leaderboard_records_list = 58; TLeaderboards leaderboards = 59; TLeaderboardRecords leaderboard_records = 60; TMatchmakeAdd matchmake_add = 61; TMatchmakeRemove matchmake_remove = 62; TMatchmakeTicket matchmake_ticket = 63; MatchmakeMatched matchmake_matched = 64; TRpc rpc = 65; TGroups groups = 29; TGroupUsers group_users = 30; TTopicsJoin topics_join = 31; TTopicsLeave topics_leave = 32; TTopicMessageSend topic_message_send = 33; TTopicMessagesList topic_messages_list = 34; TTopics topics = 35; TTopicMessageAck topic_message_ack = 36; TopicMessage topic_message = 37; TTopicMessages topic_messages = 38; TopicPresence topic_presence = 39; TMatchCreate match_create = 40; TMatchesJoin matches_join = 41; TMatchesLeave matches_leave = 42; MatchDataSend match_data_send = 43; TMatch match = 44; TMatches matches = 45; MatchData match_data = 46; MatchPresence match_presence = 47; TStorageList storage_list = 48; TStorageFetch storage_fetch = 49; TStorageWrite storage_write = 50; TStorageRemove storage_remove = 51; TStorageData storage_data = 52; TStorageKeys storage_keys = 53; TLeaderboardsList leaderboards_list = 54; TLeaderboardRecordsWrite leaderboard_records_write = 55; TLeaderboardRecordsFetch leaderboard_records_fetch = 56; TLeaderboardRecordsList leaderboard_records_list = 57; TLeaderboards leaderboards = 58; TLeaderboardRecords leaderboard_records = 59; TMatchmakeAdd matchmake_add = 60; TMatchmakeRemove matchmake_remove = 61; TMatchmakeTicket matchmake_ticket = 62; MatchmakeMatched matchmake_matched = 63; TRpc rpc = 64; } } Loading Loading @@ -509,7 +507,7 @@ message Group { /** * TGroupsCreate creates a new group. * * @returns TGroup * @returns TGroups * * NOTE: The server only processes the first item of the list, and will ignore and logs a warning message for other items. */ Loading @@ -529,13 +527,6 @@ message TGroupsCreate { repeated GroupCreate groups = 1; } /** * TGroup contains the group information. */ message TGroup { Group group = 1; } /** * TGroupsUpdate updates the group with matching Group ID. * Only group admins can update group information. Loading server/core_group.go 0 → 100644 +214 −0 Original line number Diff line number Diff line // Copyright 2017 The Nakama Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package server import ( "database/sql" "errors" "strconv" "strings" "github.com/satori/go.uuid" "go.uber.org/zap" ) type GroupCreateParam struct { Name string // mandatory Creator uuid.UUID // mandatory Description string AvatarURL string Lang string Metadata []byte Private bool } func extractGroup(r scanner) (*Group, error) { var id []byte var creatorID []byte var name sql.NullString var description sql.NullString var avatarURL sql.NullString var lang sql.NullString var utcOffsetMs sql.NullInt64 var metadata []byte var state sql.NullInt64 var count sql.NullInt64 var createdAt sql.NullInt64 var updatedAt sql.NullInt64 err := r.Scan(&id, &creatorID, &name, &description, &avatarURL, &lang, &utcOffsetMs, &metadata, &state, &count, &createdAt, &updatedAt) if err != nil { return nil, err } desc := "" if description.Valid { desc = description.String } avatar := "" if avatarURL.Valid { avatar = avatarURL.String } private := state.Int64 == 1 return &Group{ Id: id, CreatorId: creatorID, Name: name.String, Description: desc, AvatarUrl: avatar, Lang: lang.String, UtcOffsetMs: utcOffsetMs.Int64, Metadata: metadata, Private: private, Count: count.Int64, CreatedAt: createdAt.Int64, UpdatedAt: updatedAt.Int64, }, nil } func GroupsCreate(logger *zap.Logger, db *sql.DB, groupCreateParams []*GroupCreateParam) ([]*Group, error) { if groupCreateParams == nil || len(groupCreateParams) == 0 { return nil, errors.New("Could not create groups. At least one group param must be supplied") } groups := make([]*Group, 0) tx, err := db.Begin() if err != nil { logger.Error("Could not create groups", zap.Error(err)) return nil, err } defer func() { if err != nil { logger.Error("Could not create groups", zap.Error(err)) if tx != nil { txErr := tx.Rollback() if txErr != nil { logger.Error("Could not rollback transaction", zap.Error(txErr)) } } } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) } else { groupNames := make([]string, 0) for _, p := range groups { groupNames = append(groupNames, p.Name) } logger.Debug("Created new groups", zap.Strings("names", groupNames)) } } }() for _, g := range groupCreateParams { newGroup, err := groupCreate(tx, g) if err != nil { logger.Warn("Could not create group", zap.String("name", g.Name), zap.Error(err)) return nil, err } groups = append(groups, newGroup) } return groups, err } func groupCreate(tx *sql.Tx, g *GroupCreateParam) (*Group, error) { if g.Name == "" { return nil, errors.New("Group name must not be empty") } if uuid.Equal(uuid.Nil, g.Creator) { return nil, errors.New("Group creator must be set") } state := 0 if g.Private { state = 1 } columns := make([]string, 0) params := make([]string, 0) values := []interface{}{ uuid.NewV4().Bytes(), g.Creator.Bytes(), g.Name, state, nowMs(), // updated_at } if g.Description != "" { columns = append(columns, "description") params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.Description) } if g.AvatarURL != "" { columns = append(columns, "avatar_url") params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.AvatarURL) } if g.Lang != "" { columns = append(columns, "lang") params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.Lang) } if g.Metadata != nil { columns = append(columns, "metadata") params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.Metadata) } r := tx.QueryRow(` INSERT INTO groups (id, creator_id, name, state, count, created_at, updated_at, `+strings.Join(columns, ", ")+")"+` VALUES ($1, $2, $3, $4, 1, $5, $5, `+strings.Join(params, ",")+")"+` RETURNING id, creator_id, name, description, avatar_url, lang, utc_offset_ms, metadata, state, count, created_at, updated_at `, values...) group, err := extractGroup(r) if err != nil { return nil, err } res, err := tx.Exec(` INSERT INTO group_edge (source_id, position, updated_at, destination_id, state) VALUES ($1, $2, $2, $3, 0), ($3, $2, $2, $1, 0)`, group.Id, updatedAt, g.Creator.Bytes()) if err != nil { return nil, err } rowAffected, err := res.RowsAffected() if err != nil { return nil, err } if rowAffected == 0 { err = errors.New("Could not insert into group_edge table") return nil, err } return group, nil } server/core_user.go +46 −5 Original line number Diff line number Diff line Loading @@ -128,33 +128,34 @@ func UsersFetchHandle(logger *zap.Logger, db *sql.DB, handles []string) ([]*User } func UsersFetchIdsHandles(logger *zap.Logger, db *sql.DB, userIds [][]byte, handles []string) ([]*User, error) { statements := make([]string, 0) idStatements := make([]string, 0) handleStatements := make([]string, 0) params := make([]interface{}, 0) counter := 1 for _, userID := range userIds { statement := "$" + strconv.Itoa(counter) counter += 1 statements = append(statements, statement) idStatements = append(idStatements, statement) params = append(params, userID) } for _, handle := range handles { statement := "$" + strconv.Itoa(counter) counter += 1 statements = append(statements, statement) handleStatements = append(handleStatements, statement) params = append(params, handle) } query := "WHERE " if len(userIds) > 0 { query += "users.id IN (" + strings.Join(statements, ", ") + ")" query += "users.id IN (" + strings.Join(idStatements, ", ") + ")" } if len(handles) > 0 { if len(userIds) > 0 { query += " OR " } query += "users.handle IN (" + strings.Join(statements, ", ") + ")" query += "users.handle IN (" + strings.Join(handleStatements, ", ") + ")" } users, err := querySocialGraph(logger, db, query, params) Loading @@ -164,3 +165,43 @@ func UsersFetchIdsHandles(logger *zap.Logger, db *sql.DB, userIds [][]byte, hand return users, nil } func UsersBan(logger *zap.Logger, db *sql.DB, userIds [][]byte, handles []string) error { idStatements := make([]string, 0) handleStatements := make([]string, 0) params := []interface{}{nowMs()} // $1 counter := 2 for _, userID := range userIds { statement := "$" + strconv.Itoa(counter) idStatements = append(idStatements, statement) params = append(params, userID) counter++ } for _, handle := range handles { statement := "$" + strconv.Itoa(counter) handleStatements = append(handleStatements, statement) params = append(params, handle) counter++ } query := "UPDATE users SET disabled_at = $1 WHERE " if len(userIds) > 0 { query += "users.id IN (" + strings.Join(idStatements, ", ") + ")" } if len(handles) > 0 { if len(userIds) > 0 { query += " OR " } query += "users.handle IN (" + strings.Join(handleStatements, ", ") + ")" } logger.Debug("ban user query", zap.String("query", query)) _, err := db.Exec(query, params...) if err != nil { logger.Error("Failed to ban users", zap.Error(err)) } return err } server/pipeline_group.go +6 −57 Original line number Diff line number Diff line Loading @@ -38,57 +38,6 @@ type groupCursor struct { GroupID []byte } func (p *pipeline) extractGroup(r scanner) (*Group, error) { var id []byte var creatorID []byte var name sql.NullString var description sql.NullString var avatarURL sql.NullString var lang sql.NullString var utcOffsetMs sql.NullInt64 var metadata []byte var state sql.NullInt64 var count sql.NullInt64 var createdAt sql.NullInt64 var updatedAt sql.NullInt64 err := r.Scan(&id, &creatorID, &name, &description, &avatarURL, &lang, &utcOffsetMs, &metadata, &state, &count, &createdAt, &updatedAt) if err != nil { return &Group{}, err } desc := "" if description.Valid { desc = description.String } avatar := "" if avatarURL.Valid { avatar = avatarURL.String } private := state.Int64 == 1 return &Group{ Id: id, CreatorId: creatorID, Name: name.String, Description: desc, AvatarUrl: avatar, Lang: lang.String, UtcOffsetMs: utcOffsetMs.Int64, Metadata: metadata, Private: private, Count: count.Int64, CreatedAt: createdAt.Int64, UpdatedAt: updatedAt.Int64, }, nil } func (p *pipeline) groupCreate(logger *zap.Logger, session *session, envelope *Envelope) { e := envelope.GetGroupsCreate() Loading Loading @@ -135,7 +84,7 @@ func (p *pipeline) groupCreate(logger *zap.Logger, session *session, envelope *E session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not create group")) } else { logger.Info("Created new group", zap.String("name", group.Name)) session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Group{Group: &TGroup{Group: group}}}) session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Groups{&TGroups{Groups: []*Group{group}}}}) } } }() Loading Loading @@ -184,7 +133,7 @@ func (p *pipeline) groupCreate(logger *zap.Logger, session *session, envelope *E } columns = append(columns, "metadata") params = append(params, "$"+strconv.Itoa(len(values))) params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.Metadata) } Loading @@ -194,7 +143,7 @@ VALUES ($1, $2, $3, $4, 1, $5, $5, `+strings.Join(params, ",")+")"+` RETURNING id, creator_id, name, description, avatar_url, lang, utc_offset_ms, metadata, state, count, created_at, updated_at `, values...) group, err = p.extractGroup(r) group, err = extractGroup(r) if err != nil { return } Loading Loading @@ -421,7 +370,7 @@ FROM groups WHERE disabled_at = 0 AND ( `+strings.Join(statements, " OR ")+" )", groups := make([]*Group, 0) for rows.Next() { group, err := p.extractGroup(rows) group, err := extractGroup(rows) if err != nil { logger.Error("Could not get groups", zap.Error(err)) session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not get groups")) Loading Loading @@ -530,7 +479,7 @@ LIMIT $` + strconv.Itoa(len(params)) cursor = cursorBuf.Bytes() break } lastGroup, err = p.extractGroup(rows) lastGroup, err = extractGroup(rows) if err != nil { logger.Error("Could not list groups", zap.Error(err)) session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list groups")) Loading Loading @@ -564,7 +513,7 @@ WHERE group_edge.destination_id = $1 AND disabled_at = 0 AND (group_edge.state = groups := make([]*Group, 0) var lastGroup *Group for rows.Next() { lastGroup, err = p.extractGroup(rows) lastGroup, err = extractGroup(rows) if err != nil { logger.Error("Could not list joined groups", zap.Error(err)) session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list joined groups")) Loading Loading
CHANGELOG.md +3 −4 Original line number Diff line number Diff line Loading @@ -6,14 +6,13 @@ The format is based on [keep a changelog](http://keepachangelog.com/) and this p ## [Unreleased] ### Added - New storage list feature. - Ban users and create groups from within the Runtime environment. ### Changed - Run Facebook friends import after registration completes. - Streamline command line flags to be inline with the config file. ### Changed - Restructure and stabilize API messages. - Add batch operations Friends social graph. - Update Runtime modules to use plural function names for batch operations. (`users_fetch_id` and `users_fetch_handle`) ### Fixed - Invocation type was always set to "Before" in After Runtime scripts. Loading
server/api.proto +43 −52 Original line number Diff line number Diff line Loading @@ -213,50 +213,48 @@ message Envelope { TGroupUsersAdd group_users_add = 26; TGroupUsersKick group_users_kick = 27; TGroupUsersPromote group_users_promote = 28; TGroup group = 29; TGroups groups = 30; TGroupUsers group_users = 31; TTopicsJoin topics_join = 32; TTopicsLeave topics_leave = 33; TTopicMessageSend topic_message_send = 34; TTopicMessagesList topic_messages_list = 35; TTopics topics = 36; TTopicMessageAck topic_message_ack = 37; TopicMessage topic_message = 38; TTopicMessages topic_messages = 39; TopicPresence topic_presence = 40; TMatchCreate match_create = 41; TMatchesJoin matches_join = 42; TMatchesLeave matches_leave = 43; MatchDataSend match_data_send = 44; TMatch match = 45; TMatches matches = 46; MatchData match_data = 47; MatchPresence match_presence = 48; TStorageList storage_list = 49; TStorageFetch storage_fetch = 50; TStorageWrite storage_write = 51; TStorageRemove storage_remove = 52; TStorageData storage_data = 53; TStorageKeys storage_keys = 54; TLeaderboardsList leaderboards_list = 55; TLeaderboardRecordsWrite leaderboard_records_write = 56; TLeaderboardRecordsFetch leaderboard_records_fetch = 57; TLeaderboardRecordsList leaderboard_records_list = 58; TLeaderboards leaderboards = 59; TLeaderboardRecords leaderboard_records = 60; TMatchmakeAdd matchmake_add = 61; TMatchmakeRemove matchmake_remove = 62; TMatchmakeTicket matchmake_ticket = 63; MatchmakeMatched matchmake_matched = 64; TRpc rpc = 65; TGroups groups = 29; TGroupUsers group_users = 30; TTopicsJoin topics_join = 31; TTopicsLeave topics_leave = 32; TTopicMessageSend topic_message_send = 33; TTopicMessagesList topic_messages_list = 34; TTopics topics = 35; TTopicMessageAck topic_message_ack = 36; TopicMessage topic_message = 37; TTopicMessages topic_messages = 38; TopicPresence topic_presence = 39; TMatchCreate match_create = 40; TMatchesJoin matches_join = 41; TMatchesLeave matches_leave = 42; MatchDataSend match_data_send = 43; TMatch match = 44; TMatches matches = 45; MatchData match_data = 46; MatchPresence match_presence = 47; TStorageList storage_list = 48; TStorageFetch storage_fetch = 49; TStorageWrite storage_write = 50; TStorageRemove storage_remove = 51; TStorageData storage_data = 52; TStorageKeys storage_keys = 53; TLeaderboardsList leaderboards_list = 54; TLeaderboardRecordsWrite leaderboard_records_write = 55; TLeaderboardRecordsFetch leaderboard_records_fetch = 56; TLeaderboardRecordsList leaderboard_records_list = 57; TLeaderboards leaderboards = 58; TLeaderboardRecords leaderboard_records = 59; TMatchmakeAdd matchmake_add = 60; TMatchmakeRemove matchmake_remove = 61; TMatchmakeTicket matchmake_ticket = 62; MatchmakeMatched matchmake_matched = 63; TRpc rpc = 64; } } Loading Loading @@ -509,7 +507,7 @@ message Group { /** * TGroupsCreate creates a new group. * * @returns TGroup * @returns TGroups * * NOTE: The server only processes the first item of the list, and will ignore and logs a warning message for other items. */ Loading @@ -529,13 +527,6 @@ message TGroupsCreate { repeated GroupCreate groups = 1; } /** * TGroup contains the group information. */ message TGroup { Group group = 1; } /** * TGroupsUpdate updates the group with matching Group ID. * Only group admins can update group information. Loading
server/core_group.go 0 → 100644 +214 −0 Original line number Diff line number Diff line // Copyright 2017 The Nakama Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package server import ( "database/sql" "errors" "strconv" "strings" "github.com/satori/go.uuid" "go.uber.org/zap" ) type GroupCreateParam struct { Name string // mandatory Creator uuid.UUID // mandatory Description string AvatarURL string Lang string Metadata []byte Private bool } func extractGroup(r scanner) (*Group, error) { var id []byte var creatorID []byte var name sql.NullString var description sql.NullString var avatarURL sql.NullString var lang sql.NullString var utcOffsetMs sql.NullInt64 var metadata []byte var state sql.NullInt64 var count sql.NullInt64 var createdAt sql.NullInt64 var updatedAt sql.NullInt64 err := r.Scan(&id, &creatorID, &name, &description, &avatarURL, &lang, &utcOffsetMs, &metadata, &state, &count, &createdAt, &updatedAt) if err != nil { return nil, err } desc := "" if description.Valid { desc = description.String } avatar := "" if avatarURL.Valid { avatar = avatarURL.String } private := state.Int64 == 1 return &Group{ Id: id, CreatorId: creatorID, Name: name.String, Description: desc, AvatarUrl: avatar, Lang: lang.String, UtcOffsetMs: utcOffsetMs.Int64, Metadata: metadata, Private: private, Count: count.Int64, CreatedAt: createdAt.Int64, UpdatedAt: updatedAt.Int64, }, nil } func GroupsCreate(logger *zap.Logger, db *sql.DB, groupCreateParams []*GroupCreateParam) ([]*Group, error) { if groupCreateParams == nil || len(groupCreateParams) == 0 { return nil, errors.New("Could not create groups. At least one group param must be supplied") } groups := make([]*Group, 0) tx, err := db.Begin() if err != nil { logger.Error("Could not create groups", zap.Error(err)) return nil, err } defer func() { if err != nil { logger.Error("Could not create groups", zap.Error(err)) if tx != nil { txErr := tx.Rollback() if txErr != nil { logger.Error("Could not rollback transaction", zap.Error(txErr)) } } } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) } else { groupNames := make([]string, 0) for _, p := range groups { groupNames = append(groupNames, p.Name) } logger.Debug("Created new groups", zap.Strings("names", groupNames)) } } }() for _, g := range groupCreateParams { newGroup, err := groupCreate(tx, g) if err != nil { logger.Warn("Could not create group", zap.String("name", g.Name), zap.Error(err)) return nil, err } groups = append(groups, newGroup) } return groups, err } func groupCreate(tx *sql.Tx, g *GroupCreateParam) (*Group, error) { if g.Name == "" { return nil, errors.New("Group name must not be empty") } if uuid.Equal(uuid.Nil, g.Creator) { return nil, errors.New("Group creator must be set") } state := 0 if g.Private { state = 1 } columns := make([]string, 0) params := make([]string, 0) values := []interface{}{ uuid.NewV4().Bytes(), g.Creator.Bytes(), g.Name, state, nowMs(), // updated_at } if g.Description != "" { columns = append(columns, "description") params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.Description) } if g.AvatarURL != "" { columns = append(columns, "avatar_url") params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.AvatarURL) } if g.Lang != "" { columns = append(columns, "lang") params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.Lang) } if g.Metadata != nil { columns = append(columns, "metadata") params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.Metadata) } r := tx.QueryRow(` INSERT INTO groups (id, creator_id, name, state, count, created_at, updated_at, `+strings.Join(columns, ", ")+")"+` VALUES ($1, $2, $3, $4, 1, $5, $5, `+strings.Join(params, ",")+")"+` RETURNING id, creator_id, name, description, avatar_url, lang, utc_offset_ms, metadata, state, count, created_at, updated_at `, values...) group, err := extractGroup(r) if err != nil { return nil, err } res, err := tx.Exec(` INSERT INTO group_edge (source_id, position, updated_at, destination_id, state) VALUES ($1, $2, $2, $3, 0), ($3, $2, $2, $1, 0)`, group.Id, updatedAt, g.Creator.Bytes()) if err != nil { return nil, err } rowAffected, err := res.RowsAffected() if err != nil { return nil, err } if rowAffected == 0 { err = errors.New("Could not insert into group_edge table") return nil, err } return group, nil }
server/core_user.go +46 −5 Original line number Diff line number Diff line Loading @@ -128,33 +128,34 @@ func UsersFetchHandle(logger *zap.Logger, db *sql.DB, handles []string) ([]*User } func UsersFetchIdsHandles(logger *zap.Logger, db *sql.DB, userIds [][]byte, handles []string) ([]*User, error) { statements := make([]string, 0) idStatements := make([]string, 0) handleStatements := make([]string, 0) params := make([]interface{}, 0) counter := 1 for _, userID := range userIds { statement := "$" + strconv.Itoa(counter) counter += 1 statements = append(statements, statement) idStatements = append(idStatements, statement) params = append(params, userID) } for _, handle := range handles { statement := "$" + strconv.Itoa(counter) counter += 1 statements = append(statements, statement) handleStatements = append(handleStatements, statement) params = append(params, handle) } query := "WHERE " if len(userIds) > 0 { query += "users.id IN (" + strings.Join(statements, ", ") + ")" query += "users.id IN (" + strings.Join(idStatements, ", ") + ")" } if len(handles) > 0 { if len(userIds) > 0 { query += " OR " } query += "users.handle IN (" + strings.Join(statements, ", ") + ")" query += "users.handle IN (" + strings.Join(handleStatements, ", ") + ")" } users, err := querySocialGraph(logger, db, query, params) Loading @@ -164,3 +165,43 @@ func UsersFetchIdsHandles(logger *zap.Logger, db *sql.DB, userIds [][]byte, hand return users, nil } func UsersBan(logger *zap.Logger, db *sql.DB, userIds [][]byte, handles []string) error { idStatements := make([]string, 0) handleStatements := make([]string, 0) params := []interface{}{nowMs()} // $1 counter := 2 for _, userID := range userIds { statement := "$" + strconv.Itoa(counter) idStatements = append(idStatements, statement) params = append(params, userID) counter++ } for _, handle := range handles { statement := "$" + strconv.Itoa(counter) handleStatements = append(handleStatements, statement) params = append(params, handle) counter++ } query := "UPDATE users SET disabled_at = $1 WHERE " if len(userIds) > 0 { query += "users.id IN (" + strings.Join(idStatements, ", ") + ")" } if len(handles) > 0 { if len(userIds) > 0 { query += " OR " } query += "users.handle IN (" + strings.Join(handleStatements, ", ") + ")" } logger.Debug("ban user query", zap.String("query", query)) _, err := db.Exec(query, params...) if err != nil { logger.Error("Failed to ban users", zap.Error(err)) } return err }
server/pipeline_group.go +6 −57 Original line number Diff line number Diff line Loading @@ -38,57 +38,6 @@ type groupCursor struct { GroupID []byte } func (p *pipeline) extractGroup(r scanner) (*Group, error) { var id []byte var creatorID []byte var name sql.NullString var description sql.NullString var avatarURL sql.NullString var lang sql.NullString var utcOffsetMs sql.NullInt64 var metadata []byte var state sql.NullInt64 var count sql.NullInt64 var createdAt sql.NullInt64 var updatedAt sql.NullInt64 err := r.Scan(&id, &creatorID, &name, &description, &avatarURL, &lang, &utcOffsetMs, &metadata, &state, &count, &createdAt, &updatedAt) if err != nil { return &Group{}, err } desc := "" if description.Valid { desc = description.String } avatar := "" if avatarURL.Valid { avatar = avatarURL.String } private := state.Int64 == 1 return &Group{ Id: id, CreatorId: creatorID, Name: name.String, Description: desc, AvatarUrl: avatar, Lang: lang.String, UtcOffsetMs: utcOffsetMs.Int64, Metadata: metadata, Private: private, Count: count.Int64, CreatedAt: createdAt.Int64, UpdatedAt: updatedAt.Int64, }, nil } func (p *pipeline) groupCreate(logger *zap.Logger, session *session, envelope *Envelope) { e := envelope.GetGroupsCreate() Loading Loading @@ -135,7 +84,7 @@ func (p *pipeline) groupCreate(logger *zap.Logger, session *session, envelope *E session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not create group")) } else { logger.Info("Created new group", zap.String("name", group.Name)) session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Group{Group: &TGroup{Group: group}}}) session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Groups{&TGroups{Groups: []*Group{group}}}}) } } }() Loading Loading @@ -184,7 +133,7 @@ func (p *pipeline) groupCreate(logger *zap.Logger, session *session, envelope *E } columns = append(columns, "metadata") params = append(params, "$"+strconv.Itoa(len(values))) params = append(params, "$"+strconv.Itoa(len(values)+1)) values = append(values, g.Metadata) } Loading @@ -194,7 +143,7 @@ VALUES ($1, $2, $3, $4, 1, $5, $5, `+strings.Join(params, ",")+")"+` RETURNING id, creator_id, name, description, avatar_url, lang, utc_offset_ms, metadata, state, count, created_at, updated_at `, values...) group, err = p.extractGroup(r) group, err = extractGroup(r) if err != nil { return } Loading Loading @@ -421,7 +370,7 @@ FROM groups WHERE disabled_at = 0 AND ( `+strings.Join(statements, " OR ")+" )", groups := make([]*Group, 0) for rows.Next() { group, err := p.extractGroup(rows) group, err := extractGroup(rows) if err != nil { logger.Error("Could not get groups", zap.Error(err)) session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not get groups")) Loading Loading @@ -530,7 +479,7 @@ LIMIT $` + strconv.Itoa(len(params)) cursor = cursorBuf.Bytes() break } lastGroup, err = p.extractGroup(rows) lastGroup, err = extractGroup(rows) if err != nil { logger.Error("Could not list groups", zap.Error(err)) session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list groups")) Loading Loading @@ -564,7 +513,7 @@ WHERE group_edge.destination_id = $1 AND disabled_at = 0 AND (group_edge.state = groups := make([]*Group, 0) var lastGroup *Group for rows.Next() { lastGroup, err = p.extractGroup(rows) lastGroup, err = extractGroup(rows) if err != nil { logger.Error("Could not list joined groups", zap.Error(err)) session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list joined groups")) Loading