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
|
||||
}
|
||||
|
||||
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 (
|
||||
ErrChannelExists = errors.New("channel already exists")
|
||||
ErrChannelNotFound = errors.New("no channel with that ID found")
|
||||
ErrAlreadySubscribed = errors.New("already subscribed to channel")
|
||||
ErrVideoExists = errors.New("video already exists")
|
||||
ErrUserExists = errors.New("user already exists")
|
||||
|
@ -30,8 +31,10 @@ type DB interface {
|
|||
ListChannels(ctx context.Context) ([]models.Channel, error)
|
||||
// GetChannelSubscribers lists all channels from the database
|
||||
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 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(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)
|
||||
}
|
||||
|
||||
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 {
|
||||
CreateUser(ctx context.Context, user models.User) 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)
|
||||
GetWatchedVideos(ctx context.Context, username string) ([]models.Video, 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"})
|
||||
}
|
||||
|
||||
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.POST("channels/:channel_id/subscribe", srv.SubscribeToChannel)
|
||||
api.POST("channels/:channel_id/unsubscribe", srv.UnsubscribeFromChannel)
|
||||
api.GET("videos/new", srv.GetNewVideos)
|
||||
api.GET("videos/watched", srv.GetWatchedVideos)
|
||||
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 {
|
||||
// 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
|
||||
|
@ -100,6 +103,9 @@ type DBMock struct {
|
|||
// SubscribeUserToChannelFunc mocks the SubscribeUserToChannel method.
|
||||
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 struct {
|
||||
// AddVideo holds details about calls to the AddVideo method.
|
||||
|
@ -194,19 +200,29 @@ type DBMock struct {
|
|||
// ChannelID is the channelID argument value.
|
||||
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
|
||||
lockAddVideoToUser sync.RWMutex
|
||||
lockAuthenticateUser sync.RWMutex
|
||||
lockCreateChannel sync.RWMutex
|
||||
lockCreateUser sync.RWMutex
|
||||
lockDeleteUser sync.RWMutex
|
||||
lockGetChannelSubscribers sync.RWMutex
|
||||
lockGetNewVideos sync.RWMutex
|
||||
lockGetWatchedVideos sync.RWMutex
|
||||
lockListChannels sync.RWMutex
|
||||
lockSetVideoWatchTime sync.RWMutex
|
||||
lockSubscribeUserToChannel sync.RWMutex
|
||||
lockAddVideo sync.RWMutex
|
||||
lockAddVideoToUser sync.RWMutex
|
||||
lockAuthenticateUser sync.RWMutex
|
||||
lockCreateChannel sync.RWMutex
|
||||
lockCreateUser sync.RWMutex
|
||||
lockDeleteUser sync.RWMutex
|
||||
lockGetChannelSubscribers sync.RWMutex
|
||||
lockGetNewVideos sync.RWMutex
|
||||
lockGetWatchedVideos sync.RWMutex
|
||||
lockListChannels sync.RWMutex
|
||||
lockSetVideoWatchTime sync.RWMutex
|
||||
lockSubscribeUserToChannel sync.RWMutex
|
||||
lockUnsubscribeUserFromChannel sync.RWMutex
|
||||
}
|
||||
|
||||
// AddVideo calls AddVideoFunc.
|
||||
|
@ -656,3 +672,43 @@ func (mock *DBMock) SubscribeUserToChannelCalls() []struct {
|
|||
mock.lockSubscribeUserToChannel.RUnlock()
|
||||
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