Skip to content

Commit 705fdda

Browse files
committed
feat: v4Internal/notifications & better notification/order handling for map builder invites.
Notifications added to v4 internal. The create endpoint is removed (for now only created from service). It also is no longer a subpath of player since that was redundant everywhere but create. Map builder invites now handle revoking invites if removed from a map before accepting, as well as weird acceptance/denial when not pending
1 parent a93f744 commit 705fdda

20 files changed

Lines changed: 517 additions & 196 deletions

api/mapsV3/intnl/openapi.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,9 @@ paths:
456456
- name: playerId
457457
in: query
458458
schema: { type: string }
459+
- name: notify
460+
in: query
461+
schema: { type: boolean }
459462
responses:
460463
'200':
461464
description: Leaderboard successfully deleted

api/mapsV3/intnl/server.gen.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/mapsV3/intnl/server.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ type server struct {
4747

4848
objectClient object.Client
4949

50-
notificationManager notification.Manager
50+
notifications notification.Manager
5151
}
5252

5353
func NewServer(params ServerParams) (StrictServerInterface, error) {
@@ -64,13 +64,13 @@ func NewServer(params ServerParams) (StrictServerInterface, error) {
6464
}
6565

6666
return &server{
67-
log: params.Log,
68-
store: params.Store,
69-
playerStore: params.PlayerStore,
70-
redis: params.Redis,
71-
jetStream: params.JetStream,
72-
metrics: params.Metrics,
73-
objectClient: params.Object,
74-
notificationManager: params.NotificationManager,
67+
log: params.Log,
68+
store: params.Store,
69+
playerStore: params.PlayerStore,
70+
redis: params.Redis,
71+
jetStream: params.JetStream,
72+
metrics: params.Metrics,
73+
objectClient: params.Object,
74+
notifications: params.NotificationManager,
7575
}, nil
7676
}

api/mapsV3/intnl/server_leaderboard.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/hollow-cube/api-server/internal/pkg/common"
1111
"github.com/hollow-cube/api-server/internal/pkg/model"
12+
"github.com/hollow-cube/api-server/internal/pkg/notification"
1213
"github.com/redis/rueidis"
1314
)
1415

@@ -173,6 +174,17 @@ func (s *server) DeleteMapLeaderboard(ctx context.Context, request DeleteMapLead
173174
if err != nil && !errors.Is(err, rueidis.Nil) {
174175
return nil, fmt.Errorf("failed to delete player score: %w", err)
175176
}
177+
178+
if request.Params.Notify != nil && *request.Params.Notify {
179+
err = s.notifications.Create(ctx, *playerId, notification.CreateInput{
180+
Key: request.MapId,
181+
Type: "map_time_deleted",
182+
ReplaceUnread: true,
183+
})
184+
if err != nil {
185+
s.log.Warnw("failed to create notification", "playerId", *playerId, "error", err)
186+
}
187+
}
176188
}
177189

178190
return DeleteMapLeaderboard200Response{}, nil

api/v2/intnl/server.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,25 +93,25 @@ func NewServer(p ServerParams) (StrictServerInterface, error) {
9393
}
9494

9595
return &Server{
96-
log: p.Log.With("handler", "internal"),
97-
metrics: p.Metrics,
98-
store: p.Store,
99-
jetStream: p.JetStream,
100-
tbHeadless: p.TBHeadless,
101-
notificationManager: p.NotificationManager,
102-
punishmentLadders: p.PunishmentLadders,
103-
punishmentAliases: punishmentAliases,
96+
log: p.Log.With("handler", "internal"),
97+
metrics: p.Metrics,
98+
store: p.Store,
99+
jetStream: p.JetStream,
100+
tbHeadless: p.TBHeadless,
101+
notifications: p.NotificationManager,
102+
punishmentLadders: p.PunishmentLadders,
103+
punishmentAliases: punishmentAliases,
104104
}, nil
105105
}
106106

