diff --git a/CHANGELOG.md b/CHANGELOG.md index 71ac01f617..d230d9b798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [keep a changelog](http://keepachangelog.com/) and this p ### Changed - The build system now strips up to current dir in recorded source file paths at compile. +- Addition of specific error messages to the Error payload. +- Group names must be unique. ### Fixed - Fix regression loading config file. diff --git a/migrations/20170115200001_initial_schema.sql b/migrations/20170115200001_initial_schema.sql index becb5e149e..d5caefd977 100644 --- a/migrations/20170115200001_initial_schema.sql +++ b/migrations/20170115200001_initial_schema.sql @@ -18,7 +18,7 @@ CREATE TABLE IF NOT EXISTS users ( PRIMARY KEY (id), id BYTEA NOT NULL, - handle VARCHAR(20) UNIQUE NOT NULL, + handle VARCHAR(20) CONSTRAINT users_handle_key UNIQUE NOT NULL, fullname VARCHAR(70), avatar_url VARCHAR(255), -- https://tools.ietf.org/html/bcp47 @@ -77,7 +77,7 @@ CREATE TABLE IF NOT EXISTS groups ( PRIMARY KEY (id), id BYTEA NOT NULL, creator_id BYTEA NOT NULL, - name VARCHAR(70) NOT NULL, + name VARCHAR(70) CONSTRAINT groups_name_key UNIQUE NOT NULL, description VARCHAR(255), avatar_url VARCHAR(255), -- https://tools.ietf.org/html/bcp47 diff --git a/migrations/20170228205100_leaderboards.sql b/migrations/20170228205100_leaderboards.sql index 12ed4af8b2..e25fd95526 100644 --- a/migrations/20170228205100_leaderboards.sql +++ b/migrations/20170228205100_leaderboards.sql @@ -50,6 +50,7 @@ CREATE TABLE IF NOT EXISTS leaderboard_record ( ranked_at INT CHECK (ranked_at >= 0) DEFAULT 0 NOT NULL, updated_at INT CHECK (updated_at > 0) NOT NULL, -- Used to enable proper order in revscan when sorting by score descending. + -- Revscan is unaviodable here due to cockroachdb/cockroach#14241. updated_at_inverse INT CHECK (updated_at > 0) NOT NULL, expires_at INT CHECK (expires_at >= 0) DEFAULT 0 NOT NULL, banned_at INT CHECK (expires_at >= 0) DEFAULT 0 NOT NULL diff --git a/server/api.proto b/server/api.proto index 82a8b2613e..b8f7536bd6 100644 --- a/server/api.proto +++ b/server/api.proto @@ -22,7 +22,23 @@ message Heartbeat { } message Error { - string reason = 1; + enum Code { + RUNTIME_EXCEPTION = 0; + UNRECOGNIZED_PAYLOAD = 1; + MISSING_PAYLOAD = 2; + BAD_INPUT = 3; + AUTH_ERROR = 4; + USER_LINK_INUSE = 5; + USER_LINK_PROVIDER_UNAVAILABLE = 6; + USER_UNLINK_DISALLOWED = 7; + USER_HANDLE_INUSE = 8; + GROUP_NAME_INUSE = 9; + STORAGE_FETCH_DISALLOWED = 10; + MATCH_NOT_FOUND = 11; + } + + int32 code = 1; + string message = 2; } message AuthenticateRequest { @@ -58,8 +74,9 @@ message AuthenticateResponse { } message Error { - string reason = 1; - AuthenticateRequest request = 2; + int32 code = 1; + string message = 2; + AuthenticateRequest request = 3; } string collation_id = 1; diff --git a/server/pipeline.go b/server/pipeline.go index 3908ef6b72..b4c060fc27 100644 --- a/server/pipeline.go +++ b/server/pipeline.go @@ -134,8 +134,25 @@ func (p *pipeline) processRequest(logger zap.Logger, session *session, envelope p.leaderboardRecordsList(logger, session, envelope) case nil: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "No payload found"}}}) + session.Send(ErrorMessage(envelope.CollationId, MISSING_PAYLOAD, "No payload found")) default: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Unrecognized payload"}}}) + session.Send(ErrorMessage(envelope.CollationId, UNRECOGNIZED_PAYLOAD, "Unrecognized payload")) } } + +func ErrorMessageRuntimeException(collationID string, message string) *Envelope { + return ErrorMessage(collationID, RUNTIME_EXCEPTION, message) +} + +func ErrorMessageBadInput(collationID string, message string) *Envelope { + return ErrorMessage(collationID, BAD_INPUT, message) +} + +func ErrorMessage(collationID string, code Error_Code, message string) *Envelope { + return &Envelope{ + CollationId: collationID, + Payload: &Envelope_Error{&Error{ + Message: message, + Code: int32(code), + }}} +} diff --git a/server/pipeline_friend.go b/server/pipeline_friend.go index 1460ba1b7e..ee3c464a83 100644 --- a/server/pipeline_friend.go +++ b/server/pipeline_friend.go @@ -198,14 +198,14 @@ FROM users, user_edge ` + filterQuery func (p *pipeline) friendAdd(l zap.Logger, session *session, envelope *Envelope) { addFriendRequest := envelope.GetFriendAdd() if len(addFriendRequest.UserId) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "User ID must be present"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "User ID must be present")) return } friendID, err := uuid.FromBytes(addFriendRequest.UserId) if err != nil { l.Warn("Could not add friend", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid User ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid User ID")) return } logger := l.With(zap.String("friend_id", friendID.String())) @@ -213,14 +213,14 @@ func (p *pipeline) friendAdd(l zap.Logger, session *session, envelope *Envelope) if friendID.String() == session.userID.String() { logger.Warn("Cannot add self", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot add self"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cannot add self")) return } tx, err := p.db.Begin() if err != nil { logger.Error("Could not add friend", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to add friend"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to add friend")) return } defer func() { @@ -231,12 +231,12 @@ func (p *pipeline) friendAdd(l zap.Logger, session *session, envelope *Envelope) logger.Error("Could not rollback transaction", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to add friend"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to add friend")) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to add friend"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to add friend")) } else { logger.Info("Added friend") session.Send(&Envelope{CollationId: envelope.CollationId}) @@ -267,7 +267,7 @@ WHERE EXISTS (SELECT id FROM users WHERE id=$2) rowsAffected, _ = res.RowsAffected() if rowsAffected == 0 { - err = errors.New("Did not find friend ID in users table.") + err = errors.New("did not find friend ID in users table") return } @@ -292,14 +292,14 @@ WHERE EXISTS (SELECT id FROM users WHERE id=$2) func (p *pipeline) friendRemove(l zap.Logger, session *session, envelope *Envelope) { removeFriendRequest := envelope.GetFriendRemove() if len(removeFriendRequest.UserId) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "User ID must be present"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "User ID must be present")) return } friendID, err := uuid.FromBytes(removeFriendRequest.UserId) if err != nil { l.Warn("Could not add friend", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid User ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid User ID")) return } logger := l.With(zap.String("friend_id", friendID.String())) @@ -307,14 +307,14 @@ func (p *pipeline) friendRemove(l zap.Logger, session *session, envelope *Envelo if friendID.String() == session.userID.String() { logger.Warn("Cannot remove self", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot remove self"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cannot remove self")) return } tx, err := p.db.Begin() if err != nil { logger.Error("Could not remove friend", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to remove friend"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to remove friend")) return } defer func() { @@ -325,12 +325,12 @@ func (p *pipeline) friendRemove(l zap.Logger, session *session, envelope *Envelo logger.Error("Could not rollback transaction", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to remove friend"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to remove friend")) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to remove friend"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to remove friend")) } else { logger.Info("Removed friend") session.Send(&Envelope{CollationId: envelope.CollationId}) @@ -360,14 +360,14 @@ func (p *pipeline) friendRemove(l zap.Logger, session *session, envelope *Envelo func (p *pipeline) friendBlock(l zap.Logger, session *session, envelope *Envelope) { blockUserRequest := envelope.GetFriendBlock() if len(blockUserRequest.UserId) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "User ID must be present"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "User ID must be present")) return } userID, err := uuid.FromBytes(blockUserRequest.UserId) if err != nil { l.Warn("Could not block user", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid User ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid User ID")) return } logger := l.With(zap.String("user_id", userID.String())) @@ -375,14 +375,14 @@ func (p *pipeline) friendBlock(l zap.Logger, session *session, envelope *Envelop if userID.String() == session.userID.String() { logger.Warn("Cannot block self", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot block self"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cannot block self")) return } tx, err := p.db.Begin() if err != nil { logger.Error("Could not block user", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to remove friend"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to block friend")) return } defer func() { @@ -397,12 +397,12 @@ func (p *pipeline) friendBlock(l zap.Logger, session *session, envelope *Envelop logger.Error("Could not rollback transaction", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not block user"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not block user")) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not block user"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not block user")) } else { logger.Info("User blocked") session.Send(&Envelope{CollationId: envelope.CollationId}) @@ -439,7 +439,7 @@ func (p *pipeline) friendsList(logger zap.Logger, session *session, envelope *En friends, err := p.getFriends("WHERE id = destination_id AND source_id = $1", session.userID.Bytes()) if err != nil { logger.Error("Could not get friends", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get friends"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not get friends")) return } diff --git a/server/pipeline_group.go b/server/pipeline_group.go index 08840837b9..45d4417518 100644 --- a/server/pipeline_group.go +++ b/server/pipeline_group.go @@ -93,7 +93,7 @@ func (p *pipeline) groupCreate(logger zap.Logger, session *session, envelope *En g := envelope.GetGroupCreate() if g.Name == "" { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group name is mandatory."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group name is mandatory.")) return } @@ -102,7 +102,7 @@ func (p *pipeline) groupCreate(logger zap.Logger, session *session, envelope *En tx, err := p.db.Begin() if err != nil { logger.Error("Could not create group", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not create group"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Could not create group")) return } @@ -115,12 +115,16 @@ func (p *pipeline) groupCreate(logger zap.Logger, session *session, envelope *En logger.Error("Could not rollback transaction", zap.Error(err)) } } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not create group"}}}) + if strings.HasSuffix(err.Error(), "violates unique constraint \"groups_name_key\"") { + session.Send(ErrorMessage(envelope.CollationId, GROUP_NAME_INUSE, "Name is in use")) + } else { + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not create group")) + } } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not create group"}}}) + 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}}}) @@ -167,7 +171,7 @@ func (p *pipeline) groupCreate(logger zap.Logger, session *session, envelope *En // Make this `var js interface{}` if we want to allow top-level JSON arrays. var maybeJSON map[string]interface{} if json.Unmarshal(g.Metadata, &maybeJSON) != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Metadata must be a valid JSON object"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Metadata must be a valid JSON object")) return } @@ -211,14 +215,14 @@ func (p *pipeline) groupUpdate(l zap.Logger, session *session, envelope *Envelop g := envelope.GetGroupUpdate() groupID, err := uuid.FromBytes(g.GroupId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID is not valid.")) return } // Make this `var js interface{}` if we want to allow top-level JSON arrays. var maybeJSON map[string]interface{} if len(g.Metadata) != 0 && json.Unmarshal(g.Metadata, &maybeJSON) != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Metadata must be a valid JSON object"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Metadata must be a valid JSON object")) return } @@ -263,8 +267,12 @@ EXISTS (SELECT source_id FROM group_edge WHERE source_id = $1 AND destination_id params...) if err != nil { - logger.Error("Could not update group", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not update group"}}}) + if strings.HasSuffix(err.Error(), "violates unique constraint \"groups_name_key\"") { + session.Send(ErrorMessage(envelope.CollationId, GROUP_NAME_INUSE, "Name is in use")) + } else { + logger.Error("Could not update group", zap.Error(err)) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not update group")) + } return } @@ -278,7 +286,7 @@ func (p *pipeline) groupRemove(l zap.Logger, session *session, envelope *Envelop groupID, err := uuid.FromBytes(g.GroupId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID is not valid.")) return } @@ -288,7 +296,7 @@ func (p *pipeline) groupRemove(l zap.Logger, session *session, envelope *Envelop tx, err := p.db.Begin() if err != nil { logger.Error("Could not remove group", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: failureReason}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, failureReason)) return } defer func() { @@ -298,13 +306,12 @@ func (p *pipeline) groupRemove(l zap.Logger, session *session, envelope *Envelop if err != nil { logger.Error("Could not rollback transaction", zap.Error(err)) } - - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: failureReason}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, failureReason)) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: failureReason}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, failureReason)) } else { logger.Info("Removed group") session.Send(&Envelope{CollationId: envelope.CollationId}) @@ -359,7 +366,7 @@ FROM groups WHERE disabled_at = 0 AND ( `+strings.Join(statements, " OR ")+" )", validGroupIds...) if err != nil { logger.Error("Could not get groups", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get groups"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not get groups")) return } defer rows.Close() @@ -369,7 +376,7 @@ FROM groups WHERE disabled_at = 0 AND ( `+strings.Join(statements, " OR ")+" )", group, err := p.extractGroup(rows) if err != nil { logger.Error("Could not get groups", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get groups"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not get groups")) return } groups = append(groups, group) @@ -386,7 +393,7 @@ func (p *pipeline) groupsList(logger zap.Logger, session *session, envelope *Env if limit == 0 { limit = 10 } else if limit < 10 || limit > 100 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Page limit must be between 10 and 100"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Page limit must be between 10 and 100")) return } @@ -395,7 +402,7 @@ func (p *pipeline) groupsList(logger zap.Logger, session *session, envelope *Env if incoming.Cursor != nil { var c groupCursor if err := gob.NewDecoder(bytes.NewReader(incoming.Cursor)).Decode(&c); err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid cursor data"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid cursor data")) return } @@ -445,7 +452,7 @@ LIMIT $` + strconv.Itoa(len(params)) rows, err := p.db.Query(query, params...) if err != nil { logger.Error("Could not list groups", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not list groups"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list groups")) return } defer rows.Close() @@ -468,8 +475,8 @@ LIMIT $` + strconv.Itoa(len(params)) newCursor.Secondary = lastGroup.UpdatedAt } if gob.NewEncoder(cursorBuf).Encode(newCursor); err != nil { - logger.Error("Error creating group list cursor", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Database request failed"}}}) + logger.Error("Could not create group list cursor", zap.Error(err)) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list groups")) return } cursor = cursorBuf.Bytes() @@ -478,7 +485,7 @@ LIMIT $` + strconv.Itoa(len(params)) lastGroup, err = p.extractGroup(rows) if err != nil { logger.Error("Could not list groups", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not list groups"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list groups")) return } groups = append(groups, lastGroup) @@ -501,7 +508,7 @@ WHERE group_edge.destination_id = $1 AND disabled_at = 0 AND (group_edge.state = if err != nil { logger.Error("Could not list joined groups", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not list joined groups"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list joined groups")) return } defer rows.Close() @@ -512,7 +519,7 @@ WHERE group_edge.destination_id = $1 AND disabled_at = 0 AND (group_edge.state = lastGroup, err = p.extractGroup(rows) if err != nil { logger.Error("Could not list joined groups", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not list joined groups"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list joined groups")) return } groups = append(groups, lastGroup) @@ -526,7 +533,7 @@ func (p *pipeline) groupUsersList(l zap.Logger, session *session, envelope *Enve groupID, err := uuid.FromBytes(g.GroupId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID is not valid")) return } @@ -541,7 +548,7 @@ WHERE u.id = ge.source_id AND ge.destination_id = $1` rows, err := p.db.Query(query, groupID.Bytes()) if err != nil { logger.Error("Could not get group users", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get group users"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not get group users")) return } defer rows.Close() @@ -565,7 +572,7 @@ WHERE u.id = ge.source_id AND ge.destination_id = $1` err = rows.Scan(&id, &handle, &fullname, &avatarURL, &lang, &location, &timezone, &metadata, &createdAt, &updatedAt, &lastOnlineAt, &state) if err != nil { logger.Error("Could not get group users", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get group users"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not get group users")) return } @@ -595,7 +602,7 @@ func (p *pipeline) groupJoin(l zap.Logger, session *session, envelope *Envelope) groupID, err := uuid.FromBytes(g.GroupId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID is not valid.")) return } @@ -604,7 +611,7 @@ func (p *pipeline) groupJoin(l zap.Logger, session *session, envelope *Envelope) tx, err := p.db.Begin() if err != nil { logger.Error("Could not add user to group", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not add user to group"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not add user to group")) return } defer func() { @@ -615,12 +622,12 @@ func (p *pipeline) groupJoin(l zap.Logger, session *session, envelope *Envelope) logger.Error("Could not rollback transaction", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not join group"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not join group")) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not join group"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not join group")) } else { logger.Info("User joined group") session.Send(&Envelope{CollationId: envelope.CollationId}) @@ -656,7 +663,7 @@ VALUES ($1, $2, $2, $3, $4), ($3, $2, $2, $1, $4)`, } if affectedRows, _ := res.RowsAffected(); affectedRows == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not accept group join envelope. Group may not exists with the given ID"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not accept group join envelope. Group may not exists with the given ID")) return } @@ -673,7 +680,7 @@ func (p *pipeline) groupLeave(l zap.Logger, session *session, envelope *Envelope groupID, err := uuid.FromBytes(g.GroupId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID is not valid")) return } @@ -683,7 +690,7 @@ func (p *pipeline) groupLeave(l zap.Logger, session *session, envelope *Envelope tx, err := p.db.Begin() if err != nil { logger.Error("Could not leave group", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: failureReason}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, failureReason)) return } defer func() { @@ -694,12 +701,12 @@ func (p *pipeline) groupLeave(l zap.Logger, session *session, envelope *Envelope logger.Error("Could not rollback transaction", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: failureReason}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, failureReason)) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: failureReason}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, failureReason)) } else { logger.Info("User left group") session.Send(&Envelope{CollationId: envelope.CollationId}) @@ -783,13 +790,13 @@ func (p *pipeline) groupUserAdd(l zap.Logger, session *session, envelope *Envelo groupID, err := uuid.FromBytes(g.GroupId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID is not valid")) return } userID, err := uuid.FromBytes(g.UserId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "User ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "User ID is not valid")) return } @@ -799,7 +806,7 @@ func (p *pipeline) groupUserAdd(l zap.Logger, session *session, envelope *Envelo tx, err := p.db.Begin() if err != nil { logger.Error("Could not add user to group", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not add user to group"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not add user to group")) return } defer func() { @@ -814,12 +821,12 @@ func (p *pipeline) groupUserAdd(l zap.Logger, session *session, envelope *Envelo logger.Error("Could not rollback transaction", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not add user to group"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not add user to group")) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not add user to group"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not add user to group")) } else { logger.Info("Added user to the group") session.Send(&Envelope{CollationId: envelope.CollationId}) @@ -876,18 +883,18 @@ func (p *pipeline) groupUserKick(l zap.Logger, session *session, envelope *Envel groupID, err := uuid.FromBytes(g.GroupId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID is not valid")) return } userID, err := uuid.FromBytes(g.UserId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "User ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "User ID is not valid")) return } if userID.String() == session.userID.String() { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "You can't kick yourself from the group."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "You can't kick yourself from the group")) return } @@ -898,7 +905,7 @@ func (p *pipeline) groupUserKick(l zap.Logger, session *session, envelope *Envel tx, err := p.db.Begin() if err != nil { logger.Error("Could not kick user from group", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: failureReason}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, failureReason)) return } defer func() { @@ -913,12 +920,12 @@ func (p *pipeline) groupUserKick(l zap.Logger, session *session, envelope *Envel logger.Error("Could not rollback transaction", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: failureReason}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, failureReason)) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: failureReason}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, failureReason)) } else { logger.Info("Kicked user from group") session.Send(&Envelope{CollationId: envelope.CollationId}) @@ -972,18 +979,18 @@ func (p *pipeline) groupUserPromote(l zap.Logger, session *session, envelope *En groupID, err := uuid.FromBytes(g.GroupId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID is not valid")) return } userID, err := uuid.FromBytes(g.UserId) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "User ID is not valid."}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "User ID is not valid")) return } if userID.String() == session.userID.String() { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "You can't promote yourself"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "You can't promote yourself")) return } @@ -1003,14 +1010,14 @@ AND )`, groupID.Bytes(), userID.Bytes(), session.userID.Bytes(), nowMs()) if err != nil { - logger.Warn("Cannot promote user", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot promote user"}}}) + logger.Warn("Could not promote user", zap.Error(err)) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not promote user")) return } if count, _ := res.RowsAffected(); count == 0 { - logger.Warn("Cannot promote user - Make sure user is part of the group or group exists") - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot promote user - Make sure user is part of the group or group exists"}}}) + logger.Warn("Could not promote user - Make sure user is part of the group or group exists") + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not promote user - Make sure user is part of the group or group exists")) return } diff --git a/server/pipeline_leaderboard.go b/server/pipeline_leaderboard.go index 4e05ae03bc..07a794be36 100644 --- a/server/pipeline_leaderboard.go +++ b/server/pipeline_leaderboard.go @@ -48,7 +48,7 @@ func (p *pipeline) leaderboardsList(logger zap.Logger, session *session, envelop if limit == 0 { limit = 10 } else if limit < 10 || limit > 100 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Limit must be between 10 and 100"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Limit must be between 10 and 100")) return } @@ -58,7 +58,7 @@ func (p *pipeline) leaderboardsList(logger zap.Logger, session *session, envelop if len(incoming.Cursor) != 0 { var incomingCursor leaderboardCursor if err := gob.NewDecoder(bytes.NewReader(incoming.Cursor)).Decode(&incomingCursor); err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid cursor data"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid cursor data")) return } query += " WHERE id > $1" @@ -72,7 +72,7 @@ func (p *pipeline) leaderboardsList(logger zap.Logger, session *session, envelop rows, err := p.db.Query(query, params...) if err != nil { logger.Error("Could not execute leaderboards list query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboards"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list leaderboards")) return } defer rows.Close() @@ -96,7 +96,7 @@ func (p *pipeline) leaderboardsList(logger zap.Logger, session *session, envelop } if gob.NewEncoder(cursorBuf).Encode(newCursor); err != nil { logger.Error("Error creating leaderboards list cursor", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error encoding cursor"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list leaderboards")) return } outgoingCursor = cursorBuf.Bytes() @@ -106,7 +106,7 @@ func (p *pipeline) leaderboardsList(logger zap.Logger, session *session, envelop err = rows.Scan(&id, &authoritative, &sortOrder, &count, &resetSchedule, &metadata, &nextId, &prevId) if err != nil { logger.Error("Could not scan leaderboards list query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboards"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list leaderboards")) return } @@ -123,7 +123,7 @@ func (p *pipeline) leaderboardsList(logger zap.Logger, session *session, envelop } if err = rows.Err(); err != nil { logger.Error("Could not process leaderboards list query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboards"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not list leaderboards")) return } @@ -136,7 +136,7 @@ func (p *pipeline) leaderboardsList(logger zap.Logger, session *session, envelop func (p *pipeline) leaderboardRecordWrite(logger zap.Logger, session *session, envelope *Envelope) { incoming := envelope.GetLeaderboardRecordWrite() if len(incoming.LeaderboardId) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Leaderboard ID must be present"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Leaderboard ID must be present")) return } @@ -144,7 +144,7 @@ func (p *pipeline) leaderboardRecordWrite(logger zap.Logger, session *session, e // Make this `var js interface{}` if we want to allow top-level JSON arrays. var maybeJSON map[string]interface{} if json.Unmarshal(incoming.Metadata, &maybeJSON) != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Metadata must be a valid JSON object"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Metadata must be a valid JSON object")) return } } @@ -158,7 +158,7 @@ func (p *pipeline) leaderboardRecordWrite(logger zap.Logger, session *session, e Scan(&authoritative, &sortOrder, &resetSchedule) if err != nil { logger.Error("Could not execute leaderboard record write metadata query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error writing leaderboard record"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error writing leaderboard record")) return } @@ -169,14 +169,14 @@ func (p *pipeline) leaderboardRecordWrite(logger zap.Logger, session *session, e expr, err := cronexpr.Parse(resetSchedule.String) if err != nil { logger.Error("Could not parse leaderboard reset schedule query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error writing leaderboard record"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error writing leaderboard record")) return } expiresAt = timeToMs(expr.Next(now)) } if authoritative == true { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot submit to authoritative leaderboard"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cannot submit to authoritative leaderboard")) return } @@ -207,10 +207,10 @@ func (p *pipeline) leaderboardRecordWrite(logger zap.Logger, session *session, e scoreDelta = incoming.GetBest() scoreAbs = incoming.GetBest() case nil: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "No leaderboard record write operator found"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "No leaderboard record write operator found")) return default: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Unknown leaderboard record write operator"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Unknown leaderboard record write operator")) return } @@ -245,12 +245,12 @@ func (p *pipeline) leaderboardRecordWrite(logger zap.Logger, session *session, e res, err := p.db.Exec(query, params...) if err != nil { logger.Error("Could not execute leaderboard record write query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error writing leaderboard record"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error writing leaderboard record")) return } if rowsAffected, _ := res.RowsAffected(); rowsAffected == 0 { logger.Error("Unexpected row count from leaderboard record write query") - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error writing leaderboard record"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error writing leaderboard record")) return } @@ -272,7 +272,7 @@ func (p *pipeline) leaderboardRecordWrite(logger zap.Logger, session *session, e Scan(&location, &timezone, &rankValue, &score, &numScore, &metadata, &rankedAt, &bannedAt) if err != nil { logger.Error("Could not execute leaderboard record read query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error writing leaderboard record"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error writing leaderboard record")) return } @@ -297,7 +297,7 @@ func (p *pipeline) leaderboardRecordsFetch(logger zap.Logger, session *session, incoming := envelope.GetLeaderboardRecordsFetch() leaderboardIds := incoming.LeaderboardIds if len(leaderboardIds) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Leaderboard IDs must be present"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Leaderboard IDs must be present")) return } @@ -305,7 +305,7 @@ func (p *pipeline) leaderboardRecordsFetch(logger zap.Logger, session *session, if limit == 0 { limit = 10 } else if limit < 10 || limit > 100 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Limit must be between 10 and 100"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Limit must be between 10 and 100")) return } @@ -313,7 +313,7 @@ func (p *pipeline) leaderboardRecordsFetch(logger zap.Logger, session *session, if len(incoming.Cursor) != 0 { incomingCursor = &leaderboardRecordFetchCursor{} if err := gob.NewDecoder(bytes.NewReader(incoming.Cursor)).Decode(incomingCursor); err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid cursor data"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid cursor data")) return } } @@ -346,7 +346,7 @@ func (p *pipeline) leaderboardRecordsFetch(logger zap.Logger, session *session, rows, err := p.db.Query(query, params...) if err != nil { logger.Error("Could not execute leaderboard records fetch query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } defer rows.Close() @@ -377,7 +377,7 @@ func (p *pipeline) leaderboardRecordsFetch(logger zap.Logger, session *session, } if gob.NewEncoder(cursorBuf).Encode(newCursor); err != nil { logger.Error("Error creating leaderboard records fetch cursor", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error encoding cursor"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } outgoingCursor = cursorBuf.Bytes() @@ -388,7 +388,7 @@ func (p *pipeline) leaderboardRecordsFetch(logger zap.Logger, session *session, &rankValue, &score, &numScore, &metadata, &rankedAt, &updatedAt, &expiresAt, &bannedAt) if err != nil { logger.Error("Could not scan leaderboard records fetch query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } @@ -410,7 +410,7 @@ func (p *pipeline) leaderboardRecordsFetch(logger zap.Logger, session *session, } if err = rows.Err(); err != nil { logger.Error("Could not process leaderboard records fetch query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } @@ -424,7 +424,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e incoming := envelope.GetLeaderboardRecordsList() if len(incoming.LeaderboardId) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Leaderboard ID must be present"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Leaderboard ID must be present")) return } @@ -432,7 +432,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e if limit == 0 { limit = 10 } else if limit < 10 || limit > 100 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Limit must be between 10 and 100"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Limit must be between 10 and 100")) return } @@ -440,7 +440,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e if len(incoming.Cursor) != 0 { incomingCursor = &leaderboardRecordListCursor{} if err := gob.NewDecoder(bytes.NewReader(incoming.Cursor)).Decode(incomingCursor); err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid cursor data"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid cursor data")) return } } @@ -453,7 +453,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e Scan(&sortOrder, &resetSchedule) if err != nil { logger.Error("Could not execute leaderboard records list metadata query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } @@ -462,7 +462,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e expr, err := cronexpr.Parse(resetSchedule.String) if err != nil { logger.Error("Could not parse leaderboard reset schedule query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } currentExpiresAt = timeToMs(expr.Next(now())) @@ -479,7 +479,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e switch incoming.Filter.(type) { case *TLeaderboardRecordsList_OwnerId: if incomingCursor != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cursor not allowed with haystack query"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cursor not allowed with haystack query")) return } // Haystack queries are executed in a separate flow. @@ -487,11 +487,11 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e return case *TLeaderboardRecordsList_OwnerIds: if incomingCursor != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cursor not allowed with batch filter query"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cursor not allowed with batch filter query")) return } if len(incoming.GetOwnerIds().OwnerIds) < 1 || len(incoming.GetOwnerIds().OwnerIds) > 100 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Must be 1-100 owner IDs"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Must be 1-100 owner IDs")) return } statements := []string{} @@ -515,7 +515,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e // No filter. break default: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Unknown leaderboard record list filter"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Unknown leaderboard record list filter")) return } @@ -551,7 +551,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e rows, err := p.db.Query(query, params...) if err != nil { logger.Error("Could not execute leaderboard records list query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } defer rows.Close() @@ -583,7 +583,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e } if gob.NewEncoder(cursorBuf).Encode(newCursor); err != nil { logger.Error("Error creating leaderboard records list cursor", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error encoding cursor"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } outgoingCursor = cursorBuf.Bytes() @@ -594,7 +594,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e &rankValue, &score, &numScore, &metadata, &rankedAt, &updatedAt, &expiresAt, &bannedAt) if err != nil { logger.Error("Could not scan leaderboard records list query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } @@ -616,7 +616,7 @@ func (p *pipeline) leaderboardRecordsList(logger zap.Logger, session *session, e } if err = rows.Err(); err != nil { logger.Error("Could not process leaderboard records list query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } @@ -668,7 +668,7 @@ func (p *pipeline) loadLeaderboardRecordsHaystack(logger zap.Logger, session *se firstRows, err := p.db.Query(firstQuery, firstParams...) if err != nil { logger.Error("Could not execute leaderboard records list query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } defer firstRows.Close() @@ -691,7 +691,7 @@ func (p *pipeline) loadLeaderboardRecordsHaystack(logger zap.Logger, session *se &rankValue, &score, &numScore, &metadata, &rankedAt, &updatedAt, &expiresAt, &bannedAt) if err != nil { logger.Error("Could not scan leaderboard records list query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } @@ -713,7 +713,7 @@ func (p *pipeline) loadLeaderboardRecordsHaystack(logger zap.Logger, session *se } if err = firstRows.Err(); err != nil { logger.Error("Could not process leaderboard records list query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } @@ -745,7 +745,7 @@ func (p *pipeline) loadLeaderboardRecordsHaystack(logger zap.Logger, session *se secondRows, err := p.db.Query(secondQuery, secondParams...) if err != nil { logger.Error("Could not execute leaderboard records list query", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } defer secondRows.Close() @@ -762,7 +762,7 @@ func (p *pipeline) loadLeaderboardRecordsHaystack(logger zap.Logger, session *se } if gob.NewEncoder(cursorBuf).Encode(newCursor); err != nil { logger.Error("Error creating leaderboard records list cursor", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error encoding cursor"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } outgoingCursor = cursorBuf.Bytes() @@ -773,7 +773,7 @@ func (p *pipeline) loadLeaderboardRecordsHaystack(logger zap.Logger, session *se &rankValue, &score, &numScore, &metadata, &rankedAt, &updatedAt, &expiresAt, &bannedAt) if err != nil { logger.Error("Could not scan leaderboard records list query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } @@ -795,7 +795,7 @@ func (p *pipeline) loadLeaderboardRecordsHaystack(logger zap.Logger, session *se } if err = secondRows.Err(); err != nil { logger.Error("Could not process leaderboard records list query results", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error loading leaderboard records"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error loading leaderboard records")) return } diff --git a/server/pipeline_link_unlink.go b/server/pipeline_link_unlink.go index 853ae554ec..ea0518997c 100644 --- a/server/pipeline_link_unlink.go +++ b/server/pipeline_link_unlink.go @@ -15,9 +15,10 @@ package server import ( + "strconv" + "github.com/uber-go/zap" "golang.org/x/crypto/bcrypt" - "strconv" ) func (p *pipeline) linkID(logger zap.Logger, session *session, envelope *Envelope) { @@ -39,7 +40,7 @@ func (p *pipeline) linkID(logger zap.Logger, session *session, envelope *Envelop p.linkCustom(logger, session, envelope) default: logger.Error("Could not link", zap.String("error", "Invalid payload")) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid payload"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid payload")) return } } @@ -47,20 +48,20 @@ func (p *pipeline) linkID(logger zap.Logger, session *session, envelope *Envelop func (p *pipeline) linkDevice(logger zap.Logger, session *session, envelope *Envelope) { deviceID := envelope.GetLink().GetDevice() if deviceID == "" { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Device ID is required"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Device ID is required")) return } else if invalidCharsRegex.MatchString(deviceID) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid device ID, no spaces or control characters allowed"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid device ID, no spaces or control characters allowed")) return } else if len(deviceID) < 10 || len(deviceID) > 36 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid device ID, must be 10-36 bytes"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid device ID, must be 10-36 bytes")) return } txn, err := p.db.Begin() if err != nil { logger.Warn("Could not link, transaction begin error", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } res, err := txn.Exec("INSERT INTO user_device (id, user_id) VALUES ($1, $2)", deviceID, session.userID.Bytes()) @@ -70,7 +71,7 @@ func (p *pipeline) linkDevice(logger zap.Logger, session *session, envelope *Env if err != nil { logger.Warn("Could not link, transaction rollback error", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } if count, _ := res.RowsAffected(); count == 0 { @@ -78,7 +79,7 @@ func (p *pipeline) linkDevice(logger zap.Logger, session *session, envelope *Env if err != nil { logger.Warn("Could not link, transaction rollback error", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } res, err = txn.Exec("UPDATE users SET updated_at = $1 WHERE id = $2", nowMs(), session.userID.Bytes()) @@ -88,7 +89,7 @@ func (p *pipeline) linkDevice(logger zap.Logger, session *session, envelope *Env if err != nil { logger.Warn("Could not link, transaction rollback error", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } if count, _ := res.RowsAffected(); count == 0 { @@ -96,13 +97,13 @@ func (p *pipeline) linkDevice(logger zap.Logger, session *session, envelope *Env if err != nil { logger.Warn("Could not link, transaction rollback error", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } err = txn.Commit() if err != nil { logger.Warn("Could not register, transaction commit error", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } @@ -112,17 +113,17 @@ func (p *pipeline) linkDevice(logger zap.Logger, session *session, envelope *Env func (p *pipeline) linkFacebook(logger zap.Logger, session *session, envelope *Envelope) { accessToken := envelope.GetLink().GetFacebook() if accessToken == "" { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Access token is required"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Access token is required")) return } else if invalidCharsRegex.MatchString(accessToken) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid Facebook access token, no spaces or control characters allowed"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid Facebook access token, no spaces or control characters allowed")) return } fbProfile, err := p.socialClient.GetFacebookProfile(accessToken) if err != nil { logger.Warn("Could not get Facebook profile", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get Facebook profile"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_PROVIDER_UNAVAILABLE, "Could not get Facebook profile")) return } @@ -140,10 +141,10 @@ AND NOT EXISTS if err != nil { logger.Warn("Could not link", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } else if count, _ := res.RowsAffected(); count == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Facebook ID in use"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_INUSE, "Facebook ID in use")) return } @@ -155,17 +156,17 @@ AND NOT EXISTS func (p *pipeline) linkGoogle(logger zap.Logger, session *session, envelope *Envelope) { accessToken := envelope.GetLink().GetGoogle() if accessToken == "" { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Access token is required"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Access token is required")) return } else if invalidCharsRegex.MatchString(accessToken) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid Google access token, no spaces or control characters allowed"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid Google access token, no spaces or control characters allowed")) return } googleProfile, err := p.socialClient.GetGoogleProfile(accessToken) if err != nil { logger.Warn("Could not get Google profile", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get Google profile"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_PROVIDER_UNAVAILABLE, "Could not get Google profile")) return } @@ -183,10 +184,10 @@ AND NOT EXISTS if err != nil { logger.Warn("Could not link", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } else if count, _ := res.RowsAffected(); count == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Google ID in use"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_INUSE, "Google ID in use")) return } @@ -196,14 +197,14 @@ AND NOT EXISTS func (p *pipeline) linkGameCenter(logger zap.Logger, session *session, envelope *Envelope) { gc := envelope.GetLink().GetGameCenter() if gc == nil || gc.PlayerId == "" || gc.BundleId == "" || gc.Timestamp == 0 || gc.Salt == "" || gc.Signature == "" || gc.PublicKeyUrl == "" { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Game Center credentials required"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Game Center credentials required")) return } _, err := p.socialClient.CheckGameCenterID(gc.PlayerId, gc.BundleId, gc.Timestamp, gc.Salt, gc.Signature, gc.PublicKeyUrl) if err != nil { logger.Warn("Could not get Game Center profile", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get Game Center profile"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_PROVIDER_UNAVAILABLE, "Could not get Game Center profile")) return } @@ -221,10 +222,10 @@ AND NOT EXISTS if err != nil { logger.Warn("Could not link", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } else if count, _ := res.RowsAffected(); count == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Game Center ID in use"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_INUSE, "Game Center ID in use")) return } @@ -233,23 +234,23 @@ AND NOT EXISTS func (p *pipeline) linkSteam(logger zap.Logger, session *session, envelope *Envelope) { if p.config.GetSocial().Steam.PublisherKey == "" || p.config.GetSocial().Steam.AppID == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Steam link not available"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_PROVIDER_UNAVAILABLE, "Steam link not available")) return } ticket := envelope.GetLink().GetSteam() if ticket == "" { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Steam ticket is required"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Steam ticket is required")) return } else if invalidCharsRegex.MatchString(ticket) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid Steam ticket, no spaces or control characters allowed"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid Steam ticket, no spaces or control characters allowed")) return } steamProfile, err := p.socialClient.GetSteamProfile(p.config.GetSocial().Steam.PublisherKey, p.config.GetSocial().Steam.AppID, ticket) if err != nil { logger.Warn("Could not get Steam profile", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get Steam profile"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_PROVIDER_UNAVAILABLE, "Could not get Steam profile")) return } @@ -267,10 +268,10 @@ AND NOT EXISTS if err != nil { logger.Warn("Could not link", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } else if count, _ := res.RowsAffected(); count == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Steam ID in use"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_INUSE, "Steam ID in use")) return } @@ -280,22 +281,22 @@ AND NOT EXISTS func (p *pipeline) linkEmail(logger zap.Logger, session *session, envelope *Envelope) { email := envelope.GetLink().GetEmail() if email == nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid payload"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid payload")) return } else if email.Email == "" { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Email address is required"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Email address is required")) return } else if invalidCharsRegex.MatchString(email.Email) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid email address, no spaces or control characters allowed"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid email address, no spaces or control characters allowed")) return } else if !emailRegex.MatchString(email.Email) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid email address format"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid email address format")) return } else if len(email.Email) < 10 || len(email.Email) > 255 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid email address, must be 10-255 bytes"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid email address, must be 10-255 bytes")) return } else if len(email.Password) < 8 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Password must be longer than 8 characters"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Password must be longer than 8 characters")) return } @@ -316,10 +317,10 @@ AND NOT EXISTS if err != nil { logger.Warn("Could not link", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } else if count, _ := res.RowsAffected(); count == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Email address in use"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_INUSE, "Email address in use")) return } @@ -329,13 +330,13 @@ AND NOT EXISTS func (p *pipeline) linkCustom(logger zap.Logger, session *session, envelope *Envelope) { customID := envelope.GetLink().GetCustom() if customID == "" { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Custom ID is required"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Custom ID is required")) return } else if invalidCharsRegex.MatchString(customID) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid custom ID, no spaces or control characters allowed"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid custom ID, no spaces or control characters allowed")) return } else if len(customID) < 10 || len(customID) > 64 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid custom ID, must be 10-64 bytes"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid custom ID, must be 10-64 bytes")) return } @@ -353,10 +354,10 @@ AND NOT EXISTS if err != nil { logger.Warn("Could not link", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not link"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not link")) return } else if count, _ := res.RowsAffected(); count == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Custom ID in use"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_LINK_INUSE, "Custom ID in use")) return } @@ -372,7 +373,7 @@ func (p *pipeline) unlinkID(logger zap.Logger, session *session, envelope *Envel txn, err := p.db.Begin() if err != nil { logger.Warn("Could not unlink, transaction begin error", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not unlink"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not unlink")) return } res, err := txn.Exec(` @@ -393,7 +394,7 @@ AND (EXISTS (SELECT id FROM users WHERE id = $1 AND if err != nil { logger.Warn("Could not unlink, transaction rollback error", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not unlink"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not unlink")) return } if count, _ := res.RowsAffected(); count == 0 { @@ -401,7 +402,7 @@ AND (EXISTS (SELECT id FROM users WHERE id = $1 AND if err != nil { logger.Warn("Could not unlink, transaction rollback error", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Check profile exists and is not last link"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_UNLINK_DISALLOWED, "Check profile exists and is not last link")) return } res, err = txn.Exec("UPDATE users SET updated_at = $2 WHERE id = $1", session.userID.Bytes(), nowMs()) @@ -411,7 +412,7 @@ AND (EXISTS (SELECT id FROM users WHERE id = $1 AND if err != nil { logger.Warn("Could not unlink, transaction rollback error", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not unlink"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not unlink")) return } if count, _ := res.RowsAffected(); count == 0 { @@ -419,13 +420,13 @@ AND (EXISTS (SELECT id FROM users WHERE id = $1 AND if err != nil { logger.Warn("Could not unlink, transaction rollback error", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Check profile exists and is not last link"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_UNLINK_DISALLOWED, "Check profile exists and is not last link")) return } err = txn.Commit() if err != nil { logger.Warn("Could not unlink, transaction commit error", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not unlink"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not unlink")) return } @@ -505,7 +506,7 @@ AND ((facebook_id IS NOT NULL param = envelope.GetUnlink().GetCustom() default: logger.Error("Could not unlink", zap.String("error", "Invalid payload")) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid payload"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid payload")) return } @@ -513,10 +514,10 @@ AND ((facebook_id IS NOT NULL if err != nil { logger.Warn("Could not unlink", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not unlink"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not unlink")) return } else if count, _ := res.RowsAffected(); count == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Check profile exists and is not last link"}}}) + session.Send(ErrorMessage(envelope.CollationId, USER_UNLINK_DISALLOWED, "Check profile exists and is not last link")) return } diff --git a/server/pipeline_match.go b/server/pipeline_match.go index 739068eb94..c2660afce5 100644 --- a/server/pipeline_match.go +++ b/server/pipeline_match.go @@ -45,14 +45,14 @@ func (p *pipeline) matchJoin(logger zap.Logger, session *session, envelope *Enve matchIDBytes := envelope.GetMatchJoin().MatchId matchID, err := uuid.FromBytes(matchIDBytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid match ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid match ID")) return } topic := "match:" + matchID.String() ps := p.tracker.ListByTopic(topic) if len(ps) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Match not found"}}}) + session.Send(ErrorMessage(envelope.CollationId, MATCH_NOT_FOUND, "Match not found")) return } @@ -89,14 +89,14 @@ func (p *pipeline) matchLeave(logger zap.Logger, session *session, envelope *Env matchIDBytes := envelope.GetMatchLeave().MatchId matchID, err := uuid.FromBytes(matchIDBytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid match ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid match ID")) return } topic := "match:" + matchID.String() ps := p.tracker.ListByTopic(topic) if len(ps) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Match not found"}}}) + session.Send(ErrorMessage(envelope.CollationId, MATCH_NOT_FOUND, "Match not found")) return } @@ -110,7 +110,7 @@ func (p *pipeline) matchLeave(logger zap.Logger, session *session, envelope *Env // If sender wasn't part of the match. if !found { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Match not found"}}}) + session.Send(ErrorMessage(envelope.CollationId, MATCH_NOT_FOUND, "Match not found")) return } diff --git a/server/pipeline_self.go b/server/pipeline_self.go index 53394f4b7c..5f670b4010 100644 --- a/server/pipeline_self.go +++ b/server/pipeline_self.go @@ -55,7 +55,7 @@ WHERE u.id = $1`, session.userID.Bytes()) if err != nil { logger.Error("Could not lookup user profile", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Database request failed"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not lookup user profile")) return } @@ -67,7 +67,7 @@ WHERE u.id = $1`, &createdAt, &updatedAt, &verifiedAt, &lastOnlineAt, &deviceID) if err != nil { logger.Error("Error reading user profile", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Database request failed"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error reading user profile")) return } if deviceID.Valid { @@ -76,7 +76,7 @@ WHERE u.id = $1`, } if err = rows.Err(); err != nil { logger.Error("Error reading user profile", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Database request failed"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error reading user profile")) return } @@ -141,7 +141,7 @@ func (p *pipeline) selfUpdate(logger zap.Logger, session *session, envelope *Env // Make this `var js interface{}` if we want to allow top-level JSON arrays. var maybeJSON map[string]interface{} if json.Unmarshal(update.Metadata, &maybeJSON) != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Metadata must be a valid JSON object"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Metadata must be a valid JSON object")) return } @@ -156,7 +156,7 @@ func (p *pipeline) selfUpdate(logger zap.Logger, session *session, envelope *Env } if len(statements) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "No fields to update"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "No fields to update")) return } @@ -167,11 +167,15 @@ func (p *pipeline) selfUpdate(logger zap.Logger, session *session, envelope *Env params...) if err != nil { - logger.Warn("Could not update user profile", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not update user profile"}}}) + if strings.HasSuffix(err.Error(), "violates unique constraint \"users_handle_key\"") { + session.Send(ErrorMessage(envelope.CollationId, USER_HANDLE_INUSE, "Handle is in use")) + } else { + logger.Warn("Could not update user profile", zap.Error(err)) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not update user profile")) + } return } else if count, _ := res.RowsAffected(); count == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to update user profile"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to update user profile")) return } diff --git a/server/pipeline_storage.go b/server/pipeline_storage.go index 352be8c626..aadf86d704 100644 --- a/server/pipeline_storage.go +++ b/server/pipeline_storage.go @@ -68,21 +68,20 @@ func (p *pipeline) storageFetch(logger zap.Logger, session *session, envelope *E for _, key := range incoming.Keys { if key.Bucket == "" || key.Collection == "" || key.Record == "" { logger.Error("Invalid values for Bucket or Collection or Record") - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid values for Bucket or Collection or Record"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid values for Bucket or Collection or Record")) return } if len(key.UserId) != 0 { userID, err := uuid.FromBytes(key.UserId) if err != nil { - logger.Error("Invalid User ID") - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid User ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid User ID")) return } if userID.String() != session.userID.String() { logger.Error("Not allowed to fetch from storage of a different user") - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Not allowed to fetch from storage of a different user"}}}) + session.Send(ErrorMessage(envelope.CollationId, STORAGE_FETCH_DISALLOWED, "Not allowed to fetch from storage of a different user")) return } } @@ -127,7 +126,7 @@ func (p *pipeline) storageWrite(logger zap.Logger, session *session, envelope *E tx, err := p.db.Begin() if err != nil { logger.Error("Could not store data", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not store data"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not store data")) return } @@ -143,12 +142,12 @@ func (p *pipeline) storageWrite(logger zap.Logger, session *session, envelope *E logger.Error("Could not rollback transaction", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: errorMessage}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, errorMessage)) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: errorMessage}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, errorMessage)) } else { logger.Info("Stored data successfully") session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_StorageKey{StorageKey: &TStorageKey{Keys: response}}}) @@ -232,8 +231,8 @@ func (p *pipeline) storageRemove(logger zap.Logger, session *session, envelope * tx, err := p.db.Begin() if err != nil { - logger.Error("Could not store data", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not store data"}}}) + logger.Error("Could not remove data", zap.Error(err)) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not remove data")) return } @@ -247,12 +246,12 @@ func (p *pipeline) storageRemove(logger zap.Logger, session *session, envelope * logger.Error("Could not rollback transaction", zap.Error(err)) } - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: errorMessage}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, errorMessage)) } else { err = tx.Commit() if err != nil { logger.Error("Could not commit transaction", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: errorMessage}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, errorMessage)) } else { logger.Info("Removed data successfully") session.Send(&Envelope{CollationId: envelope.CollationId}) diff --git a/server/pipeline_topic.go b/server/pipeline_topic.go index 26a7dac394..ff1b227da1 100644 --- a/server/pipeline_topic.go +++ b/server/pipeline_topic.go @@ -45,13 +45,13 @@ func (p *pipeline) topicJoin(logger zap.Logger, session *session, envelope *Enve otherUserIDBytes := id.GetUserId() otherUserID, err := uuid.FromBytes(otherUserIDBytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "User ID not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid User ID")) return } // Don't allow chat to self. if session.userID == otherUserID { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot chat to self"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cannot chat to self")) return } @@ -59,10 +59,10 @@ func (p *pipeline) topicJoin(logger zap.Logger, session *session, envelope *Enve existsAndDoesNotBlock, err := p.userExistsAndDoesNotBlock(otherUserIDBytes, session.userID.Bytes()) if err != nil { logger.Error("Could not check if user exists", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to look up user ID"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to look up user ID")) return } else if !existsAndDoesNotBlock { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "User ID not found"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "User ID not found")) return } @@ -79,15 +79,15 @@ func (p *pipeline) topicJoin(logger zap.Logger, session *session, envelope *Enve // Check input is valid room name. room := id.GetRoom() if room == nil || len(room) < 1 || len(room) > 64 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name is required and must be 1-64 chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name is required and must be 1-64 chars")) return } if invalidRoomRegex.Match(room) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name must not contain control chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name must not contain control chars")) return } if !utf8.Valid(room) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name must not contain control chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name must not contain control chars")) return } @@ -98,7 +98,7 @@ func (p *pipeline) topicJoin(logger zap.Logger, session *session, envelope *Enve groupIDBytes := id.GetGroupId() groupID, err := uuid.FromBytes(groupIDBytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID not valid")) return } @@ -106,20 +106,20 @@ func (p *pipeline) topicJoin(logger zap.Logger, session *session, envelope *Enve member, err := p.isGroupMember(session.userID, groupIDBytes) if err != nil { logger.Error("Could not check if user is group member", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to look up group membership"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to look up group membership")) return } else if !member { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group not found, or not a member"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group not found, or not a member")) return } trackerTopic = "group:" + groupID.String() topic = &TopicId{Id: &TopicId_GroupId{GroupId: groupIDBytes}} case nil: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "No topic ID found"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "No topic ID found")) return default: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Unrecognized topic ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Unrecognized topic ID")) return } @@ -159,7 +159,7 @@ func (p *pipeline) topicLeave(logger zap.Logger, session *session, envelope *Env // Check input is valid DM topic. bothUserIDBytes := topic.GetDm() if bothUserIDBytes == nil || len(bothUserIDBytes) != 32 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } @@ -168,12 +168,12 @@ func (p *pipeline) topicLeave(logger zap.Logger, session *session, envelope *Env userID2Bytes := bothUserIDBytes[16:] userID1, err := uuid.FromBytes(userID1Bytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } userID2, err := uuid.FromBytes(userID2Bytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } @@ -181,19 +181,19 @@ func (p *pipeline) topicLeave(logger zap.Logger, session *session, envelope *Env userID1String := userID1.String() userID2String := userID2.String() if userID1String > userID2String { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } // Check one of the users in this DM topic is the current one. if userID1 != session.userID && userID2 != session.userID { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } // Check the DM topic is between two different users. if userID1 == userID2 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot chat to self"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cannot chat to self")) return } @@ -202,15 +202,15 @@ func (p *pipeline) topicLeave(logger zap.Logger, session *session, envelope *Env // Check input is valid room name. room := topic.GetRoom() if room == nil || len(room) < 1 || len(room) > 64 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name is required and must be 1-64 chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name is required and must be 1-64 chars")) return } if invalidRoomRegex.Match(room) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name must not contain control chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name must not contain control chars")) return } if !utf8.Valid(room) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name must not contain control chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name must not contain control chars")) return } @@ -220,16 +220,16 @@ func (p *pipeline) topicLeave(logger zap.Logger, session *session, envelope *Env groupIDBytes := topic.GetGroupId() groupID, err := uuid.FromBytes(groupIDBytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID not valid")) return } trackerTopic = "group:" + groupID.String() case nil: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "No topic ID found"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "No topic ID found")) return default: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Unrecognized topic ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Unrecognized topic ID")) return } @@ -242,18 +242,18 @@ func (p *pipeline) topicLeave(logger zap.Logger, session *session, envelope *Env func (p *pipeline) topicMessageSend(logger zap.Logger, session *session, envelope *Envelope) { topic := envelope.GetTopicMessageSend().Topic if topic == nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic ID is required"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic ID is required")) return } data := envelope.GetTopicMessageSend().Data if data == nil || len(data) == 0 || len(data) > 1000 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Data is required and must be 1-1000 JSON bytes"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Data is required and must be 1-1000 JSON bytes")) return } // Make this `var js interface{}` if we want to allow top-level JSON arrays. var maybeJSON map[string]interface{} if json.Unmarshal(data, &maybeJSON) != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Data must be a valid JSON object"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Data must be a valid JSON object")) return } @@ -263,7 +263,7 @@ func (p *pipeline) topicMessageSend(logger zap.Logger, session *session, envelop // Check input is valid DM topic. bothUserIDBytes := topic.GetDm() if bothUserIDBytes == nil || len(bothUserIDBytes) != 32 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } @@ -272,12 +272,12 @@ func (p *pipeline) topicMessageSend(logger zap.Logger, session *session, envelop userID2Bytes := bothUserIDBytes[16:] userID1, err := uuid.FromBytes(userID1Bytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } userID2, err := uuid.FromBytes(userID2Bytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } @@ -285,19 +285,19 @@ func (p *pipeline) topicMessageSend(logger zap.Logger, session *session, envelop userID1String := userID1.String() userID2String := userID2.String() if userID1String > userID2String { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } // Check one of the users in this DM topic is the current one. if userID1 != session.userID && userID2 != session.userID { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic not valid")) return } // Check the DM topic is between two different users. if userID1 == userID2 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot chat to self"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cannot chat to self")) return } @@ -306,15 +306,15 @@ func (p *pipeline) topicMessageSend(logger zap.Logger, session *session, envelop // Check input is valid room name. room := topic.GetRoom() if room == nil || len(room) < 1 || len(room) > 64 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name is required and must be 1-64 chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name is required and must be 1-64 chars")) return } if invalidRoomRegex.Match(room) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name must not contain control chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name must not contain control chars")) return } if !utf8.Valid(room) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name must not contain control chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name must not contain control chars")) return } @@ -324,28 +324,28 @@ func (p *pipeline) topicMessageSend(logger zap.Logger, session *session, envelop groupIDBytes := topic.GetGroupId() groupID, err := uuid.FromBytes(groupIDBytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID not valid")) return } trackerTopic = "group:" + groupID.String() case nil: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "No topic ID found"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "No topic ID found")) return default: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Unrecognized topic ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Unrecognized topic ID")) return } if !p.tracker.CheckLocalByIDTopicUser(session.id, trackerTopic, session.userID) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Must join topic before sending messages"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Must join topic before sending messages")) return } // Store message to history. messageID, handle, createdAt, expiresAt, err := p.storeMessage(logger, session, topic, 0, data) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error storing message"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not store message")) return } @@ -365,7 +365,7 @@ func (p *pipeline) topicMessageSend(logger zap.Logger, session *session, envelop func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelope *Envelope) { input := envelope.GetTopicMessagesList() if input.Id == nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Topic ID is required"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Topic ID is required")) return } limit := input.Limit @@ -373,7 +373,7 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo limit = 10 } if limit < 10 || limit > 100 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Limit must be 10-100"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Limit must be 10-100")) return } @@ -386,13 +386,13 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo otherUserIDBytes := input.GetUserId() otherUserID, err := uuid.FromBytes(otherUserIDBytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "User ID not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid User ID")) return } // Don't allow chat to self. if session.userID == otherUserID { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Cannot chat to self"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Cannot chat to self")) return } @@ -409,15 +409,15 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo // Check input is valid room name. room := input.GetRoom() if room == nil || len(room) < 1 || len(room) > 64 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name is required and must be 1-64 chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name is required and must be 1-64 chars")) return } if invalidRoomRegex.Match(room) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name must not contain control chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name must not contain control chars")) return } if !utf8.Valid(room) { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Room name must not contain control chars"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Room name must not contain control chars")) return } @@ -429,7 +429,7 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo groupIDBytes := input.GetGroupId() _, err := uuid.FromBytes(groupIDBytes) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group ID not valid"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group ID not valid")) return } @@ -437,10 +437,10 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo member, err := p.isGroupMember(session.userID, groupIDBytes) if err != nil { logger.Error("Could not check if user is group member", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Failed to look up group membership"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Failed to look up group membership")) return } else if !member { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Group not found, or not a member"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Group not found, or not a member")) return } @@ -448,10 +448,10 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo topicBytes = groupIDBytes topicType = 2 case nil: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "No topic ID found"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "No topic ID found")) return default: - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Unrecognized topic ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Unrecognized topic ID")) return } @@ -462,7 +462,7 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo if input.Cursor != nil { var c messageCursor if err := gob.NewDecoder(bytes.NewReader(input.Cursor)).Decode(&c); err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Invalid cursor data"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "Invalid cursor data")) return } op := "<" @@ -483,7 +483,7 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo rows, err := p.db.Query(query, params...) if err != nil { logger.Error("Could not get topic messages list", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not get topic messages list"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not get topic messages list")) return } defer rows.Close() @@ -502,7 +502,7 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo cursorBuf := new(bytes.Buffer) if gob.NewEncoder(cursorBuf).Encode(&messageCursor{MessageID: messageID, UserID: userID, CreatedAt: createdAt}); err != nil { logger.Error("Error creating topic messages list cursor", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Database request failed"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not create topic messages list cursor")) } cursor = cursorBuf.Bytes() break @@ -510,7 +510,7 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo err = rows.Scan(&messageID, &userID, &createdAt, &expiresAt, &handle, &msgType, &data) if err != nil { logger.Error("Error scanning topic messages list", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error scanning topic messages list"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Error scanning topic messages list")) return } @@ -528,7 +528,7 @@ func (p *pipeline) topicMessagesList(logger zap.Logger, session *session, envelo } if err = rows.Err(); err != nil { logger.Error("Error reading topic history", zap.Error(err)) - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Error reading topic history"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not read topic history")) return } diff --git a/server/pipeline_user.go b/server/pipeline_user.go index 109bf890cc..5212d98560 100644 --- a/server/pipeline_user.go +++ b/server/pipeline_user.go @@ -25,7 +25,7 @@ import ( func (p *pipeline) usersFetch(logger zap.Logger, session *session, envelope *Envelope) { userIds := envelope.GetUsersFetch().UserIds if len(userIds) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "List must contain at least one user ID"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "List must contain at least one user ID")) return } @@ -41,18 +41,17 @@ func (p *pipeline) usersFetch(logger zap.Logger, session *session, envelope *Env statements = append(statements, statement) params = append(params, userID.Bytes()) } - // TODO log invalid IDs? If so, how do we represent them in the log message? } if len(statements) == 0 { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "No valid user IDs received"}}}) + session.Send(ErrorMessageBadInput(envelope.CollationId, "No valid user IDs received")) return } query := "WHERE users.id IN (" + strings.Join(statements, ", ") + ")" users, err := p.querySocialGraph(logger, query, params) if err != nil { - session.Send(&Envelope{CollationId: envelope.CollationId, Payload: &Envelope_Error{&Error{Reason: "Could not retrieve users"}}}) + session.Send(ErrorMessageRuntimeException(envelope.CollationId, "Could not retrieve users")) return } diff --git a/server/session.go b/server/session.go index af6ddddadb..3b727ac7b4 100644 --- a/server/session.go +++ b/server/session.go @@ -88,7 +88,7 @@ func (s *session) Consume(processRequest func(logger zap.Logger, session *sessio err = proto.Unmarshal(data, request) if err != nil { s.logger.Warn("Received malformed payload", zap.Object("data", data)) - s.Send(&Envelope{CollationId: request.CollationId, Payload: &Envelope_Error{&Error{Reason: "Unrecognized message"}}}) + s.Send(ErrorMessage(request.CollationId, UNRECOGNIZED_PAYLOAD, "Unrecognized payload")) } else { // TODO Add session-global context here to cancel in-progress operations when the session is closed. requestLogger := s.logger.With(zap.String("cid", request.CollationId)) diff --git a/server/session_auth.go b/server/session_auth.go index 53c0728a56..7eaf97dfa0 100644 --- a/server/session_auth.go +++ b/server/session_auth.go @@ -204,7 +204,11 @@ func (a *authenticationService) sendAuthError(w http.ResponseWriter, error strin } w.Header().Set("X-Content-Type-Options", "nosniff") w.WriteHeader(errorCode) - authResponse := &AuthenticateResponse{CollationId: collationID, Payload: &AuthenticateResponse_Error_{&AuthenticateResponse_Error{error, authRequest}}} + authResponse := &AuthenticateResponse{CollationId: collationID, Payload: &AuthenticateResponse_Error_{&AuthenticateResponse_Error{ + Code: int32(AUTH_ERROR), + Message: error, + Request: authRequest, + }}} a.sendAuthResponse(w, authResponse) }