Skip to content

Commit 7046ddc

Browse files
authored
Merge pull request #7 from SwimResults/develop
Register Notification User
2 parents 85f6e25 + 9b3f614 commit 7046ddc

11 files changed

+424
-3
lines changed

controller/controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func Run() {
2222
userController()
2323
widgetController()
2424
dashboardController()
25+
notificationUserController()
2526

2627
router.GET("/actuator", actuator)
2728

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package controller
2+
3+
import (
4+
"github.com/gin-gonic/gin"
5+
"github.com/swimresults/user-service/dto"
6+
"github.com/swimresults/user-service/model"
7+
"github.com/swimresults/user-service/service"
8+
"go.mongodb.org/mongo-driver/bson/primitive"
9+
"net/http"
10+
)
11+
12+
func notificationUserController() {
13+
router.GET("/notification_users", getNotificationUsers)
14+
router.GET("/notification_user", getNotificationUser)
15+
router.GET("/notification_user/:id", getNotificationUserById)
16+
17+
router.POST("/notification_user", addNotificationUser)
18+
router.POST("/notification_user/register", registerNotificationUser)
19+
20+
router.DELETE("/notification_user/:id", removeNotificationUser)
21+
22+
router.PUT("/notification_user", updateNotificationUser)
23+
24+
router.OPTIONS("/notification_user", okay)
25+
router.OPTIONS("/notification_user/register", okay)
26+
}
27+
28+
func getNotificationUsers(c *gin.Context) {
29+
30+
if failIfNotRoot(c) {
31+
return
32+
}
33+
34+
users, err := service.GetNotificationUsers()
35+
if err != nil {
36+
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
37+
return
38+
}
39+
40+
c.IndentedJSON(http.StatusOK, users)
41+
}
42+
43+
func getNotificationUser(c *gin.Context) {
44+
45+
claims, err1 := getClaimsFromAuthHeader(c)
46+
47+
if err1 != nil {
48+
c.IndentedJSON(http.StatusNotFound, gin.H{"message": err1.Error()})
49+
return
50+
}
51+
52+
user, err := service.GetUserByKeycloakId(claims.Sub)
53+
if err != nil {
54+
c.IndentedJSON(http.StatusNotFound, gin.H{"message": err.Error()})
55+
return
56+
}
57+
58+
notificationUser, err2 := service.GetNotificationUserById(user.Identifier)
59+
if err2 != nil {
60+
c.IndentedJSON(http.StatusNotFound, gin.H{"message": err.Error()})
61+
return
62+
}
63+
64+
c.IndentedJSON(http.StatusOK, notificationUser)
65+
}
66+
67+
func getNotificationUserById(c *gin.Context) {
68+
69+
if failIfNotRoot(c) {
70+
return
71+
}
72+
73+
id, convErr := primitive.ObjectIDFromHex(c.Param("id"))
74+
if convErr != nil {
75+
c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "given id was not of type ObjectID"})
76+
return
77+
}
78+
79+
user, err := service.GetNotificationUserById(id)
80+
if err != nil {
81+
c.IndentedJSON(http.StatusNotFound, gin.H{"message": err.Error()})
82+
return
83+
}
84+
85+
c.IndentedJSON(http.StatusOK, user)
86+
}
87+
88+
func removeNotificationUser(c *gin.Context) {
89+
90+
if failIfNotRoot(c) {
91+
return
92+
}
93+
94+
id, convErr := primitive.ObjectIDFromHex(c.Param("id"))
95+
if convErr != nil {
96+
c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "given id was not of type ObjectID"})
97+
return
98+
}
99+
100+
err := service.RemoveNotificationUserById(id)
101+
if err != nil {
102+
c.IndentedJSON(http.StatusNotFound, gin.H{"message": err.Error()})
103+
return
104+
}
105+
106+
c.IndentedJSON(http.StatusNoContent, "")
107+
}
108+
109+
func addNotificationUser(c *gin.Context) {
110+
111+
if failIfNotRoot(c) {
112+
return
113+
}
114+
115+
var user model.NotificationUser
116+
if err := c.BindJSON(&user); err != nil {
117+
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
118+
return
119+
}
120+
121+
r, err := service.AddNotificationUser(user)
122+
if err != nil {
123+
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
124+
return
125+
}
126+
127+
c.IndentedJSON(http.StatusOK, r)
128+
}
129+
130+
func updateNotificationUser(c *gin.Context) {
131+
132+
if failIfNotRoot(c) {
133+
return
134+
}
135+
136+
var user model.NotificationUser
137+
if err := c.BindJSON(&user); err != nil {
138+
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
139+
return
140+
}
141+
142+
r, err := service.UpdateNotificationUser(user)
143+
if err != nil {
144+
c.IndentedJSON(http.StatusNotFound, gin.H{"message": err.Error()})
145+
return
146+
}
147+
148+
c.IndentedJSON(http.StatusOK, r)
149+
}
150+
151+
func registerNotificationUser(c *gin.Context) {
152+
var request dto.RegisterNotificationUserRequestDto
153+
if err := c.BindJSON(&request); err != nil {
154+
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
155+
return
156+
}
157+
158+
r, err := service.RegisterNotificationUser(request.Token)
159+
if err != nil {
160+
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
161+
return
162+
}
163+
164+
c.IndentedJSON(http.StatusOK, r)
165+
}