107107
type Server struct {
108108
log *zap.SugaredLogger
109109
metrics metric.Writer
110110

111-
store *playerdb.Store
112-
jetStream *natsutil.JetStreamWrapper
113-
tbHeadless *tebex.HeadlessClient
114-
notificationManager notification.Manager
111+
store *playerdb.Store
112+
jetStream *natsutil.JetStreamWrapper
113+
tbHeadless *tebex.HeadlessClient
114+
notifications notification.Manager
115115

116116
punishmentLadders map[string]*model.PunishmentLadder
117117
punishmentAliases map[model.PunishmentType]map[string]*model.PunishmentLadder
@@ -300,6 +300,7 @@ func (s *Server) GetPlayerAlts(ctx context.Context, request GetPlayerAltsRequest
300300
return GetPlayerAlts200JSONResponse{Results: results}, nil
301301
}
302302

303+
// v4
303304
func (s *Server) CyclePlayerApiKey(ctx context.Context, request CyclePlayerApiKeyRequestObject) (CyclePlayerApiKeyResponseObject, error) {
304305
res, err := playerdb.Tx(ctx, s.store, func(ctx context.Context, txStore *playerdb.Store) (*CyclePlayerApiKey200JSONResponse, error) {
305306
_, err := txStore.GetPlayerData(ctx, request.PlayerId)
@@ -343,6 +344,7 @@ func (s *Server) GetPlayerId(ctx context.Context, request GetPlayerIdRequestObje
343344
return GetPlayerId200TextResponse(pid), nil
344345
}
345346

347+
// v4
346348
func (s *Server) PerformTabComplete(ctx context.Context, request PerformTabCompleteRequestObject) (PerformTabCompleteResponseObject, error) {
347349
if request.Body.Query == "" {
348350
return PerformTabComplete200JSONResponse{Result: []TabCompleteEntry{}}, nil

api/v2/intnl/server_notifications.go

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import (
55
"errors"
66
"fmt"
77

8-
"github.com/hollow-cube/api-server/internal/pkg/model"
98
"github.com/hollow-cube/api-server/internal/pkg/notification"
9+
"github.com/hollow-cube/api-server/internal/playerdb"
1010
)
1111

12+
// v4
1213
func (s *Server) DeletePlayerNotification(ctx context.Context, request DeletePlayerNotificationRequestObject) (DeletePlayerNotificationResponseObject, error) {
13-
if err := s.notificationManager.DeleteNotification(ctx, request.NotificationId, request.PlayerId); err != nil {
14+
if err := s.notifications.Delete(ctx, request.NotificationId); err != nil {
1415
if errors.Is(err, notification.ErrNotFound) {
1516
return DeletePlayerNotification404Response{}, nil
1617
}
@@ -19,6 +20,7 @@ func (s *Server) DeletePlayerNotification(ctx context.Context, request DeletePla
1920
return DeletePlayerNotification200Response{}, nil
2021
}
2122

23+
// v4
2224
func (s *Server) GetPlayerNotifications(ctx context.Context, request GetPlayerNotificationsRequestObject) (GetPlayerNotificationsResponseObject, error) {
2325
var unreadOnly = request.Params.Unread != nil && *request.Params.Unread
2426

@@ -30,26 +32,29 @@ func (s *Server) GetPlayerNotifications(ctx context.Context, request GetPlayerNo
3032
return GetPlayerNotifications400JSONResponse{Message: "page must be non-negative"}, nil
3133
}
3234

33-
result, err := s.notificationManager.GetNotifications(ctx, request.PlayerId, page, unreadOnly)
35+
offset := int32(page * 21)
36+
limit := int32(21)
37+
38+
result, count, err := s.notifications.List(ctx, request.PlayerId, unreadOnly, offset, limit)
3439
if err != nil {
3540
return nil, err
3641
}
3742

38-
results := make([]PlayerNotification, len(result.Results))
39-
for i, notif := range result.Results {
43+
results := make([]PlayerNotification, len(result))
44+
for i, notif := range result {
4045
results[i] = notificationToApi(notif)
4146
}
4247

4348
return GetPlayerNotifications200JSONResponse{
44-
Page: result.Page,
45-
PageCount: result.PageCount,
49+
Page: int32(page),
50+
PageCount: int32(count/21) + 1,
4651
Results: results,
4752
}, nil
4853
}
4954

50-
func notificationToApi(n notification.Notification) PlayerNotification {
55+
func notificationToApi(n playerdb.PlayerNotification) PlayerNotification {
5156
return PlayerNotification{
52-
Id: n.Id,
57+
Id: n.ID,
5358
Type: n.Type,
5459
Key: n.Key,
5560
Data: n.Data,
@@ -59,8 +64,9 @@ func notificationToApi(n notification.Notification) PlayerNotification {
5964
}
6065
}
6166

67+
// v4
6268
func (s *Server) UpdatePlayerNotification(ctx context.Context, request UpdatePlayerNotificationRequestObject) (UpdatePlayerNotificationResponseObject, error) {
63-
if err := s.notificationManager.UpdateNotification(ctx, request.NotificationId, request.PlayerId, request.Body.Read); err != nil {
69+
if err := s.notifications.SetReadState(ctx, request.NotificationId, request.Body.Read); err != nil {
6470
if errors.Is(err, notification.ErrNotFound) {
6571
return UpdatePlayerNotification404Response{}, nil
6672
}
@@ -69,6 +75,7 @@ func (s *Server) UpdatePlayerNotification(ctx context.Context, request UpdatePla
6975
return UpdatePlayerNotification200Response{}, nil
7076
}
7177

78+
// v4 removed
7279
func (s *Server) CreatePlayerNotification(ctx context.Context, request CreatePlayerNotificationRequestObject) (CreatePlayerNotificationResponseObject, error) {
7380
replaceUnread := false
7481
if request.Params.ReplaceUnread != nil {
@@ -79,23 +86,23 @@ func (s *Server) CreatePlayerNotification(ctx context.Context, request CreatePla
7986
Key: request.Body.Key,
8087
Type: request.Body.Type,
8188
ExpiresIn: request.Body.ExpiresIn,
82-
Data: request.Body.Data,
89+
Data: *request.Body.Data,
8390
ReplaceUnread: replaceUnread,
8491
}
85-
if err := s.notificationManager.Create(ctx, request.PlayerId, input); err != nil {
92+
if err := s.notifications.Create(ctx, request.PlayerId, input); err != nil {
8693
return nil, err
8794
}
8895

8996
return CreatePlayerNotification201Response{}, nil
9097
}
9198

9299
func (s *Server) sendNotificationMessage(ctx context.Context, request CreatePlayerNotificationRequestObject) error {
93-
msg := model.NotificationUpdateMessage{
94-
Action: model.NotificationCreateAction,
100+
msg := notification.UpdateMessage{
101+
Action: notification.CreateAction,
95102
PlayerId: request.PlayerId,
96103
Type: request.Body.Type,
97104
Key: request.Body.Key,
98-
Data: request.Body.Data,
105+
Data: *request.Body.Data,
99106
}
100107
if err := s.jetStream.PublishJSONAsync(ctx, msg); err != nil {
101108
return fmt.Errorf("failed to publish notification message: %w", err)

api/v2/intnl/server_relationships.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,12 +338,12 @@ func (s *Server) SendFriendRequest(ctx context.Context, request SendFriendReques
338338
ReplaceUnread: false,
339339
}
340340
// Send notification of friend request (to both players)
341-
if err := s.notificationManager.Create(ctx, request.TargetId, notifInput); err != nil {
341+
if err := s.notifications.Create(ctx, request.TargetId, notifInput); err != nil {
342342
s.log.Warnw("failed to send friendship notification - continuing", "playerId", request.PlayerId, "targetId", request.TargetId, "err", err)
343343
}
344344

345345
notifInput.Key = request.TargetId
346-
if err := s.notificationManager.Create(ctx, request.PlayerId, notifInput); err != nil {
346+
if err := s.notifications.Create(ctx, request.PlayerId, notifInput); err != nil {
347347
s.log.Warnw("failed to send friendship notification - continuing", "playerId", request.PlayerId, "targetId", request.TargetId, "err", err)
348348
}
349349
return SendFriendRequest201JSONResponse{IsRequest: false}, nil
@@ -388,7 +388,7 @@ func (s *Server) SendFriendRequest(ctx context.Context, request SendFriendReques
388388
}
389389

390390
// Send notification of friend request
391-
if err := s.notificationManager.Create(ctx, request.TargetId, notification.CreateInput{
391+
if err := s.notifications.Create(ctx, request.TargetId, notification.CreateInput{
392392
Key: request.PlayerId, // Use the player ID as the key for a friend_request type
393393
Type: "friend_request",
394394
ReplaceUnread: true,

api/v4Internal/openapi.yaml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,66 @@ paths:
150150
responses:
151151
"200":
152152
description: Successful response
153+
/notifications:
154+
get:
155+
operationId: getNotifications
156+
parameters:
157+
- name: playerId
158+
in: query
159+
required: true
160+
schema:
161+
type: string
162+
- name: page
163+
in: query
164+
required: true
165+
schema:
166+
type: integer
167+
- name: pageSize
168+
in: query
169+
required: true
170+
schema:
171+
type: integer
172+
- name: unreadOnly
173+
in: query
174+
required: true
175+
schema:
176+
type: boolean
177+
responses:
178+
"200":
179+
description: Successful response
180+
content:
181+
application/json:
182+
schema:
183+
type: object
184+
/notifications/{notificationId}:
185+
delete:
186+
operationId: deleteNotification
187+
parameters:
188+
- name: notificationId
189+
in: path
190+
required: true
191+
schema:
192+
type: string
193+
responses:
194+
"204":
195+
description: Successful response
196+
patch:
197+
operationId: updateNotification
198+
parameters:
199+
- name: notificationId
200+
in: path
201+
required: true
202+
schema:
203+
type: string
204+
requestBody:
205+
required: true
206+
content:
207+
application/json:
208+
schema:
209+
type: object
210+
responses:
211+
"200":
212+
description: Successful response
153213
/players:
154214
post:
155215
operationId: createPlayerData

0 commit comments

Comments
 (0)