Skip to content

Commit

Permalink
Bug fixes and improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
sesposito committed Jan 15, 2025
1 parent c96ee79 commit e15e753
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 36 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr
## [Unreleased]
### Added
- Allow account filtering by email in the Console.
- Add friend metadata support.

### Fixed
- Ensure persisted chat messages listing returns correct order.
Expand Down
11 changes: 11 additions & 0 deletions apigrpc/apigrpc.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1516,6 +1516,13 @@
"type": "string"
},
"collectionFormat": "multi"
},
{
"name": "metadata",
"description": "Optional metadata to add to friends.",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
Expand Down Expand Up @@ -4169,6 +4176,10 @@
"type": "string",
"format": "date-time",
"description": "Time of the latest relationship update."
},
"metadata": {
"type": "string",
"description": "Metadata."
}
},
"description": "A friend of a user."
Expand Down
4 changes: 4 additions & 0 deletions console/console.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -2971,6 +2971,10 @@
"type": "string",
"format": "date-time",
"description": "Time of the latest relationship update."
},
"metadata": {
"type": "string",
"description": "Metadata."
}
},
"description": "A friend of a user."
Expand Down
2 changes: 2 additions & 0 deletions console/ui/src/app/console.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,8 @@ export interface ApiChannelMessageList {

/** A friend of a user. */
export interface ApiFriend {
// Metadata.
metadata?:string
// The friend status. / / one of "Friend.State".
state?:number
// Time of the latest relationship update.
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/heroiclabs/nakama-common v1.35.0 h1:gO3J2v2E12sZ2uL258lt5YF6yNO1tiPtvL7ZwV8t/n0=
github.com/heroiclabs/nakama-common v1.35.0/go.mod h1:E4kw2QpsINoXXJS7aOjen1dycPkoo9bD9pYPAjmA8rc=
github.com/heroiclabs/sql-migrate v0.0.0-20241125131053-95a7949783b0 h1:hHJcYOP6L2/wZIEnYjjkJM+rOk/bK0uaYkDAejYpLhI=
github.com/heroiclabs/sql-migrate v0.0.0-20241125131053-95a7949783b0/go.mod h1:uwcmopkVQIfb/JQqul5zmGI9ounclRC08j9S9lLcpRQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
Expand Down
2 changes: 1 addition & 1 deletion server/api_friend.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func (s *ApiServer) AddFriends(ctx context.Context, in *api.AddFriendsRequest) (
allIDs = append(allIDs, in.GetIds()...)
allIDs = append(allIDs, userIDs...)

if err := AddFriends(ctx, s.logger, s.db, s.tracker, s.router, userID, username, allIDs, nil); err != nil {
if err := AddFriends(ctx, s.logger, s.db, s.tracker, s.router, userID, username, allIDs, in.Metadata); err != nil {
return nil, status.Error(codes.Internal, "Error while trying to add friends.")
}

Expand Down
53 changes: 24 additions & 29 deletions server/core_friend.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,24 +469,14 @@ AND state = 0
return &api.FriendsOfFriendsList{FriendsOfFriends: fof, Cursor: outgoingCursor}, nil
}

func AddFriends(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tracker, messageRouter MessageRouter, userID uuid.UUID, username string, friendIDs []string, metadata map[string]any) error {
func AddFriends(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tracker, messageRouter MessageRouter, userID uuid.UUID, username string, friendIDs []string, metadata string) error {
uniqueFriendIDs := make(map[string]struct{})
for _, fid := range friendIDs {
uniqueFriendIDs[fid] = struct{}{}
}

var notificationToSend map[string]bool

metadataStr := "{}"
if metadata != nil {
metadataBytes, err := json.Marshal(metadata)
if err != nil {
logger.Error("Failed to marshal metadata", zap.Error(err))
return err
}
metadataStr = string(metadataBytes)
}

if err := ExecuteInTx(ctx, db, func(tx *sql.Tx) error {
// If the transaction is retried ensure we wipe any notifications that may have been prepared by previous attempts.
notificationToSend = make(map[string]bool)
Expand All @@ -506,7 +496,7 @@ func AddFriends(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tra
continue
}

isFriendAccept, addFriendErr := addFriend(ctx, logger, tx, userID, id, metadataStr)
isFriendAccept, addFriendErr := addFriend(ctx, logger, tx, userID, id, metadata)
if addFriendErr == nil {
notificationToSend[id] = isFriendAccept
} else if addFriendErr != sql.ErrNoRows { // Check to see if friend had blocked user.
Expand Down Expand Up @@ -547,13 +537,18 @@ func AddFriends(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tra
}

func UpdateFriendMetadata(ctx context.Context, logger *zap.Logger, db *sql.DB, userID, friendUserID uuid.UUID, metadata map[string]any) error {
metadataBytes, err := json.Marshal(metadata)
if err != nil {
logger.Error("Failed to marshal friend metadata", zap.Error(err))
return err
metadataStr := "{}"
if metadata != nil {
metadataBytes, err := json.Marshal(metadata)
if err != nil {
logger.Error("Failed to marshal friend metadata", zap.Error(err))
return err
}
metadataStr = string(metadataBytes)
}

_, err = db.ExecContext(ctx, "UPDATE user_edge SET metadata = $3 WHERE source_id = $1 AND destination_id = $2", metadataBytes, userID, friendUserID)
// TODO: should we merge with empty string to reset to empty or just overwrite whole content?
_, err := db.ExecContext(ctx, "UPDATE user_edge SET metadata = $3::JSONB WHERE source_id = $1 AND destination_id = $2", userID, friendUserID, metadataStr)
if err != nil {
logger.Error("Failed to update friend metadata", zap.Error(err))
return err
Expand All @@ -564,12 +559,20 @@ func UpdateFriendMetadata(ctx context.Context, logger *zap.Logger, db *sql.DB, u

// Returns "true" if accepting an invite, otherwise false.
func addFriend(ctx context.Context, logger *zap.Logger, tx *sql.Tx, userID uuid.UUID, friendID, metadata string) (bool, error) {
if metadata == "" {
metadata = "{}"
}

// Mark an invite as accepted, if one was in place.
res, err := tx.ExecContext(ctx, `
UPDATE user_edge SET state = 0, update_time = now()
UPDATE user_edge SET state = 0, update_time = now(),
metadata = CASE
WHEN source_id = $2 AND destination_id = $1 THEN metadata || $3::JSONB
ELSE metadata
END
WHERE (source_id = $1 AND destination_id = $2 AND state = 1)
OR (source_id = $2 AND destination_id = $1 AND state = 2)
`, friendID, userID)
`, friendID, userID, metadata)
if err != nil {
logger.Debug("Failed to update user state.", zap.Error(err), zap.String("user", userID.String()), zap.String("friend", friendID))
return false, err
Expand All @@ -578,25 +581,17 @@ OR (source_id = $2 AND destination_id = $1 AND state = 2)
// If both edges were updated, it accepted an invite successfully.
if rowsAffected, _ := res.RowsAffected(); rowsAffected == 2 {
logger.Debug("Accepting friend invitation.", zap.String("user", userID.String()), zap.String("friend", friendID))
if metadata != "" {
_, err := tx.ExecContext(ctx, "UPDATE user_edge SET metadata = metadata || $3::JSONB WHERE source_id = $1 AND destination_id = $2", userID, friendID, metadata)
if err != nil {
logger.Debug("Failed to update user metadata.", zap.Error(err), zap.String("user", userID.String()), zap.String("friend", friendID))
return false, err
}
}
return true, nil
}

position := fmt.Sprintf("%v", time.Now().UTC().UnixNano())

// If no edge updates took place, it's either a new invite being set up, or user was blocked off by friend.
_, err = tx.ExecContext(ctx, `
INSERT INTO user_edge (source_id, destination_id, state, position, update_time, metadata)
SELECT source_id, destination_id, state, position, update_time, metadata
FROM (VALUES
($1::UUID, $2::UUID, 1, $3::BIGINT, now(), '{}'),
($2::UUID, $1::UUID, 2, $3::BIGINT, now(), $4::JSONB)
($1::UUID, $2::UUID, 1, $3::BIGINT, now(), $4::JSONB),
($2::UUID, $1::UUID, 2, $3::BIGINT, now(), '{}'::JSONB)
) AS ue(source_id, destination_id, state, position, update_time, metadata)
WHERE
EXISTS (SELECT id FROM users WHERE id = $2::UUID)
Expand Down
12 changes: 11 additions & 1 deletion server/runtime_go_nakama.go
Original file line number Diff line number Diff line change
Expand Up @@ -4163,7 +4163,17 @@ func (n *RuntimeGoNakamaModule) FriendsAdd(ctx context.Context, userID string, u
allIDs = append(allIDs, ids...)
allIDs = append(allIDs, fetchIDs...)

err = AddFriends(ctx, n.logger, n.db, n.tracker, n.router, userUUID, username, allIDs, metadata)
var metadataStr string
if metadata != nil {
bytes, err := json.Marshal(metadata)
if err != nil {
n.logger.Error("Could not marshal metadata", zap.Error(err))
return fmt.Errorf("failed to marshal metadata: %w", err)
}
metadataStr = string(bytes)
}

err = AddFriends(ctx, n.logger, n.db, n.tracker, n.router, userUUID, username, allIDs, metadataStr)
if err != nil {
return err
}
Expand Down
14 changes: 12 additions & 2 deletions server/runtime_javascript_nakama.go
Original file line number Diff line number Diff line change
Expand Up @@ -7796,7 +7796,17 @@ func (n *RuntimeJavascriptNakamaModule) friendsAdd(r *goja.Runtime) func(goja.Fu
panic(r.NewTypeError("invalid metadata: must be an object"))
}

err = AddFriends(n.ctx, n.logger, n.db, n.tracker, n.router, userID, username, allIDs, metadata)
var metadataStr string
if metadata != nil {
bytes, err := json.Marshal(metadata)
if err != nil {
n.logger.Error("Could not marshal metadata", zap.Error(err))
panic(r.NewTypeError("failed to marshal metadata: %s", err.Error()))
}
metadataStr = string(bytes)
}

err = AddFriends(n.ctx, n.logger, n.db, n.tracker, n.router, userID, username, allIDs, metadataStr)
if err != nil {
panic(r.NewTypeError(err.Error()))
}
Expand Down Expand Up @@ -7982,7 +7992,7 @@ func (n *RuntimeJavascriptNakamaModule) friendMetadataUpdate(r *goja.Runtime) fu
panic(r.NewTypeError("expects user ID to be a valid identifier"))
}

metadata, ok := f.Argument(3).Export().(map[string]any)
metadata, ok := f.Argument(2).Export().(map[string]any)
if !ok {
panic(r.NewTypeError("expects metadata to be an object"))
}
Expand Down
13 changes: 12 additions & 1 deletion server/runtime_lua_nakama.go
Original file line number Diff line number Diff line change
Expand Up @@ -10098,7 +10098,18 @@ func (n *RuntimeLuaNakamaModule) friendsAdd(l *lua.LState) int {
metadata = RuntimeLuaConvertLuaTable(metadataTable)
}

err = AddFriends(l.Context(), n.logger, n.db, n.tracker, n.router, userID, username, allIDs, metadata)
var metadataStr string
if metadata != nil {
bytes, err := json.Marshal(metadata)
if err != nil {
n.logger.Error("Could not marshal metadata", zap.Error(err))
l.RaiseError("error marshalling metadata: %s", err.Error())
return 0
}
metadataStr = string(bytes)
}

err = AddFriends(l.Context(), n.logger, n.db, n.tracker, n.router, userID, username, allIDs, metadataStr)
if err != nil {
l.RaiseError("error adding friends: %s", err.Error())
return 0
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e15e753

Please sign in to comment.