Add unsubscribe functionality
This commit is contained in:
parent
337b4af251
commit
8b098dadb2
|
@ -90,3 +90,19 @@ func (d *postgresDB) SubscribeUserToChannel(ctx context.Context, username string
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var unsubscribeUserFromChannelQuery = `DELETE FROM user_subscriptions WHERE username = $1 AND channel_id = $2`
|
||||||
|
|
||||||
|
func (d *postgresDB) UnsubscribeUserFromChannel(ctx context.Context, username string, channelID string) error {
|
||||||
|
resp, err := d.db.ExecContext(ctx, unsubscribeUserFromChannelQuery, username, channelID)
|
||||||
|
if err != nil {
|
||||||
|
d.l.Log("level", "ERROR", "function", "db.SubscribeUserToChannel", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if affected, err := resp.RowsAffected(); err != nil || affected != 1 {
|
||||||
|
return ErrChannelNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
5
db/db.go
5
db/db.go
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrChannelExists = errors.New("channel already exists")
|
ErrChannelExists = errors.New("channel already exists")
|
||||||
|
ErrChannelNotFound = errors.New("no channel with that ID found")
|
||||||
ErrAlreadySubscribed = errors.New("already subscribed to channel")
|
ErrAlreadySubscribed = errors.New("already subscribed to channel")
|
||||||
ErrVideoExists = errors.New("video already exists")
|
ErrVideoExists = errors.New("video already exists")
|
||||||
ErrUserExists = errors.New("user already exists")
|
ErrUserExists = errors.New("user already exists")
|
||||||
|
@ -30,8 +31,10 @@ type DB interface {
|
||||||
ListChannels(ctx context.Context) ([]models.Channel, error)
|
ListChannels(ctx context.Context) ([]models.Channel, error)
|
||||||
// GetChannelSubscribers lists all channels from the database
|
// GetChannelSubscribers lists all channels from the database
|
||||||
GetChannelSubscribers(ctx context.Context, channelID string) ([]string, error)
|
GetChannelSubscribers(ctx context.Context, channelID string) ([]string, error)
|
||||||
// SubscribeUserToChannel will start showing new videos for that channel to the user
|
// SubscribeUserToChannel will start adding new videos from that channel to the user
|
||||||
SubscribeUserToChannel(ctx context.Context, username string, channelID string) error
|
SubscribeUserToChannel(ctx context.Context, username string, channelID string) error
|
||||||
|
// SubscribeUserToChannel will stop adding videos from that channel to the user
|
||||||
|
UnsubscribeUserFromChannel(ctx context.Context, username string, channelID string) error
|
||||||
|
|
||||||
// GetNewVideos returns a list of unwatched videos from all subscribed channels
|
// GetNewVideos returns a list of unwatched videos from all subscribed channels
|
||||||
GetNewVideos(ctx context.Context, username string) ([]models.Video, error)
|
GetNewVideos(ctx context.Context, username string) ([]models.Video, error)
|
||||||
|
|
|
@ -27,3 +27,7 @@ func (h *handler) SubscribeToChannel(ctx context.Context, username string, chann
|
||||||
|
|
||||||
return h.db.SubscribeUserToChannel(ctx, username, channelID)
|
return h.db.SubscribeUserToChannel(ctx, username, channelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *handler) UnsubscribeFromChannel(ctx context.Context, username string, channelID string) error {
|
||||||
|
return h.db.UnsubscribeUserFromChannel(ctx, username, channelID)
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
CreateUser(ctx context.Context, user models.User) error
|
CreateUser(ctx context.Context, user models.User) error
|
||||||
SubscribeToChannel(ctx context.Context, username string, channelID string) error
|
SubscribeToChannel(ctx context.Context, username string, channelID string) error
|
||||||
|
UnsubscribeFromChannel(ctx context.Context, username string, channelID string) error
|
||||||
GetNewVideos(ctx context.Context, username string) ([]models.Video, error)
|
GetNewVideos(ctx context.Context, username string) ([]models.Video, error)
|
||||||
GetWatchedVideos(ctx context.Context, username string) ([]models.Video, error)
|
GetWatchedVideos(ctx context.Context, username string) ([]models.Video, error)
|
||||||
FetchVideos(ctx context.Context) error
|
FetchVideos(ctx context.Context) error
|
||||||
|
|
|
@ -37,3 +37,26 @@ func (s *server) SubscribeToChannel(c *gin.Context) {
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"msg": "subscribed to channel successfully"})
|
c.JSON(http.StatusOK, gin.H{"msg": "subscribed to channel successfully"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *server) UnsubscribeFromChannel(c *gin.Context) {
|
||||||
|
var channel models.Channel
|
||||||
|
err := c.ShouldBindUri(&channel)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
username := c.GetString("username")
|
||||||
|
|
||||||
|
err = s.handler.UnsubscribeFromChannel(c.Request.Context(), username, channel.ID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, db.ErrChannelNotFound) {
|
||||||
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"msg": "unsubscribed from channel successfully"})
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ func SetupGinRouter(l log.Logger, handler handler.Handler, authMiddleware func(c
|
||||||
api.Use(authMiddleware)
|
api.Use(authMiddleware)
|
||||||
{
|
{
|
||||||
api.POST("channels/:channel_id/subscribe", srv.SubscribeToChannel)
|
api.POST("channels/:channel_id/subscribe", srv.SubscribeToChannel)
|
||||||
|
api.POST("channels/:channel_id/unsubscribe", srv.UnsubscribeFromChannel)
|
||||||
api.GET("videos/new", srv.GetNewVideos)
|
api.GET("videos/new", srv.GetNewVideos)
|
||||||
api.GET("videos/watched", srv.GetWatchedVideos)
|
api.GET("videos/watched", srv.GetWatchedVideos)
|
||||||
api.POST("videos/:video_id/watch", srv.MarkVideoAsWatched)
|
api.POST("videos/:video_id/watch", srv.MarkVideoAsWatched)
|
||||||
|
|
|
@ -57,6 +57,9 @@ var _ db.DB = &DBMock{}
|
||||||
// SubscribeUserToChannelFunc: func(ctx context.Context, username string, channelID string) error {
|
// SubscribeUserToChannelFunc: func(ctx context.Context, username string, channelID string) error {
|
||||||
// panic("mock out the SubscribeUserToChannel method")
|
// panic("mock out the SubscribeUserToChannel method")
|
||||||
// },
|
// },
|
||||||
|
// UnsubscribeUserFromChannelFunc: func(ctx context.Context, username string, channelID string) error {
|
||||||
|
// panic("mock out the UnsubscribeUserFromChannel method")
|
||||||
|
// },
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// // use mockedDB in code that requires db.DB
|
// // use mockedDB in code that requires db.DB
|
||||||
|
@ -100,6 +103,9 @@ type DBMock struct {
|
||||||
// SubscribeUserToChannelFunc mocks the SubscribeUserToChannel method.
|
// SubscribeUserToChannelFunc mocks the SubscribeUserToChannel method.
|
||||||
SubscribeUserToChannelFunc func(ctx context.Context, username string, channelID string) error
|
SubscribeUserToChannelFunc func(ctx context.Context, username string, channelID string) error
|
||||||
|
|
||||||
|
// UnsubscribeUserFromChannelFunc mocks the UnsubscribeUserFromChannel method.
|
||||||
|
UnsubscribeUserFromChannelFunc func(ctx context.Context, username string, channelID string) error
|
||||||
|
|
||||||
// calls tracks calls to the methods.
|
// calls tracks calls to the methods.
|
||||||
calls struct {
|
calls struct {
|
||||||
// AddVideo holds details about calls to the AddVideo method.
|
// AddVideo holds details about calls to the AddVideo method.
|
||||||
|
@ -194,19 +200,29 @@ type DBMock struct {
|
||||||
// ChannelID is the channelID argument value.
|
// ChannelID is the channelID argument value.
|
||||||
ChannelID string
|
ChannelID string
|
||||||
}
|
}
|
||||||
|
// UnsubscribeUserFromChannel holds details about calls to the UnsubscribeUserFromChannel method.
|
||||||
|
UnsubscribeUserFromChannel []struct {
|
||||||
|
// Ctx is the ctx argument value.
|
||||||
|
Ctx context.Context
|
||||||
|
// Username is the username argument value.
|
||||||
|
Username string
|
||||||
|
// ChannelID is the channelID argument value.
|
||||||
|
ChannelID string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lockAddVideo sync.RWMutex
|
lockAddVideo sync.RWMutex
|
||||||
lockAddVideoToUser sync.RWMutex
|
lockAddVideoToUser sync.RWMutex
|
||||||
lockAuthenticateUser sync.RWMutex
|
lockAuthenticateUser sync.RWMutex
|
||||||
lockCreateChannel sync.RWMutex
|
lockCreateChannel sync.RWMutex
|
||||||
lockCreateUser sync.RWMutex
|
lockCreateUser sync.RWMutex
|
||||||
lockDeleteUser sync.RWMutex
|
lockDeleteUser sync.RWMutex
|
||||||
lockGetChannelSubscribers sync.RWMutex
|
lockGetChannelSubscribers sync.RWMutex
|
||||||
lockGetNewVideos sync.RWMutex
|
lockGetNewVideos sync.RWMutex
|
||||||
lockGetWatchedVideos sync.RWMutex
|
lockGetWatchedVideos sync.RWMutex
|
||||||
lockListChannels sync.RWMutex
|
lockListChannels sync.RWMutex
|
||||||
lockSetVideoWatchTime sync.RWMutex
|
lockSetVideoWatchTime sync.RWMutex
|
||||||
lockSubscribeUserToChannel sync.RWMutex
|
lockSubscribeUserToChannel sync.RWMutex
|
||||||
|
lockUnsubscribeUserFromChannel sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddVideo calls AddVideoFunc.
|
// AddVideo calls AddVideoFunc.
|
||||||
|
@ -656,3 +672,43 @@ func (mock *DBMock) SubscribeUserToChannelCalls() []struct {
|
||||||
mock.lockSubscribeUserToChannel.RUnlock()
|
mock.lockSubscribeUserToChannel.RUnlock()
|
||||||
return calls
|
return calls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnsubscribeUserFromChannel calls UnsubscribeUserFromChannelFunc.
|
||||||
|
func (mock *DBMock) UnsubscribeUserFromChannel(ctx context.Context, username string, channelID string) error {
|
||||||
|
if mock.UnsubscribeUserFromChannelFunc == nil {
|
||||||
|
panic("DBMock.UnsubscribeUserFromChannelFunc: method is nil but DB.UnsubscribeUserFromChannel was just called")
|
||||||
|
}
|
||||||
|
callInfo := struct {
|
||||||
|
Ctx context.Context
|
||||||
|
Username string
|
||||||
|
ChannelID string
|
||||||
|
}{
|
||||||
|
Ctx: ctx,
|
||||||
|
Username: username,
|
||||||
|
ChannelID: channelID,
|
||||||
|
}
|
||||||
|
mock.lockUnsubscribeUserFromChannel.Lock()
|
||||||
|
mock.calls.UnsubscribeUserFromChannel = append(mock.calls.UnsubscribeUserFromChannel, callInfo)
|
||||||
|
mock.lockUnsubscribeUserFromChannel.Unlock()
|
||||||
|
return mock.UnsubscribeUserFromChannelFunc(ctx, username, channelID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsubscribeUserFromChannelCalls gets all the calls that were made to UnsubscribeUserFromChannel.
|
||||||
|
// Check the length with:
|
||||||
|
//
|
||||||
|
// len(mockedDB.UnsubscribeUserFromChannelCalls())
|
||||||
|
func (mock *DBMock) UnsubscribeUserFromChannelCalls() []struct {
|
||||||
|
Ctx context.Context
|
||||||
|
Username string
|
||||||
|
ChannelID string
|
||||||
|
} {
|
||||||
|
var calls []struct {
|
||||||
|
Ctx context.Context
|
||||||
|
Username string
|
||||||
|
ChannelID string
|
||||||
|
}
|
||||||
|
mock.lockUnsubscribeUserFromChannel.RLock()
|
||||||
|
calls = mock.calls.UnsubscribeUserFromChannel
|
||||||
|
mock.lockUnsubscribeUserFromChannel.RUnlock()
|
||||||
|
return calls
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue