From 8640992fadaf67901a46271cb5d993bd584d68cf Mon Sep 17 00:00:00 2001 From: Suhail Ahmed Date: Sat, 14 Jan 2023 01:53:03 +0530 Subject: [PATCH 1/8] feat: podcast --- config/database.go | 2 + controllers/app_controller.go | 13 ++++-- controllers/podcast_controller.go | 61 ++++++++++++++++++++++++++ docker-compose.yml | 5 --- docs/docs.go | 70 ++++++++++++++++++++++++++++++ docs/swagger.json | 70 ++++++++++++++++++++++++++++++ docs/swagger.yaml | 48 ++++++++++++++++++++ helpers/asset.go | 53 ++++++++++++++++++++++ helpers/podcast.go | 22 ++++++++++ helpers/team.go | 20 --------- models/podcast.go | 15 +++++++ registry/podcast_registry.go | 19 ++++++++ registry/registry.go | 7 +-- repositories/podcast_repository.go | 47 ++++++++++++++++++++ repositories/seeder_repository.go | 5 +++ router/podcast.go | 27 ++++++++++++ router/router.go | 1 + router/swagger.go | 4 +- schemas/asset.go | 5 ++- schemas/podcast.go | 28 ++++++++++++ services/podcast_service.go | 65 +++++++++++++++++++++++++++ services/seeder_service.go | 14 ++++++ services/team_service.go | 2 +- templates/html/swagger.html | 2 +- utils/validator.go | 12 ++++- 25 files changed, 578 insertions(+), 39 deletions(-) create mode 100644 controllers/podcast_controller.go create mode 100644 helpers/asset.go create mode 100644 helpers/podcast.go create mode 100644 models/podcast.go create mode 100644 registry/podcast_registry.go create mode 100644 repositories/podcast_repository.go create mode 100644 router/podcast.go create mode 100644 schemas/podcast.go create mode 100644 services/podcast_service.go diff --git a/config/database.go b/config/database.go index bb5a8dd..d0f48cb 100644 --- a/config/database.go +++ b/config/database.go @@ -47,6 +47,8 @@ func MigrateDB() { &schemas.Team{}, &schemas.Role{}, &schemas.Member{}, + &schemas.PodcastType{}, + &schemas.Podcast{}, } { if err := db.AutoMigrate(&schema); err != nil { panic(err) diff --git a/controllers/app_controller.go b/controllers/app_controller.go index 2f99e31..6ed73ce 100644 --- a/controllers/app_controller.go +++ b/controllers/app_controller.go @@ -8,9 +8,10 @@ import ( ) type AppController struct { - User interface{ UserController } - Team interface{ TeamController } - Seed interface{ SeedController } + User interface{ UserController } + Team interface{ TeamController } + Podcast interface{ PodcastController } + Seed interface{ SeedController } } type seedController struct { @@ -41,6 +42,12 @@ func (s *seedController) SeedDB() error { log.Panic(err) return err } + err = s.seed.PodcastTypesSeeder() + if err != nil { + log.Panic(err) + return err + } + log.Println(color.GreenString("Seeded Successfully")) return nil } diff --git a/controllers/podcast_controller.go b/controllers/podcast_controller.go new file mode 100644 index 0000000..7d1a89c --- /dev/null +++ b/controllers/podcast_controller.go @@ -0,0 +1,61 @@ +package controllers + +import ( + "log" + "net/http" + + "github.com/ecea-nitt/ecea-server/middlewares" + "github.com/ecea-nitt/ecea-server/models" + "github.com/ecea-nitt/ecea-server/services" + "github.com/labstack/echo/v4" +) + +type podcastController struct { + ps services.PodcastService +} + +type PodcastController interface { + CreatePodcast(c echo.Context) error +} + +func NewPodcastController(ps services.PodcastService) PodcastController { + return &podcastController{ps} +} + +// AddMember godoc +// +// @Summary Create Podcast +// @Description Adds a new podcast to the database +// @Tags Podcast +// @Accept multipart/form-data +// @Produce json +// @Param name formData string true "Enter name" +// @Param type formData models.PodcastType true "Choose a type" +// @Param description formData string true "Enter description" +// @Param mediaURL formData string true "Enter Media URL" +// @Param image formData file true "Upload Thumbnail" +// @Success 200 {object} string +// @Failure 400 {object} models.Error +// +// @Router /v1/podcast/create [post] +func (pc *podcastController) CreatePodcast(c echo.Context) error { + request := new(models.PodcastRequest) + if err := c.Bind(request); err != nil { + log.Println(err) + return middlewares.Responder(c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + file, err := c.FormFile("image") + if err != nil { + log.Println(err) + return err + } + + err = pc.ps.CreatePodcast(*request, file) + if err != nil { + log.Println(err) + return middlewares.Responder(c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + return middlewares.Responder(c, http.StatusOK, http.StatusText(http.StatusOK)) +} diff --git a/docker-compose.yml b/docker-compose.yml index 1c09ed0..0e1f45b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,8 +31,3 @@ services: command: -p ${POSTGRES_PORT} volumes: - ./database:/data/postgres - -networks: - default: - external: - name: thirumathikart_network diff --git a/docs/docs.go b/docs/docs.go index 2f0243d..263faeb 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -24,6 +24,76 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/v1/podcast/create": { + "post": { + "description": "Adds a new podcast to the database", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Create Podcast", + "parameters": [ + { + "type": "string", + "description": "Enter name", + "name": "name", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter description", + "name": "description", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter Media URL", + "name": "mediaURL", + "in": "formData", + "required": true + }, + { + "type": "file", + "description": "Upload Thumbnail", + "name": "image", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, "/v1/team/add": { "post": { "security": [ diff --git a/docs/swagger.json b/docs/swagger.json index b74456a..19902e6 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -15,6 +15,76 @@ "version": "1.0" }, "paths": { + "/v1/podcast/create": { + "post": { + "description": "Adds a new podcast to the database", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Create Podcast", + "parameters": [ + { + "type": "string", + "description": "Enter name", + "name": "name", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter description", + "name": "description", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter Media URL", + "name": "mediaURL", + "in": "formData", + "required": true + }, + { + "type": "file", + "description": "Upload Thumbnail", + "name": "image", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, "/v1/team/add": { "post": { "security": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 4f5429a..2ceadd2 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -75,6 +75,54 @@ info: title: Probe Admin version: "1.0" paths: + /v1/podcast/create: + post: + consumes: + - multipart/form-data + description: Adds a new podcast to the database + parameters: + - description: Enter name + in: formData + name: name + required: true + type: string + - description: Choose a type + enum: + - GUEST_LECTURE + - CAREER_PATH + in: formData + name: type + required: true + type: string + - description: Enter description + in: formData + name: description + required: true + type: string + - description: Enter Media URL + in: formData + name: mediaURL + required: true + type: string + - description: Upload Thumbnail + in: formData + name: image + required: true + type: file + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Error' + summary: Create Podcast + tags: + - Podcast /v1/team/add: post: consumes: diff --git a/helpers/asset.go b/helpers/asset.go new file mode 100644 index 0000000..8c649e2 --- /dev/null +++ b/helpers/asset.go @@ -0,0 +1,53 @@ +package helpers + +import ( + "log" + "mime/multipart" + + "github.com/ecea-nitt/ecea-server/repositories" + "github.com/ecea-nitt/ecea-server/utils" +) + +func UploadAndFetchAssetID( + channel chan int, + image *multipart.FileHeader, + repo interface{}, + repoType string) { + + switch repoType { + case "podcast": + repo := repo.(repositories.PodcastRepository) + fileName, err := utils.UploadImage(image) + if err != nil { + log.Println(err) + channel <- -1 + return + } + + id, err := repo.InsertAsset(fileName) + if err != nil { + log.Println(err) + channel <- -1 + return + } + + channel <- int(id) + case "team": + repo := repo.(repositories.TeamRepository) + fileName, err := utils.UploadImage(image) + if err != nil { + log.Println(err) + channel <- -1 + return + } + + id, err := repo.InsertAsset(fileName) + if err != nil { + log.Println(err) + channel <- -1 + return + } + + channel <- int(id) + } +} diff --git a/helpers/podcast.go b/helpers/podcast.go new file mode 100644 index 0000000..8fff1e8 --- /dev/null +++ b/helpers/podcast.go @@ -0,0 +1,22 @@ +package helpers + +import ( + "log" + + "github.com/ecea-nitt/ecea-server/repositories" +) + +func FetchPodcastTypeID( + channel chan int, + podcastTypeName string, + repo repositories.PodcastRepository) { + + id, err := repo.FindPodcastTypeByName(podcastTypeName) + if err != nil { + log.Println(err) + channel <- -1 + return + } + channel <- int(id) + +} diff --git a/helpers/team.go b/helpers/team.go index f3e347b..af1f430 100644 --- a/helpers/team.go +++ b/helpers/team.go @@ -38,26 +38,6 @@ func FetchRoleID( } -func UploadAndFetchAssetID( - channel chan int, - image *multipart.FileHeader, - repo repositories.TeamRepository) { - - fileName, err := utils.UploadImage(image) - if err != nil { - log.Println(err) - channel <- -1 - return - } - id, err := repo.InsertAsset(fileName) - if err != nil { - log.Println(err) - channel <- -1 - return - } - channel <- int(id) -} - func UpdateAndFetchTeamID( dbID uint, dbName string, diff --git a/models/podcast.go b/models/podcast.go new file mode 100644 index 0000000..f9e9025 --- /dev/null +++ b/models/podcast.go @@ -0,0 +1,15 @@ +package models + +type PodcastRequest struct { + Name string `json:"name" form:"name"` + Description string `json:"description" form:"description"` + MediaURL string `json:"mediaUrl" form:"mediaUrl"` + Type PodcastType `json:"type" form:"type"` +} + +type PodcastType string + +const ( + Guestlecture PodcastType = "GUEST_LECTURE" + CareerPath PodcastType = "CAREER_PATH" +) diff --git a/registry/podcast_registry.go b/registry/podcast_registry.go new file mode 100644 index 0000000..ca900ba --- /dev/null +++ b/registry/podcast_registry.go @@ -0,0 +1,19 @@ +package registry + +import ( + "github.com/ecea-nitt/ecea-server/controllers" + "github.com/ecea-nitt/ecea-server/repositories" + "github.com/ecea-nitt/ecea-server/services" +) + +func (r *registry) NewPodcastController() controllers.PodcastController { + return controllers.NewPodcastController(r.NewPodcastService()) +} + +func (r *registry) NewPodcastService() services.PodcastService { + return services.NewPodcastService(r.NewPodcastRepository()) +} + +func (r *registry) NewPodcastRepository() repositories.PodcastRepository { + return repositories.NewPodcastRepository(r.db) +} diff --git a/registry/registry.go b/registry/registry.go index 1142ec8..2fb485b 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -19,8 +19,9 @@ func NewRegistry(db *gorm.DB) Registry { func (r *registry) NewAppController() controllers.AppController { return controllers.AppController{ - User: r.NewUserController(), - Team: r.NewTeamController(), - Seed: r.NewSeedController(), + User: r.NewUserController(), + Team: r.NewTeamController(), + Podcast: r.NewPodcastController(), + Seed: r.NewSeedController(), } } diff --git a/repositories/podcast_repository.go b/repositories/podcast_repository.go new file mode 100644 index 0000000..2ef3fa7 --- /dev/null +++ b/repositories/podcast_repository.go @@ -0,0 +1,47 @@ +package repositories + +import ( + "github.com/ecea-nitt/ecea-server/schemas" + "gorm.io/gorm" +) + +type podcastRepository struct { + db *gorm.DB +} + +type PodcastRepository interface { + InsertPodcast(podcast schemas.Podcast) error + InsertAsset(name string) (uint, error) + FindPodcastTypeByName(name string) (uint, error) +} + +func NewPodcastRepository(db *gorm.DB) PodcastRepository { + return &podcastRepository{db} +} + +func (pr *podcastRepository) InsertPodcast(podcast schemas.Podcast) error { + return pr.db.Create(&podcast).Error +} + +func (pr *podcastRepository) InsertAsset(name string) (uint, error) { + var assetType schemas.AssetType + if err := pr.db.Where("name = ?", schemas.Image).First(&assetType).Error; err != nil { + return 0, err + } + asset := schemas.Asset{ + Name: name, + AssetTypeID: assetType.ID, + } + if err := pr.db.Create(&asset).Error; err != nil { + return 0, err + } + return asset.ID, nil +} + +func (pr *podcastRepository) FindPodcastTypeByName(name string) (uint, error) { + var podcastType schemas.PodcastType + if err := pr.db.Where("name = ?", name).First(&podcastType).Error; err != nil { + return 0, err + } + return podcastType.ID, nil +} diff --git a/repositories/seeder_repository.go b/repositories/seeder_repository.go index f71db45..3f0441e 100644 --- a/repositories/seeder_repository.go +++ b/repositories/seeder_repository.go @@ -13,6 +13,7 @@ type SeedRepository interface { InsertTeams(teams []schemas.Team) error InsertRoles(roles []schemas.Role) error InsertAssetTypes(types []schemas.AssetType) error + InsertPodcastTypes(types []schemas.PodcastType) error } func NewSeedRepository(db *gorm.DB) SeedRepository { @@ -31,3 +32,7 @@ func (sr *seedRepository) InsertRoles(roles []schemas.Role) error { func (sr *seedRepository) InsertAssetTypes(types []schemas.AssetType) error { return sr.db.Create(&types).Error } + +func (sr *seedRepository) InsertPodcastTypes(types []schemas.PodcastType) error { + return sr.db.Create(&types).Error +} diff --git a/router/podcast.go b/router/podcast.go new file mode 100644 index 0000000..6f41cc0 --- /dev/null +++ b/router/podcast.go @@ -0,0 +1,27 @@ +package router + +import ( + "github.com/ecea-nitt/ecea-server/controllers" + "github.com/labstack/echo/v4" +) + +func PodcastRoutes(e *echo.Group, c controllers.PodcastController) { + podcast := e.Group("/podcast") + + // Create + podcast.POST("/create", c.CreatePodcast) + + // Read + // podcast.GET("/getall", c.GetAllPodcasts) + // podcast.GET("/getall/:type", c.GetPodcastByType) + // podcast.GET("/get/:name", c.GetPodcastByName) + + // // Update + // podcast.POST("/edit/thumbnail", c.EditThumbnail) + // podcast.POST("/edit/media", c.EditURL) + // podcast.POST("/edit/description", c.EditDescription) + + // // Delete + // podcast.DELETE("/delete/:name", c.DeletePodcastByName) + +} diff --git a/router/router.go b/router/router.go index 20a3ac4..13a9be7 100644 --- a/router/router.go +++ b/router/router.go @@ -16,5 +16,6 @@ func NewRouter(e *echo.Echo, c controllers.AppController) { UserRoutes(api, c.User) TeamRoutes(api, c.Team) + PodcastRoutes(api, c.Podcast) SwaggerRoutes(api) } diff --git a/router/swagger.go b/router/swagger.go index c0631d3..c7993ae 100644 --- a/router/swagger.go +++ b/router/swagger.go @@ -10,6 +10,6 @@ func SwaggerRoutes(e *echo.Group) { origin := config.Origin url := echoSwagger.URL(origin + "/v1/admin/doc.json") e.GET("/admin/*", echoSwagger.EchoWrapHandler(url)) - // e.File("/admin/index.html", "templates/html/swagger.html") - // e.File("/admin/swagger-ui.css", "templates/css/swagger-ui.css") + e.File("/admin/index.html", "templates/html/swagger.html") + //e.File("/admin/swagger-ui.css", "templates/css/swagger-ui.css") } diff --git a/schemas/asset.go b/schemas/asset.go index 7ab5f23..5d05b0f 100644 --- a/schemas/asset.go +++ b/schemas/asset.go @@ -17,6 +17,7 @@ type AssetType struct { type AssetTypes string const ( - Image AssetTypes = "IMAGE" - Document AssetTypes = "DOCUMENT" + Image AssetTypes = "IMAGE" + Document AssetTypes = "DOCUMENT" + ExternalAudio AssetTypes = "EXTERNAL_AUDIO" ) diff --git a/schemas/podcast.go b/schemas/podcast.go new file mode 100644 index 0000000..58f618c --- /dev/null +++ b/schemas/podcast.go @@ -0,0 +1,28 @@ +package schemas + +import "gorm.io/gorm" + +type Podcast struct { + gorm.Model + Name string `gorm:"varchar(50);not null;unique"` + Description string `gorm:"varchar(800);not null"` + MediaURL string `gorm:"not null"` + + // Relations + TypeID uint `gorm:"not null;"` + Type PodcastType `gorm:"foreignKey:TypeID;"` + ThumbnailID uint `gorm:"not null;"` + Thumbnail Asset `gorm:"foreignKey:ThumbnailID;"` +} + +type PodcastType struct { + gorm.Model + Name string `gorm:"not null;unique"` +} + +type PodcastTypes string + +const ( + GuestLecture PodcastTypes = "GUEST_LECTURE" + CareerPath PodcastTypes = "CAREER_PATH" +) diff --git a/services/podcast_service.go b/services/podcast_service.go new file mode 100644 index 0000000..612d5a4 --- /dev/null +++ b/services/podcast_service.go @@ -0,0 +1,65 @@ +package services + +import ( + "errors" + "mime/multipart" + + "github.com/ecea-nitt/ecea-server/helpers" + "github.com/ecea-nitt/ecea-server/models" + "github.com/ecea-nitt/ecea-server/repositories" + "github.com/ecea-nitt/ecea-server/schemas" + "github.com/ecea-nitt/ecea-server/utils" +) + +type podcastService struct { + repo repositories.PodcastRepository +} + +type PodcastService interface { + CreatePodcast( + podcastDetails models.PodcastRequest, + podcastImage *multipart.FileHeader) error +} + +func NewPodcastService(repo repositories.PodcastRepository) PodcastService { + return &podcastService{repo} +} + +func (ps *podcastService) CreatePodcast( + podcast models.PodcastRequest, + podcastImage *multipart.FileHeader) error { + + name, err := utils.NameValidator(podcast.Name) + if err != nil { + return err + } + + mediaURL, err := utils.URLValidator(podcast.MediaURL) + if err != nil { + return err + } + + assetChannel := make(chan int) + typeChannel := make(chan int) + + go helpers.FetchPodcastTypeID(typeChannel, string(podcast.Type), ps.repo) + + go helpers.UploadAndFetchAssetID(assetChannel, podcastImage, ps.repo, "podcast") + + assetID := <-assetChannel + podcastTypeID := <-typeChannel + + if assetID == -1 || podcastTypeID == -1 { + return errors.New("error uploading asset") + } + + newPodcast := schemas.Podcast{ + Name: name, + Description: podcast.Description, + MediaURL: mediaURL, + ThumbnailID: uint(assetID), + TypeID: uint(podcastTypeID), + } + + return ps.repo.InsertPodcast(newPodcast) +} diff --git a/services/seeder_service.go b/services/seeder_service.go index 9837b41..3a9001e 100644 --- a/services/seeder_service.go +++ b/services/seeder_service.go @@ -14,6 +14,7 @@ type SeederService interface { AssetTypesSeeder() error TeamsSeeder() error RolesSeeder() error + PodcastTypesSeeder() error } var teams = []schemas.Team{ @@ -61,6 +62,15 @@ var assetTypes = []schemas.AssetType{ }, } +var podcastTypes = []schemas.PodcastType{ + { + Name: string(schemas.CareerPath), + }, + { + Name: string(schemas.GuestLecture), + }, +} + func NewSeeder(repo repositories.SeedRepository) SeederService { return &seederService{repo} } @@ -76,3 +86,7 @@ func (s *seederService) RolesSeeder() error { func (s *seederService) AssetTypesSeeder() error { return s.repo.InsertAssetTypes(assetTypes) } + +func (s *seederService) PodcastTypesSeeder() error { + return s.repo.InsertPodcastTypes(podcastTypes) +} diff --git a/services/team_service.go b/services/team_service.go index 084336b..16e7cd2 100644 --- a/services/team_service.go +++ b/services/team_service.go @@ -64,7 +64,7 @@ func (ts *teamService) CreateTeamMember( go helpers.FetchRoleID(roleChannel, string(memberDetails.Role), ts.repo) - go helpers.UploadAndFetchAssetID(assetChannel, memberImage, ts.repo) + go helpers.UploadAndFetchAssetID(assetChannel, memberImage, ts.repo, "team") teamID := <-teamChannel roleID := <-roleChannel diff --git a/templates/html/swagger.html b/templates/html/swagger.html index 77ac384..c3240f7 100644 --- a/templates/html/swagger.html +++ b/templates/html/swagger.html @@ -4,7 +4,7 @@ Probe Admin - +