From 2abcbe32d9b454e2223c646d93a0ac29b4ade217 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Wed, 1 Nov 2017 18:21:06 -0700 Subject: [PATCH 1/9] Do not fail if event is already deleted --- pkg/webhook/hook.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/webhook/hook.go b/pkg/webhook/hook.go index d500d46..4f13512 100644 --- a/pkg/webhook/hook.go +++ b/pkg/webhook/hook.go @@ -68,7 +68,12 @@ func (h Handler) Handle(pledge *patreon.Pledge, event string) error { case patreon.EventUpdatePledge: return h.db.Update(model) case patreon.EventDeletePledge: - return h.db.Delete(model) + err := h.db.Delete(model) + if err == pg.ErrNoRows { + return nil + } + + return err default: return fmt.Errorf("unknown event: %s", event) } From 0595068feba1160fafcd3a30a5c05d9c0c2489bb Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Thu, 2 Nov 2017 17:13:01 -0700 Subject: [PATCH 2/9] Add pledge query --- pkg/models/pg.sql | 2 +- pkg/webhook/hook.go | 5 +++++ pkg/webhook/hook_test.go | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/pkg/models/pg.sql b/pkg/models/pg.sql index f05d0cd..f6f67b0 100644 --- a/pkg/models/pg.sql +++ b/pkg/models/pg.sql @@ -2,7 +2,7 @@ BEGIN; CREATE TABLE IF NOT EXISTS pledges ( pledge_id BIGSERIAL PRIMARY KEY, - patron_id BIGINT NOT NULL , + patron_id BIGINT NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL, declined_since TIMESTAMPTZ NULL, amount_cents INT NOT NULL, diff --git a/pkg/webhook/hook.go b/pkg/webhook/hook.go index 4f13512..6c3e04b 100644 --- a/pkg/webhook/hook.go +++ b/pkg/webhook/hook.go @@ -79,6 +79,11 @@ func (h Handler) Handle(pledge *patreon.Pledge, event string) error { } } +func (h Handler) FindPledge(patronID string) (*models.Pledge, error) { + p := &models.Pledge{} + return p, h.db.Model(p).Where("pledge_id = ?", patronID).Limit(1).Select() +} + func NewHookHandler(db *pg.DB) *Handler { return &Handler{db: db} } diff --git a/pkg/webhook/hook_test.go b/pkg/webhook/hook_test.go index cb05e79..5c3905e 100644 --- a/pkg/webhook/hook_test.go +++ b/pkg/webhook/hook_test.go @@ -52,6 +52,18 @@ func TestDelete(t *testing.T) { require.NoError(t, err) } +func TestFindPledge(t *testing.T) { + pledge := createPledge() + hook := createHandler(t) + + err := hook.Handle(pledge, patreon.EventCreatePledge) + require.NoError(t, err) + + res, err := hook.FindPledge(pledge.ID) + require.NoError(t, err) + require.Equal(t, res.AmountCents, pledge.Attributes.AmountCents) +} + func createHandler(t *testing.T) *Handler { opts, err := pg.ParseURL("postgres://postgres:@localhost/podsync?sslmode=disable") if err != nil { From b96f484bae0c1a61c9d20a01dfb9242a6557201e Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Thu, 2 Nov 2017 17:28:03 -0700 Subject: [PATCH 3/9] Detect feature level --- pkg/handler/handler.go | 19 ++++++++++--------- pkg/webhook/hook.go | 2 +- pkg/webhook/hook_test.go | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index cd61c8c..5a4f930 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -95,17 +95,18 @@ func (h handler) patreonCallback(c *gin.Context) { if user.Data.ID == creatorID { level = api.PodcasterFeature } else { - amount := 0 - for _, item := range user.Included.Items { - pledge, ok := item.(*patreon.Pledge) - if ok { - amount += pledge.Attributes.AmountCents + pledge, err := h.hook.FindPledge(user.Data.ID) + if err != nil { + log.Printf("! can't find pledge for user %s: %v", user.Data.ID, err) + } else { + // Check pledge is valid + if pledge.DeclinedSince.IsZero() && !pledge.IsPaused { + // Check the amount of pledge + if pledge.AmountCents >= 100 { + level = api.ExtendedFeatures + } } } - - if amount >= 100 { - level = api.ExtendedFeatures - } } identity := &api.Identity{ diff --git a/pkg/webhook/hook.go b/pkg/webhook/hook.go index 6c3e04b..e459e3c 100644 --- a/pkg/webhook/hook.go +++ b/pkg/webhook/hook.go @@ -81,7 +81,7 @@ func (h Handler) Handle(pledge *patreon.Pledge, event string) error { func (h Handler) FindPledge(patronID string) (*models.Pledge, error) { p := &models.Pledge{} - return p, h.db.Model(p).Where("pledge_id = ?", patronID).Limit(1).Select() + return p, h.db.Model(p).Where("patron_id = ?", patronID).Limit(1).Select() } func NewHookHandler(db *pg.DB) *Handler { diff --git a/pkg/webhook/hook_test.go b/pkg/webhook/hook_test.go index 5c3905e..7a2fa2c 100644 --- a/pkg/webhook/hook_test.go +++ b/pkg/webhook/hook_test.go @@ -59,7 +59,7 @@ func TestFindPledge(t *testing.T) { err := hook.Handle(pledge, patreon.EventCreatePledge) require.NoError(t, err) - res, err := hook.FindPledge(pledge.ID) + res, err := hook.FindPledge("67890") require.NoError(t, err) require.Equal(t, res.AmountCents, pledge.Attributes.AmountCents) } From d865ad7fb8b67e1ecb961015d2d0efe3564eae3d Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Thu, 2 Nov 2017 17:43:08 -0700 Subject: [PATCH 4/9] Rename package webhook to patreon --- pkg/handler/handler.go | 38 ++++----------- pkg/{webhook/hook.go => support/patreon.go} | 46 ++++++++++++++++--- .../hook_test.go => support/patreon_test.go} | 31 +++++++++---- 3 files changed, 71 insertions(+), 44 deletions(-) rename pkg/{webhook/hook.go => support/patreon.go} (67%) rename pkg/{webhook/hook_test.go => support/patreon_test.go} (67%) diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 5a4f930..117a956 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -16,12 +16,11 @@ import ( "github.com/mxpv/podsync/pkg/api" "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/session" - "github.com/mxpv/podsync/pkg/webhook" + "github.com/mxpv/podsync/pkg/support" "golang.org/x/oauth2" ) const ( - creatorID = "2822191" maxHashIDLength = 16 ) @@ -32,10 +31,10 @@ type feed interface { } type handler struct { - feed feed - cfg *config.AppConfig - oauth2 oauth2.Config - hook *webhook.Handler + feed feed + cfg *config.AppConfig + oauth2 oauth2.Config + patreon *support.Patreon } func (h handler) index(c *gin.Context) { @@ -90,24 +89,7 @@ func (h handler) patreonCallback(c *gin.Context) { } // Determine feature level - level := api.DefaultFeatures - - if user.Data.ID == creatorID { - level = api.PodcasterFeature - } else { - pledge, err := h.hook.FindPledge(user.Data.ID) - if err != nil { - log.Printf("! can't find pledge for user %s: %v", user.Data.ID, err) - } else { - // Check pledge is valid - if pledge.DeclinedSince.IsZero() && !pledge.IsPaused { - // Check the amount of pledge - if pledge.AmountCents >= 100 { - level = api.ExtendedFeatures - } - } - } - } + level := h.patreon.GetFeatureLevel(user.Data.ID) identity := &api.Identity{ UserId: user.Data.ID, @@ -236,7 +218,7 @@ func (h handler) webhook(c *gin.Context) { return } - if err := h.hook.Handle(&pledge.Data, eventName); err != nil { + if err := h.patreon.Hook(&pledge.Data, eventName); err != nil { log.Printf("failed to process patreon event %s (%s): %v", pledge.Data.ID, eventName, err) c.JSON(internalError(err)) return @@ -265,9 +247,9 @@ func New(feed feed, db *pg.DB, cfg *config.AppConfig) http.Handler { } h := handler{ - feed: feed, - cfg: cfg, - hook: webhook.NewHookHandler(db), + feed: feed, + cfg: cfg, + patreon: support.NewPatreon(db), } // OAuth 2 configuration diff --git a/pkg/webhook/hook.go b/pkg/support/patreon.go similarity index 67% rename from pkg/webhook/hook.go rename to pkg/support/patreon.go index e459e3c..75e155a 100644 --- a/pkg/webhook/hook.go +++ b/pkg/support/patreon.go @@ -1,20 +1,26 @@ -package webhook +package support import ( "fmt" + "log" "strconv" "github.com/go-pg/pg" "github.com/mxpv/patreon-go" + "github.com/mxpv/podsync/pkg/api" "github.com/mxpv/podsync/pkg/models" "github.com/pkg/errors" ) -type Handler struct { +const ( + creatorID = "2822191" +) + +type Patreon struct { db *pg.DB } -func (h Handler) toModel(pledge *patreon.Pledge) (*models.Pledge, error) { +func (h Patreon) toModel(pledge *patreon.Pledge) (*models.Pledge, error) { pledgeID, err := strconv.ParseInt(pledge.ID, 10, 64) if err != nil { return nil, errors.Wrapf(err, "failed to parse pledge id: %s", pledge.ID) @@ -56,7 +62,7 @@ func (h Handler) toModel(pledge *patreon.Pledge) (*models.Pledge, error) { return model, nil } -func (h Handler) Handle(pledge *patreon.Pledge, event string) error { +func (h Patreon) Hook(pledge *patreon.Pledge, event string) error { model, err := h.toModel(pledge) if err != nil { return err @@ -79,11 +85,37 @@ func (h Handler) Handle(pledge *patreon.Pledge, event string) error { } } -func (h Handler) FindPledge(patronID string) (*models.Pledge, error) { +func (h Patreon) FindPledge(patronID string) (*models.Pledge, error) { p := &models.Pledge{} return p, h.db.Model(p).Where("patron_id = ?", patronID).Limit(1).Select() } -func NewHookHandler(db *pg.DB) *Handler { - return &Handler{db: db} +func (h Patreon) GetFeatureLevel(patronID string) (level int) { + level = api.DefaultFeatures + + if patronID == creatorID { + level = api.PodcasterFeature + return + } + + pledge, err := h.FindPledge(patronID) + if err != nil { + log.Printf("! can't find pledge for user %s: %v", patronID, err) + return + } + + // Check pledge is valid + if pledge.DeclinedSince.IsZero() && !pledge.IsPaused { + // Check the amount of pledge + if pledge.AmountCents >= 100 { + level = api.ExtendedFeatures + return + } + } + + return +} + +func NewPatreon(db *pg.DB) *Patreon { + return &Patreon{db: db} } diff --git a/pkg/webhook/hook_test.go b/pkg/support/patreon_test.go similarity index 67% rename from pkg/webhook/hook_test.go rename to pkg/support/patreon_test.go index 7a2fa2c..bd48233 100644 --- a/pkg/webhook/hook_test.go +++ b/pkg/support/patreon_test.go @@ -1,4 +1,4 @@ -package webhook +package support import ( "testing" @@ -6,6 +6,7 @@ import ( "github.com/go-pg/pg" "github.com/mxpv/patreon-go" + "github.com/mxpv/podsync/pkg/api" "github.com/mxpv/podsync/pkg/models" "github.com/stretchr/testify/require" ) @@ -14,7 +15,7 @@ func TestCreate(t *testing.T) { pledge := createPledge() hook := createHandler(t) - err := hook.Handle(pledge, patreon.EventCreatePledge) + err := hook.Hook(pledge, patreon.EventCreatePledge) require.NoError(t, err) model := &models.Pledge{PledgeID: 12345} @@ -27,12 +28,12 @@ func TestUpdate(t *testing.T) { pledge := createPledge() hook := createHandler(t) - err := hook.Handle(pledge, patreon.EventCreatePledge) + err := hook.Hook(pledge, patreon.EventCreatePledge) require.NoError(t, err) pledge.Attributes.AmountCents = 999 - err = hook.Handle(pledge, patreon.EventUpdatePledge) + err = hook.Hook(pledge, patreon.EventUpdatePledge) require.NoError(t, err) model := &models.Pledge{PledgeID: 12345} @@ -45,10 +46,10 @@ func TestDelete(t *testing.T) { pledge := createPledge() hook := createHandler(t) - err := hook.Handle(pledge, patreon.EventCreatePledge) + err := hook.Hook(pledge, patreon.EventCreatePledge) require.NoError(t, err) - err = hook.Handle(pledge, patreon.EventDeletePledge) + err = hook.Hook(pledge, patreon.EventDeletePledge) require.NoError(t, err) } @@ -56,7 +57,7 @@ func TestFindPledge(t *testing.T) { pledge := createPledge() hook := createHandler(t) - err := hook.Handle(pledge, patreon.EventCreatePledge) + err := hook.Hook(pledge, patreon.EventCreatePledge) require.NoError(t, err) res, err := hook.FindPledge("67890") @@ -64,7 +65,19 @@ func TestFindPledge(t *testing.T) { require.Equal(t, res.AmountCents, pledge.Attributes.AmountCents) } -func createHandler(t *testing.T) *Handler { +func TestGetFeatureLevel(t *testing.T) { + pledge := createPledge() + hook := createHandler(t) + + err := hook.Hook(pledge, patreon.EventCreatePledge) + require.NoError(t, err) + + require.Equal(t, api.PodcasterFeature, hook.GetFeatureLevel(creatorID)) + require.Equal(t, api.DefaultFeatures, hook.GetFeatureLevel("xyz")) + require.Equal(t, api.ExtendedFeatures, hook.GetFeatureLevel(pledge.Relationships.Patron.Data.ID)) +} + +func createHandler(t *testing.T) *Patreon { opts, err := pg.ParseURL("postgres://postgres:@localhost/podsync?sslmode=disable") if err != nil { require.NoError(t, err) @@ -75,7 +88,7 @@ func createHandler(t *testing.T) *Handler { _, err = db.Model(&models.Pledge{}).Where("1=1").Delete() require.NoError(t, err) - return NewHookHandler(db) + return NewPatreon(db) } func createPledge() *patreon.Pledge { From 14307ddd800cf1cb34eb4115b60e2496115f43b1 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Thu, 2 Nov 2017 17:46:34 -0700 Subject: [PATCH 5/9] Check feature level when creating a new feed --- pkg/handler/handler.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 117a956..b22e25f 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -128,6 +128,9 @@ func (h handler) create(c *gin.Context) { return } + // Check feature level again if user deleted pledge by still logged in + identity.FeatureLevel = h.patreon.GetFeatureLevel(identity.UserId) + hashId, err := h.feed.CreateFeed(req, identity) if err != nil { c.JSON(internalError(err)) From e403bd5c20f0409467b5fd1b4fb5821854c961ea Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Thu, 2 Nov 2017 18:01:35 -0700 Subject: [PATCH 6/9] Refactor dependencies, fix unit test --- cmd/app/main.go | 7 ++- pkg/handler/handler.go | 20 +++++--- pkg/handler/handler_mock_test.go | 87 ++++++++++++++++++++++++-------- pkg/handler/handler_test.go | 13 +++-- 4 files changed, 94 insertions(+), 33 deletions(-) diff --git a/cmd/app/main.go b/cmd/app/main.go index 0441eb1..ec0b2f7 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -12,6 +12,7 @@ import ( "syscall" "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy" + "github.com/coreos/etcd/pkg/srv" "github.com/go-pg/pg" "github.com/mxpv/podsync/pkg/api" "github.com/mxpv/podsync/pkg/builders" @@ -20,6 +21,7 @@ import ( "github.com/mxpv/podsync/pkg/handler" "github.com/mxpv/podsync/pkg/id" "github.com/mxpv/podsync/pkg/storage" + "github.com/mxpv/podsync/pkg/support" "github.com/pkg/errors" ) @@ -71,9 +73,11 @@ func main() { feeds.WithBuilder(api.Vimeo, vimeo), ) + patreon := support.NewPatreon(pg) + srv := http.Server{ Addr: fmt.Sprintf(":%d", 5001), - Handler: handler.New(feed, pg, cfg), + Handler: handler.New(feed, patreon, cfg), } go func() { @@ -88,6 +92,7 @@ func main() { log.Printf("shutting down server") srv.Shutdown(ctx) + pg.Close() log.Printf("server gracefully stopped") } diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index b22e25f..b5b3b36 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -10,13 +10,11 @@ import ( "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" - "github.com/go-pg/pg" "github.com/mxpv/patreon-go" itunes "github.com/mxpv/podcast" "github.com/mxpv/podsync/pkg/api" "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/session" - "github.com/mxpv/podsync/pkg/support" "golang.org/x/oauth2" ) @@ -24,17 +22,25 @@ const ( maxHashIDLength = 16 ) -type feed interface { +type feedService interface { CreateFeed(req *api.CreateFeedRequest, identity *api.Identity) (string, error) GetFeed(hashId string) (*itunes.Podcast, error) GetMetadata(hashId string) (*api.Feed, error) } +// HACK: mockgen fails to import patreon.Pledge type +type P *patreon.Pledge + +type patreonService interface { + Hook(pledge P, event string) error + GetFeatureLevel(patronID string) int +} + type handler struct { - feed feed + feed feedService cfg *config.AppConfig oauth2 oauth2.Config - patreon *support.Patreon + patreon patreonService } func (h handler) index(c *gin.Context) { @@ -230,7 +236,7 @@ func (h handler) webhook(c *gin.Context) { log.Printf("sucessfully processed patreon event %s (%s)", pledge.Data.ID, eventName) } -func New(feed feed, db *pg.DB, cfg *config.AppConfig) http.Handler { +func New(feed feedService, support patreonService, cfg *config.AppConfig) http.Handler { r := gin.New() r.Use(gin.Recovery()) @@ -251,8 +257,8 @@ func New(feed feed, db *pg.DB, cfg *config.AppConfig) http.Handler { h := handler{ feed: feed, + patreon: support, cfg: cfg, - patreon: support.NewPatreon(db), } // OAuth 2 configuration diff --git a/pkg/handler/handler_mock_test.go b/pkg/handler/handler_mock_test.go index 9c88046..ecfa7d1 100644 --- a/pkg/handler/handler_mock_test.go +++ b/pkg/handler/handler_mock_test.go @@ -10,31 +10,31 @@ import ( reflect "reflect" ) -// Mockfeed is a mock of feed interface -type Mockfeed struct { +// MockfeedService is a mock of feedService interface +type MockfeedService struct { ctrl *gomock.Controller - recorder *MockfeedMockRecorder + recorder *MockfeedServiceMockRecorder } -// MockfeedMockRecorder is the mock recorder for Mockfeed -type MockfeedMockRecorder struct { - mock *Mockfeed +// MockfeedServiceMockRecorder is the mock recorder for MockfeedService +type MockfeedServiceMockRecorder struct { + mock *MockfeedService } -// NewMockfeed creates a new mock instance -func NewMockfeed(ctrl *gomock.Controller) *Mockfeed { - mock := &Mockfeed{ctrl: ctrl} - mock.recorder = &MockfeedMockRecorder{mock} +// NewMockfeedService creates a new mock instance +func NewMockfeedService(ctrl *gomock.Controller) *MockfeedService { + mock := &MockfeedService{ctrl: ctrl} + mock.recorder = &MockfeedServiceMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *Mockfeed) EXPECT() *MockfeedMockRecorder { +func (_m *MockfeedService) EXPECT() *MockfeedServiceMockRecorder { return _m.recorder } // CreateFeed mocks base method -func (_m *Mockfeed) CreateFeed(req *api.CreateFeedRequest, identity *api.Identity) (string, error) { +func (_m *MockfeedService) CreateFeed(req *api.CreateFeedRequest, identity *api.Identity) (string, error) { ret := _m.ctrl.Call(_m, "CreateFeed", req, identity) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) @@ -42,12 +42,12 @@ func (_m *Mockfeed) CreateFeed(req *api.CreateFeedRequest, identity *api.Identit } // CreateFeed indicates an expected call of CreateFeed -func (_mr *MockfeedMockRecorder) CreateFeed(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "CreateFeed", reflect.TypeOf((*Mockfeed)(nil).CreateFeed), arg0, arg1) +func (_mr *MockfeedServiceMockRecorder) CreateFeed(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "CreateFeed", reflect.TypeOf((*MockfeedService)(nil).CreateFeed), arg0, arg1) } // GetFeed mocks base method -func (_m *Mockfeed) GetFeed(hashId string) (*podcast.Podcast, error) { +func (_m *MockfeedService) GetFeed(hashId string) (*podcast.Podcast, error) { ret := _m.ctrl.Call(_m, "GetFeed", hashId) ret0, _ := ret[0].(*podcast.Podcast) ret1, _ := ret[1].(error) @@ -55,12 +55,12 @@ func (_m *Mockfeed) GetFeed(hashId string) (*podcast.Podcast, error) { } // GetFeed indicates an expected call of GetFeed -func (_mr *MockfeedMockRecorder) GetFeed(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GetFeed", reflect.TypeOf((*Mockfeed)(nil).GetFeed), arg0) +func (_mr *MockfeedServiceMockRecorder) GetFeed(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GetFeed", reflect.TypeOf((*MockfeedService)(nil).GetFeed), arg0) } // GetMetadata mocks base method -func (_m *Mockfeed) GetMetadata(hashId string) (*api.Feed, error) { +func (_m *MockfeedService) GetMetadata(hashId string) (*api.Feed, error) { ret := _m.ctrl.Call(_m, "GetMetadata", hashId) ret0, _ := ret[0].(*api.Feed) ret1, _ := ret[1].(error) @@ -68,6 +68,53 @@ func (_m *Mockfeed) GetMetadata(hashId string) (*api.Feed, error) { } // GetMetadata indicates an expected call of GetMetadata -func (_mr *MockfeedMockRecorder) GetMetadata(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GetMetadata", reflect.TypeOf((*Mockfeed)(nil).GetMetadata), arg0) +func (_mr *MockfeedServiceMockRecorder) GetMetadata(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GetMetadata", reflect.TypeOf((*MockfeedService)(nil).GetMetadata), arg0) +} + +// MockpatreonService is a mock of patreonService interface +type MockpatreonService struct { + ctrl *gomock.Controller + recorder *MockpatreonServiceMockRecorder +} + +// MockpatreonServiceMockRecorder is the mock recorder for MockpatreonService +type MockpatreonServiceMockRecorder struct { + mock *MockpatreonService +} + +// NewMockpatreonService creates a new mock instance +func NewMockpatreonService(ctrl *gomock.Controller) *MockpatreonService { + mock := &MockpatreonService{ctrl: ctrl} + mock.recorder = &MockpatreonServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (_m *MockpatreonService) EXPECT() *MockpatreonServiceMockRecorder { + return _m.recorder +} + +// Hook mocks base method +func (_m *MockpatreonService) Hook(pledge P, event string) error { + ret := _m.ctrl.Call(_m, "Hook", pledge, event) + ret0, _ := ret[0].(error) + return ret0 +} + +// Hook indicates an expected call of Hook +func (_mr *MockpatreonServiceMockRecorder) Hook(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Hook", reflect.TypeOf((*MockpatreonService)(nil).Hook), arg0, arg1) +} + +// GetFeatureLevel mocks base method +func (_m *MockpatreonService) GetFeatureLevel(patronID string) int { + ret := _m.ctrl.Call(_m, "GetFeatureLevel", patronID) + ret0, _ := ret[0].(int) + return ret0 +} + +// GetFeatureLevel indicates an expected call of GetFeatureLevel +func (_mr *MockpatreonServiceMockRecorder) GetFeatureLevel(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GetFeatureLevel", reflect.TypeOf((*MockpatreonService)(nil).GetFeatureLevel), arg0) } diff --git a/pkg/handler/handler_test.go b/pkg/handler/handler_test.go index b0e04a3..0a1f516 100644 --- a/pkg/handler/handler_test.go +++ b/pkg/handler/handler_test.go @@ -29,10 +29,13 @@ func TestCreateFeed(t *testing.T) { Format: api.AudioFormat, } - feed := NewMockfeed(ctrl) + feed := NewMockfeedService(ctrl) feed.EXPECT().CreateFeed(gomock.Eq(req), gomock.Any()).Times(1).Return("456", nil) - srv := httptest.NewServer(New(feed, nil, cfg)) + patreon := NewMockpatreonService(ctrl) + patreon.EXPECT().GetFeatureLevel(gomock.Any()).Return(api.DefaultFeatures) + + srv := httptest.NewServer(New(feed, patreon, cfg)) defer srv.Close() query := `{"url": "https://youtube.com/channel/123", "page_size": 55, "quality": "low", "format": "audio"}` @@ -47,7 +50,7 @@ func TestCreateInvalidFeed(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - srv := httptest.NewServer(New(NewMockfeed(ctrl), nil, cfg)) + srv := httptest.NewServer(New(NewMockfeedService(ctrl), nil, cfg)) defer srv.Close() query := `{}` @@ -97,7 +100,7 @@ func TestGetFeed(t *testing.T) { podcast := itunes.New("", "", "", nil, nil) - feed := NewMockfeed(ctrl) + feed := NewMockfeedService(ctrl) feed.EXPECT().GetFeed("123").Return(&podcast, nil) srv := httptest.NewServer(New(feed, nil, cfg)) @@ -112,7 +115,7 @@ func TestGetMetadata(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - feed := NewMockfeed(ctrl) + feed := NewMockfeedService(ctrl) feed.EXPECT().GetMetadata("123").Times(1).Return(&api.Feed{}, nil) srv := httptest.NewServer(New(feed, nil, cfg)) From 3c43f84cc0c1ee5a1dfa9585f5da554b227192fd Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Thu, 2 Nov 2017 18:03:44 -0700 Subject: [PATCH 7/9] Refactor feeds package --- pkg/feeds/feeds.go | 21 ++++-- ...rfaces_mock_test.go => feeds_mock_test.go} | 64 +++++++++---------- pkg/feeds/feeds_test.go | 10 +-- pkg/feeds/interfaces.go | 19 ------ 4 files changed, 54 insertions(+), 60 deletions(-) rename pkg/feeds/{interfaces_mock_test.go => feeds_mock_test.go} (57%) delete mode 100644 pkg/feeds/interfaces.go diff --git a/pkg/feeds/feeds.go b/pkg/feeds/feeds.go index da070df..f564a29 100644 --- a/pkg/feeds/feeds.go +++ b/pkg/feeds/feeds.go @@ -13,9 +13,22 @@ const ( maxPageSize = 150 ) +type idService interface { + Generate(feed *api.Feed) (string, error) +} + +type storageService interface { + CreateFeed(feed *api.Feed) error + GetFeed(hashId string) (*api.Feed, error) +} + +type builder interface { + Build(feed *api.Feed) (podcast *itunes.Podcast, err error) +} + type service struct { - id id - storage storage + id idService + storage storageService builders map[api.Provider]builder } @@ -85,13 +98,13 @@ func (s *service) GetMetadata(hashId string) (*api.Feed, error) { type feedOption func(*service) -func WithStorage(storage storage) feedOption { +func WithStorage(storage storageService) feedOption { return func(service *service) { service.storage = storage } } -func WithIdGen(id id) feedOption { +func WithIdGen(id idService) feedOption { return func(service *service) { service.id = id } diff --git a/pkg/feeds/interfaces_mock_test.go b/pkg/feeds/feeds_mock_test.go similarity index 57% rename from pkg/feeds/interfaces_mock_test.go rename to pkg/feeds/feeds_mock_test.go index a76822c..cc1cdcc 100644 --- a/pkg/feeds/interfaces_mock_test.go +++ b/pkg/feeds/feeds_mock_test.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: interfaces.go +// Source: feeds.go package feeds @@ -10,31 +10,31 @@ import ( reflect "reflect" ) -// Mockid is a mock of id interface -type Mockid struct { +// MockidService is a mock of idService interface +type MockidService struct { ctrl *gomock.Controller - recorder *MockidMockRecorder + recorder *MockidServiceMockRecorder } -// MockidMockRecorder is the mock recorder for Mockid -type MockidMockRecorder struct { - mock *Mockid +// MockidServiceMockRecorder is the mock recorder for MockidService +type MockidServiceMockRecorder struct { + mock *MockidService } -// NewMockid creates a new mock instance -func NewMockid(ctrl *gomock.Controller) *Mockid { - mock := &Mockid{ctrl: ctrl} - mock.recorder = &MockidMockRecorder{mock} +// NewMockidService creates a new mock instance +func NewMockidService(ctrl *gomock.Controller) *MockidService { + mock := &MockidService{ctrl: ctrl} + mock.recorder = &MockidServiceMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *Mockid) EXPECT() *MockidMockRecorder { +func (_m *MockidService) EXPECT() *MockidServiceMockRecorder { return _m.recorder } // Generate mocks base method -func (_m *Mockid) Generate(feed *api.Feed) (string, error) { +func (_m *MockidService) Generate(feed *api.Feed) (string, error) { ret := _m.ctrl.Call(_m, "Generate", feed) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) @@ -42,47 +42,47 @@ func (_m *Mockid) Generate(feed *api.Feed) (string, error) { } // Generate indicates an expected call of Generate -func (_mr *MockidMockRecorder) Generate(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Generate", reflect.TypeOf((*Mockid)(nil).Generate), arg0) +func (_mr *MockidServiceMockRecorder) Generate(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Generate", reflect.TypeOf((*MockidService)(nil).Generate), arg0) } -// Mockstorage is a mock of storage interface -type Mockstorage struct { +// MockstorageService is a mock of storageService interface +type MockstorageService struct { ctrl *gomock.Controller - recorder *MockstorageMockRecorder + recorder *MockstorageServiceMockRecorder } -// MockstorageMockRecorder is the mock recorder for Mockstorage -type MockstorageMockRecorder struct { - mock *Mockstorage +// MockstorageServiceMockRecorder is the mock recorder for MockstorageService +type MockstorageServiceMockRecorder struct { + mock *MockstorageService } -// NewMockstorage creates a new mock instance -func NewMockstorage(ctrl *gomock.Controller) *Mockstorage { - mock := &Mockstorage{ctrl: ctrl} - mock.recorder = &MockstorageMockRecorder{mock} +// NewMockstorageService creates a new mock instance +func NewMockstorageService(ctrl *gomock.Controller) *MockstorageService { + mock := &MockstorageService{ctrl: ctrl} + mock.recorder = &MockstorageServiceMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *Mockstorage) EXPECT() *MockstorageMockRecorder { +func (_m *MockstorageService) EXPECT() *MockstorageServiceMockRecorder { return _m.recorder } // CreateFeed mocks base method -func (_m *Mockstorage) CreateFeed(feed *api.Feed) error { +func (_m *MockstorageService) CreateFeed(feed *api.Feed) error { ret := _m.ctrl.Call(_m, "CreateFeed", feed) ret0, _ := ret[0].(error) return ret0 } // CreateFeed indicates an expected call of CreateFeed -func (_mr *MockstorageMockRecorder) CreateFeed(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "CreateFeed", reflect.TypeOf((*Mockstorage)(nil).CreateFeed), arg0) +func (_mr *MockstorageServiceMockRecorder) CreateFeed(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "CreateFeed", reflect.TypeOf((*MockstorageService)(nil).CreateFeed), arg0) } // GetFeed mocks base method -func (_m *Mockstorage) GetFeed(hashId string) (*api.Feed, error) { +func (_m *MockstorageService) GetFeed(hashId string) (*api.Feed, error) { ret := _m.ctrl.Call(_m, "GetFeed", hashId) ret0, _ := ret[0].(*api.Feed) ret1, _ := ret[1].(error) @@ -90,8 +90,8 @@ func (_m *Mockstorage) GetFeed(hashId string) (*api.Feed, error) { } // GetFeed indicates an expected call of GetFeed -func (_mr *MockstorageMockRecorder) GetFeed(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GetFeed", reflect.TypeOf((*Mockstorage)(nil).GetFeed), arg0) +func (_mr *MockstorageServiceMockRecorder) GetFeed(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GetFeed", reflect.TypeOf((*MockstorageService)(nil).GetFeed), arg0) } // Mockbuilder is a mock of builder interface diff --git a/pkg/feeds/feeds_test.go b/pkg/feeds/feeds_test.go index 7d81baa..b585847 100644 --- a/pkg/feeds/feeds_test.go +++ b/pkg/feeds/feeds_test.go @@ -1,4 +1,4 @@ -//go:generate mockgen -source=interfaces.go -destination=interfaces_mock_test.go -package=feeds +//go:generate mockgen -source=feeds.go -destination=feeds_mock_test.go -package=feeds package feeds @@ -14,10 +14,10 @@ func TestService_CreateFeed(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - id := NewMockid(ctrl) + id := NewMockidService(ctrl) id.EXPECT().Generate(gomock.Any()).Times(1).Return("123", nil) - storage := NewMockstorage(ctrl) + storage := NewMockstorageService(ctrl) storage.EXPECT().CreateFeed(gomock.Any()).Times(1).Return(nil) s := service{ @@ -44,7 +44,7 @@ func TestService_GetFeed(t *testing.T) { feed := &api.Feed{Provider: api.Youtube} - storage := NewMockstorage(ctrl) + storage := NewMockstorageService(ctrl) storage.EXPECT().GetFeed("123").Times(1).Return(feed, nil) bld := NewMockbuilder(ctrl) @@ -63,7 +63,7 @@ func TestService_GetMetadata(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - storage := NewMockstorage(ctrl) + storage := NewMockstorageService(ctrl) storage.EXPECT().GetFeed("123").Times(1).Return(&api.Feed{}, nil) s := service{storage: storage} diff --git a/pkg/feeds/interfaces.go b/pkg/feeds/interfaces.go deleted file mode 100644 index f85355e..0000000 --- a/pkg/feeds/interfaces.go +++ /dev/null @@ -1,19 +0,0 @@ -package feeds - -import ( - itunes "github.com/mxpv/podcast" - "github.com/mxpv/podsync/pkg/api" -) - -type id interface { - Generate(feed *api.Feed) (string, error) -} - -type storage interface { - CreateFeed(feed *api.Feed) error - GetFeed(hashId string) (*api.Feed, error) -} - -type builder interface { - Build(feed *api.Feed) (podcast *itunes.Podcast, err error) -} From aaa96f0f042f2b3a3ebc66a814d77507544f74f0 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Thu, 2 Nov 2017 18:19:21 -0700 Subject: [PATCH 8/9] Code cleanup --- cmd/app/main.go | 9 ++++----- pkg/handler/handler.go | 5 +---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/cmd/app/main.go b/cmd/app/main.go index ec0b2f7..0f19e8a 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -12,7 +12,6 @@ import ( "syscall" "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy" - "github.com/coreos/etcd/pkg/srv" "github.com/go-pg/pg" "github.com/mxpv/podsync/pkg/api" "github.com/mxpv/podsync/pkg/builders" @@ -49,11 +48,13 @@ func main() { panic(err) } - pg, err := createPg(cfg.PostgresConnectionURL) + database, err := createPg(cfg.PostgresConnectionURL) if err != nil { panic(err) } + patreon := support.NewPatreon(database) + // Builders youtube, err := builders.NewYouTubeBuilder(cfg.YouTubeApiKey) @@ -73,8 +74,6 @@ func main() { feeds.WithBuilder(api.Vimeo, vimeo), ) - patreon := support.NewPatreon(pg) - srv := http.Server{ Addr: fmt.Sprintf(":%d", 5001), Handler: handler.New(feed, patreon, cfg), @@ -92,7 +91,7 @@ func main() { log.Printf("shutting down server") srv.Shutdown(ctx) - pg.Close() + database.Close() log.Printf("server gracefully stopped") } diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index b5b3b36..2dc7ec9 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -28,11 +28,8 @@ type feedService interface { GetMetadata(hashId string) (*api.Feed, error) } -// HACK: mockgen fails to import patreon.Pledge type -type P *patreon.Pledge - type patreonService interface { - Hook(pledge P, event string) error + Hook(pledge *patreon.Pledge, event string) error GetFeatureLevel(patronID string) int } From 66150c642ba81a1a8ad488fc9a3d28c294b30f90 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Thu, 2 Nov 2017 18:22:01 -0700 Subject: [PATCH 9/9] Fix mockgen --- pkg/handler/handler.go | 2 +- pkg/handler/handler_mock_test.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 2dc7ec9..806857c 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -10,7 +10,7 @@ import ( "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" - "github.com/mxpv/patreon-go" + patreon "github.com/mxpv/patreon-go" itunes "github.com/mxpv/podcast" "github.com/mxpv/podsync/pkg/api" "github.com/mxpv/podsync/pkg/config" diff --git a/pkg/handler/handler_mock_test.go b/pkg/handler/handler_mock_test.go index ecfa7d1..050eb6a 100644 --- a/pkg/handler/handler_mock_test.go +++ b/pkg/handler/handler_mock_test.go @@ -5,6 +5,7 @@ package handler import ( gomock "github.com/golang/mock/gomock" + patreon_go "github.com/mxpv/patreon-go" podcast "github.com/mxpv/podcast" api "github.com/mxpv/podsync/pkg/api" reflect "reflect" @@ -96,7 +97,7 @@ func (_m *MockpatreonService) EXPECT() *MockpatreonServiceMockRecorder { } // Hook mocks base method -func (_m *MockpatreonService) Hook(pledge P, event string) error { +func (_m *MockpatreonService) Hook(pledge *patreon_go.Pledge, event string) error { ret := _m.ctrl.Call(_m, "Hook", pledge, event) ret0, _ := ret[0].(error) return ret0