From 563f7deb175b95ede4e36d2c9306f27e8813013b Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Sun, 13 Aug 2017 17:39:30 -0700 Subject: [PATCH] Implement server handler, add startup logic --- web/main.go | 47 ++++++++++++++++++++- web/pkg/feeds/feeds.go | 31 ++++++++++++++ web/pkg/server/server.go | 91 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 web/pkg/server/server.go diff --git a/web/main.go b/web/main.go index c2ba404..207b7bf 100644 --- a/web/main.go +++ b/web/main.go @@ -8,6 +8,14 @@ import ( "os" "os/signal" "syscall" + + "github.com/mxpv/podsync/web/pkg/api" + "github.com/mxpv/podsync/web/pkg/builders" + "github.com/mxpv/podsync/web/pkg/config" + "github.com/mxpv/podsync/web/pkg/feeds" + "github.com/mxpv/podsync/web/pkg/id" + "github.com/mxpv/podsync/web/pkg/server" + "github.com/mxpv/podsync/web/pkg/storage" ) func main() { @@ -17,8 +25,45 @@ func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + // Create core sevices + + cfg, err := config.ReadConfiguration() + if err != nil { + panic(err) + } + + hashIds, err := id.NewIdGenerator() + if err != nil { + panic(err) + } + + redis, err := storage.NewRedisStorage(cfg.RedisURL) + if err != nil { + panic(err) + } + + // Builders + + youtube, err := builders.NewYouTubeBuilder(cfg.YouTubeApiKey) + if err != nil { + panic(err) + } + + vimeo, err := builders.NewVimeoBuilder(ctx, cfg.VimeoApiKey) + if err != nil { + panic(err) + } + + feed := feeds.NewFeedService( + feeds.WithIdGen(hashIds), + feeds.WithStorage(redis), + feeds.WithBuilder(api.Youtube, youtube), + feeds.WithBuilder(api.Vimeo, vimeo), + ) + srv := http.Server{ - Addr: fmt.Sprintf(":%d", 8080), + Addr: fmt.Sprintf(":%d", 8080), + Handler: server.MakeHandlers(feed), } go func() { diff --git a/web/pkg/feeds/feeds.go b/web/pkg/feeds/feeds.go index 8ab351d..24a0e5a 100644 --- a/web/pkg/feeds/feeds.go +++ b/web/pkg/feeds/feeds.go @@ -73,3 +73,34 @@ func (s *service) GetMetadata(hashId string) (*api.Feed, error) { return feed, nil } + +type feedOption func(*service) + +func WithStorage(storage storage) feedOption { + return func(service *service) { + service.storage = storage + } +} + +func WithIdGen(id id) feedOption { + return func(service *service) { + service.id = id + } +} + +func WithBuilder(provider api.Provider, builder builder) feedOption { + return func(service *service) { + service.builders[provider] = builder + } +} + +func NewFeedService(opts ...feedOption) *service { + svc := &service{} + svc.builders = make(map[api.Provider]builder) + + for _, fn := range opts { + fn(svc) + } + + return svc +} diff --git a/web/pkg/server/server.go b/web/pkg/server/server.go new file mode 100644 index 0000000..bbb7550 --- /dev/null +++ b/web/pkg/server/server.go @@ -0,0 +1,91 @@ +package server + +import ( + "context" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + itunes "github.com/mxpv/podcast" + "github.com/mxpv/podsync/web/pkg/api" + "github.com/pkg/errors" +) + +type feed interface { + CreateFeed(ctx context.Context, req *api.CreateFeedRequest) (string, error) + GetFeed(hashId string) (*itunes.Podcast, error) + GetMetadata(hashId string) (*api.Feed, error) +} + +func MakeHandlers(feed feed) http.Handler { + r := gin.New() + r.Use(gin.Recovery()) + + r.GET("/ping", func(c *gin.Context) { + c.String(http.StatusOK, "ok") + }) + + r.POST("/create", func(c *gin.Context) { + req := &api.CreateFeedRequest{} + + if err := c.BindJSON(req); err != nil { + c.JSON(badRequest(err)) + return + } + + if err := binding.Validator.ValidateStruct(req); err != nil { + c.JSON(badRequest(err)) + return + } + + hashId, err := feed.CreateFeed(c.Request.Context(), req) + if err != nil { + c.JSON(internalError(err)) + return + } + + c.JSON(http.StatusOK, gin.H{"id": hashId}) + }) + + r.GET("/:hashId", func(c *gin.Context) { + hashId := c.Param("hashId") + if hashId == "" || len(hashId) > 12 { + c.JSON(badRequest(errors.New("invalid feed id"))) + return + } + + podcast, err := feed.GetFeed(hashId) + if err != nil { + c.JSON(internalError(err)) + return + } + + c.Data(http.StatusOK, "application/rss+xml", podcast.Bytes()) + }) + + r.GET("/metadata/:hashId", func(c *gin.Context) { + hashId := c.Param("hashId") + if hashId == "" || len(hashId) > 12 { + c.JSON(badRequest(errors.New("invalid feed id"))) + return + } + + feed, err := feed.GetMetadata(hashId) + if err != nil { + c.JSON(internalError(err)) + return + } + + c.JSON(http.StatusOK, feed) + }) + + return r +} + +func badRequest(err error) (int, interface{}) { + return http.StatusBadRequest, gin.H{"error": err.Error()} +} + +func internalError(err error) (int, interface{}) { + return http.StatusInternalServerError, gin.H{"error": err.Error()} +}