docs/notifications.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
- different options in terms of complexity:
2+
- special notification settings for each combination of
3+
- meeting
4+
- athlete
5+
- team
6+
- only default settings
7+
- some settings for different notification types
8+
- files
9+
- favorites
10+
- team mates
11+
- myself
12+
- settings for time of notification → 15 min before start?
13+
14+
# Notify about…
15+
16+
- free text
17+
18+
## Before Meeting
19+
20+
- ~~new files → prob only interesting for start list? other publications?~~
21+
- start list available/published → to find own starts and add favourites
22+
- meeting upcoming reminder → to remember that SwimResults exists for this meeting (only when subscribed)
23+
- new meeting with your athlete → remember user that app exists (only if not subscribed but meeting with his athlete, instead of two previous ones?)
24+
25+
## During Meeting
26+
27+
- live stream info → maybe as part of start notification (meeting has been started on time, see live timing and stream)
28+
- me → be informed about my own start
29+
- upcoming start → reminder to not miss a start
30+
- new result published → to know places as fast as possible
31+
- new start imported → final qualification, changes in the start list
32+
- favourites
33+
- upcoming start → don’t miss your friend’s/child’s/favourite’s starts
34+
- new result published → to know places as fast as possible, really necessary?
35+
- ~~swimming right now? → live link + stream → upcoming start is enough~~
36+
- new start imported → final qualification, changes in the start list
37+
- team mates
38+
- ~~upcoming start → too much~~
39+
- ~~new result published → too much~~
40+
- ~~swimming right now? → live link + stream~~
41+
- new start imported → final qualification (only finals!)
42+
- schedule
43+
- next start delayed → to know if you have more break time
44+
- next start early → to not miss starts
45+
- changes/updates (break shortened, etc.) → to know about changed breaks/warmup times
46+
47+
## After Meeting
48+
49+
- results and results file published → to receive final results without checking all the time
50+
51+
# Notification Settings
52+
53+
configure which…
54+
55+
Athlete Notifications
56+
57+
Favourites Notifications
58+
59+
Schedule Notifications
60+
61+
Meeting Notifications
62+
63+
… you want to receive
64+
65+
# Notification Service vs. User Service
66+
67+
| Notification Service | Notifications in User Service |
68+
| --- | --- |
69+
| not affected by user service availability | no new micro service |
70+
| | no inter-service relations since notifications are user-related |
71+
| | less server resources needed |
72+
| | user settings and notification settings in one place |
73+
| | closer relation to subscriptions |
74+
75+
# SwimResults User ↔ Notification User
76+
77+
- Notifications are pay feature
78+
- with SwimResults PLUS or SwimResults PRO user has notifications
79+
- for both an account is required
80+
- notifications per account
81+
- no notification user without account?
82+
- but what about general notifications for everyone?! → notification identifier required
83+
- what about multiple devices of the same user?
84+
- maybe subscription not related to account?!
85+
- app only with account?
86+
87+
# Questions to clarify
88+
89+
## How prevent multiple notifications for the same reason?
90+
91+
## Should subscriptions always be related to a user account?
92+
93+
## How to handle multiple devices of the same user?
94+
95+
## Are there notifications without subscription?
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package dto
2+
3+
type RegisterNotificationUserRequestDto struct {
4+
Token string `json:"token,omitempty"`
5+
}

model/following.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,19 @@ import (
55
"time"
66
)
77

8-
type Following struct {
8+
type FollowingAthlete struct {
99
AthleteId primitive.ObjectID `json:"athlete_id" bson:"athlete_id"`
1010
AddedAt time.Time `json:"added_at,omitempty" bson:"added_at,omitempty"`
1111
}
12+
13+
// FollowingTeam TODO make teams followable
14+
type FollowingTeam struct {
15+
TeamId primitive.ObjectID `json:"team_id" bson:"team_id"`
16+
AddedAt time.Time `json:"added_at,omitempty" bson:"added_at,omitempty"`
17+
}
18+
19+
// Following TODO use merged struct
20+
type Following struct {
21+
FollowingAthletes []FollowingAthlete `json:"following_athletes,omitempty" bson:"following_athletes,omitempty"`
22+
FollowingTeams []FollowingTeam `json:"following_teams,omitempty" bson:"following_teams,omitempty"`
23+
}

model/notification_user.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package model
2+
3+
import "go.mongodb.org/mongo-driver/bson/primitive"
4+
5+
type NotificationUser struct {
6+
Identifier primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
7+
UserId primitive.ObjectID `json:"user_id,omitempty" bson:"user_id,omitempty"`
8+
Token string `json:"token,omitempty" bson:"token,omitempty"`
9+
Settings Settings `json:"settings,omitempty" bson:"settings,omitempty"`
10+
Meetings []string `json:"meetings,omitempty" bson:"meetings,omitempty"`
11+
}

model/user.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
type User struct {
88
Identifier primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
99
KeycloakId string `json:"keycloak_id,omitempty" bson:"keycloak_id,omitempty"`
10-
Following []Following `json:"following,omitempty" bson:"following,omitempty"`
10+
Following []FollowingAthlete `json:"following,omitempty" bson:"following,omitempty"`
1111
OwnAthleteId *primitive.ObjectID `json:"own_athlete_id,omitempty" bson:"own_athlete_id,omitempty"`
1212
Settings Settings `json:"settings,omitempty" bson:"settings,omitempty"`
1313
Meetings []string `json:"meetings,omitempty" bson:"meetings,omitempty"`

model/widget_tile.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ import "go.mongodb.org/mongo-driver/bson/primitive"
55
type WidgetTile struct {
66
WidgetID primitive.ObjectID `json:"-" bson:"widget_id,omitempty"`
77
Widget Widget `json:"widget,omitempty" bson:"-"`
8+
Data any `json:"data,omitempty" bson:"data,omitempty"`
89
OrderPosition int `json:"order_position,omitempty" bson:"order_position,omitempty"`
910
}

0 commit comments

Comments
 (0)