8b098dadb208d7009c1bdce50b28e2874d9741fa
- Author
- Pavle Portic <git@theedgeofrage.com>
- Committer
- Pavle Portic <git@theedgeofrage.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/db/channels.go b/db/channels.go
2index 999ba1c706e1d3c32cf4f84f3f1569306985a9e2..00cd8f20cbd2891711f900333b4e7015533eddfb 100644
3--- a/db/channels.go
4+++ b/db/channels.go
5@@ -90,3 +90,19 @@ func (d *postgresDB) SubscribeUserToChannel(ctx context.Context, username string
6
7 return nil
8 }
9+
10+var unsubscribeUserFromChannelQuery = `DELETE FROM user_subscriptions WHERE username = $1 AND channel_id = $2`
11+
12+func (d *postgresDB) UnsubscribeUserFromChannel(ctx context.Context, username string, channelID string) error {
13+ resp, err := d.db.ExecContext(ctx, unsubscribeUserFromChannelQuery, username, channelID)
14+ if err != nil {
15+ d.l.Log("level", "ERROR", "function", "db.SubscribeUserToChannel", "error", err)
16+ return err
17+ }
18+
19+ if affected, err := resp.RowsAffected(); err != nil || affected != 1 {
20+ return ErrChannelNotFound
21+ }
22+
23+ return nil
24+}
25diff --git a/db/db.go b/db/db.go
26index 41d8c14b200b7b6964f18fae1237ec7fc5dca737..c79f451b06277152a15ca1e7788ac2abaf351dfa 100644
27--- a/db/db.go
28+++ b/db/db.go
29@@ -10,6 +10,7 @@ import (
30
31 var (
32 ErrChannelExists = errors.New("channel already exists")
33+ ErrChannelNotFound = errors.New("no channel with that ID found")
34 ErrAlreadySubscribed = errors.New("already subscribed to channel")
35 ErrVideoExists = errors.New("video already exists")
36 ErrUserExists = errors.New("user already exists")
37@@ -30,8 +31,10 @@ type DB interface {
38 ListChannels(ctx context.Context) ([]models.Channel, error)
39 // GetChannelSubscribers lists all channels from the database
40 GetChannelSubscribers(ctx context.Context, channelID string) ([]string, error)
41- // SubscribeUserToChannel will start showing new videos for that channel to the user
42+ // SubscribeUserToChannel will start adding new videos from that channel to the user
43 SubscribeUserToChannel(ctx context.Context, username string, channelID string) error
44+ // SubscribeUserToChannel will stop adding videos from that channel to the user
45+ UnsubscribeUserFromChannel(ctx context.Context, username string, channelID string) error
46
47 // GetNewVideos returns a list of unwatched videos from all subscribed channels
48 GetNewVideos(ctx context.Context, username string) ([]models.Video, error)
49diff --git a/handler/channels.go b/handler/channels.go
50index eebceadf8c8e933f42ff519919196545cce3ef29..0b2b787e0146ea5988f25ba2ee7bbe3e70d710cd 100644
51--- a/handler/channels.go
52+++ b/handler/channels.go
53@@ -27,3 +27,7 @@ func (h *handler) SubscribeToChannel(ctx context.Context, username string, chann
54
55 return h.db.SubscribeUserToChannel(ctx, username, channelID)
56 }
57+
58+func (h *handler) UnsubscribeFromChannel(ctx context.Context, username string, channelID string) error {
59+ return h.db.UnsubscribeUserFromChannel(ctx, username, channelID)
60+}
61diff --git a/handler/handler.go b/handler/handler.go
62index f8f82ae13e8d41093448a5763c0b844b3b241853..8c95bf75ce8fb719695a057b0b3474103c9fb629 100644
63--- a/handler/handler.go
64+++ b/handler/handler.go
65@@ -11,6 +11,7 @@ import (
66 type Handler interface {
67 CreateUser(ctx context.Context, user models.User) error
68 SubscribeToChannel(ctx context.Context, username string, channelID string) error
69+ UnsubscribeFromChannel(ctx context.Context, username string, channelID string) error
70 GetNewVideos(ctx context.Context, username string) ([]models.Video, error)
71 GetWatchedVideos(ctx context.Context, username string) ([]models.Video, error)
72 FetchVideos(ctx context.Context) error
73diff --git a/httpserver/ytrssil/channels.go b/httpserver/ytrssil/channels.go
74index 457677cdfd7932810feb72baddfa096b64c6fd34..78645179129ccce462b16aff7f0f22c5c7dd44c8 100644
75--- a/httpserver/ytrssil/channels.go
76+++ b/httpserver/ytrssil/channels.go
77@@ -37,3 +37,26 @@ func (s *server) SubscribeToChannel(c *gin.Context) {
78
79 c.JSON(http.StatusOK, gin.H{"msg": "subscribed to channel successfully"})
80 }
81+
82+func (s *server) UnsubscribeFromChannel(c *gin.Context) {
83+ var channel models.Channel
84+ err := c.ShouldBindUri(&channel)
85+ if err != nil {
86+ c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
87+ return
88+ }
89+ username := c.GetString("username")
90+
91+ err = s.handler.UnsubscribeFromChannel(c.Request.Context(), username, channel.ID)
92+ if err != nil {
93+ if errors.Is(err, db.ErrChannelNotFound) {
94+ c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": err.Error()})
95+ return
96+ }
97+
98+ c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
99+ return
100+ }
101+
102+ c.JSON(http.StatusOK, gin.H{"msg": "unsubscribed from channel successfully"})
103+}
104diff --git a/httpserver/ytrssil/server.go b/httpserver/ytrssil/server.go
105index 668a8c5ae337b90d9b1d134d625bb02b7441564c..548d4e985c29b2645bc2440310e6c418f8de3aa0 100644
106--- a/httpserver/ytrssil/server.go
107+++ b/httpserver/ytrssil/server.go
108@@ -44,6 +44,7 @@ func SetupGinRouter(l log.Logger, handler handler.Handler, authMiddleware func(c
109 api.Use(authMiddleware)
110 {
111 api.POST("channels/:channel_id/subscribe", srv.SubscribeToChannel)
112+ api.POST("channels/:channel_id/unsubscribe", srv.UnsubscribeFromChannel)
113 api.GET("videos/new", srv.GetNewVideos)
114 api.GET("videos/watched", srv.GetWatchedVideos)
115 api.POST("videos/:video_id/watch", srv.MarkVideoAsWatched)
116diff --git a/mocks/db/db.go b/mocks/db/db.go
117index 0bcbb02b6dcd1d54ce61279dddbdfa4d011ed3c5..4291a0015ebf421af8769d0942d6331f60cbee96 100644
118--- a/mocks/db/db.go
119+++ b/mocks/db/db.go
120@@ -57,6 +57,9 @@ var _ db.DB = &DBMock{}
121 // SubscribeUserToChannelFunc: func(ctx context.Context, username string, channelID string) error {
122 // panic("mock out the SubscribeUserToChannel method")
123 // },
124+// UnsubscribeUserFromChannelFunc: func(ctx context.Context, username string, channelID string) error {
125+// panic("mock out the UnsubscribeUserFromChannel method")
126+// },
127 // }
128 //
129 // // use mockedDB in code that requires db.DB
130@@ -100,6 +103,9 @@ type DBMock struct {
131 // SubscribeUserToChannelFunc mocks the SubscribeUserToChannel method.
132 SubscribeUserToChannelFunc func(ctx context.Context, username string, channelID string) error
133
134+ // UnsubscribeUserFromChannelFunc mocks the UnsubscribeUserFromChannel method.
135+ UnsubscribeUserFromChannelFunc func(ctx context.Context, username string, channelID string) error
136+
137 // calls tracks calls to the methods.
138 calls struct {
139 // AddVideo holds details about calls to the AddVideo method.
140@@ -194,19 +200,29 @@ type DBMock struct {
141 // ChannelID is the channelID argument value.
142 ChannelID string
143 }
144+ // UnsubscribeUserFromChannel holds details about calls to the UnsubscribeUserFromChannel method.
145+ UnsubscribeUserFromChannel []struct {
146+ // Ctx is the ctx argument value.
147+ Ctx context.Context
148+ // Username is the username argument value.
149+ Username string
150+ // ChannelID is the channelID argument value.
151+ ChannelID string
152+ }
153 }
154- lockAddVideo sync.RWMutex
155- lockAddVideoToUser sync.RWMutex
156- lockAuthenticateUser sync.RWMutex
157- lockCreateChannel sync.RWMutex
158- lockCreateUser sync.RWMutex
159- lockDeleteUser sync.RWMutex
160- lockGetChannelSubscribers sync.RWMutex
161- lockGetNewVideos sync.RWMutex
162- lockGetWatchedVideos sync.RWMutex
163- lockListChannels sync.RWMutex
164- lockSetVideoWatchTime sync.RWMutex
165- lockSubscribeUserToChannel sync.RWMutex
166+ lockAddVideo sync.RWMutex
167+ lockAddVideoToUser sync.RWMutex
168+ lockAuthenticateUser sync.RWMutex
169+ lockCreateChannel sync.RWMutex
170+ lockCreateUser sync.RWMutex
171+ lockDeleteUser sync.RWMutex
172+ lockGetChannelSubscribers sync.RWMutex
173+ lockGetNewVideos sync.RWMutex
174+ lockGetWatchedVideos sync.RWMutex
175+ lockListChannels sync.RWMutex
176+ lockSetVideoWatchTime sync.RWMutex
177+ lockSubscribeUserToChannel sync.RWMutex
178+ lockUnsubscribeUserFromChannel sync.RWMutex
179 }
180
181 // AddVideo calls AddVideoFunc.
182@@ -656,3 +672,43 @@ func (mock *DBMock) SubscribeUserToChannelCalls() []struct {
183 mock.lockSubscribeUserToChannel.RUnlock()
184 return calls
185 }
186+
187+// UnsubscribeUserFromChannel calls UnsubscribeUserFromChannelFunc.
188+func (mock *DBMock) UnsubscribeUserFromChannel(ctx context.Context, username string, channelID string) error {
189+ if mock.UnsubscribeUserFromChannelFunc == nil {
190+ panic("DBMock.UnsubscribeUserFromChannelFunc: method is nil but DB.UnsubscribeUserFromChannel was just called")
191+ }
192+ callInfo := struct {
193+ Ctx context.Context
194+ Username string
195+ ChannelID string
196+ }{
197+ Ctx: ctx,
198+ Username: username,
199+ ChannelID: channelID,
200+ }
201+ mock.lockUnsubscribeUserFromChannel.Lock()
202+ mock.calls.UnsubscribeUserFromChannel = append(mock.calls.UnsubscribeUserFromChannel, callInfo)
203+ mock.lockUnsubscribeUserFromChannel.Unlock()
204+ return mock.UnsubscribeUserFromChannelFunc(ctx, username, channelID)
205+}
206+
207+// UnsubscribeUserFromChannelCalls gets all the calls that were made to UnsubscribeUserFromChannel.
208+// Check the length with:
209+//
210+// len(mockedDB.UnsubscribeUserFromChannelCalls())
211+func (mock *DBMock) UnsubscribeUserFromChannelCalls() []struct {
212+ Ctx context.Context
213+ Username string
214+ ChannelID string
215+} {
216+ var calls []struct {
217+ Ctx context.Context
218+ Username string
219+ ChannelID string