Skip to content

Commit

Permalink
Merge pull request #6 from ystv/DDD
Browse files Browse the repository at this point in the history
Alleged Domain Driven Design / Dependency Injection and other things
  • Loading branch information
rmil authored Aug 28, 2020
2 parents c8452ee + 772adee commit 550b456
Show file tree
Hide file tree
Showing 57 changed files with 1,853 additions and 1,115 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,15 @@ Then fill in the docker-compose file with your credentials.

## Developing

Clone the repo and create a `.env.local` for an easy config setup. Use the `debug` flag to disable auth.
I recommend not using the production environment for testing and recommend running postgres and rabbitmq in Docker.
Developed on Go version 1.14+

Clone the repo and create a `.env.local` for an easy config setup. Use the `debug` flag to disable auth.

I recommend not using the production environment for testing and recommend running postgres and rabbitmq in Docker.

Updating the DB schema use `goose` to migrate safely.

Developed on Go version 1.14+
When ran with the `debug` flag set to true. 500 server errors will be returned to the browser, not just logged. Otherwise it will only return the 500 code and not not the actual error.

Try to keep all "business" logic with in the `/services` and try to keep the imports local to that package, but you'll probably need the `utils` package but we're trying to keep it modular so web-api can theoretically be split up and keeping it seperate would likely make it a lot easier.

Expand Down
10 changes: 4 additions & 6 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package main

//go:generate ./sqlboiler --wipe psql --add-global-variants

import (
"log"
"os"
Expand All @@ -13,7 +11,7 @@ import (
)

// Version returns web-api's current version
var Version = "dev (0.5.3)"
var Version = "dev (0.5.7)"

// Commit returns latest commit hash
var Commit = "unknown"
Expand All @@ -33,11 +31,11 @@ func main() {
if debug {
log.Println("Debug Mode - Disabled auth - pls don't run in production")
}
utils.InitDB()
utils.InitCDN()
db := utils.InitDB()
cdn := utils.InitCDN()
// utils.InitMessaging()

e := routes.Init(Version, Commit)
e := routes.Init(Version, Commit, db, cdn)

e.Logger.Fatal(e.Start(":8081"))
}
21 changes: 21 additions & 0 deletions controllers/v1/clapper/clapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package clapper

import (
"github.com/jmoiron/sqlx"
"github.com/ystv/web-api/services/clapper"
"github.com/ystv/web-api/services/clapper/event"
"github.com/ystv/web-api/services/clapper/position"
"github.com/ystv/web-api/services/clapper/signup"
)

// Repos encapsulates the dependency
type Repos struct {
event clapper.EventRepo
signup clapper.SignupRepo
position clapper.PositionRepo
}

// NewRepos creates our data store
func NewRepos(db *sqlx.DB) *Repos {
return &Repos{event.NewStore(db), signup.NewStore(db), position.NewStore(db)}
}
67 changes: 37 additions & 30 deletions controllers/v1/clapper/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,91 @@ package clapper

import (
"database/sql"
"log"
"errors"
"fmt"
"net/http"
"strconv"

"github.com/labstack/echo/v4"
"github.com/ystv/web-api/controllers/v1/people"
"github.com/ystv/web-api/services/clapper/event"
"github.com/ystv/web-api/services/clapper"
)

// MonthList returns all events for a month.
func MonthList(c echo.Context) error {
func (r *Repos) MonthList(c echo.Context) error {
year, err := strconv.Atoi(c.Param("year"))
if err != nil {
return c.String(http.StatusBadRequest, "Year incorrect, format /yyyy/mm")
return echo.NewHTTPError(http.StatusBadRequest, "Year incorrect, format /yyyy/mm")
}
month, err := strconv.Atoi(c.Param("month"))
if err != nil {
return c.String(http.StatusBadRequest, "Month incorrect, format /yyyy/mm")
return echo.NewHTTPError(http.StatusBadRequest, "Month incorrect, format /yyyy/mm")
}
e, err := event.ListMonth(year, month)
e, err := r.event.ListMonth(c.Request().Context(), year, month)
if err != nil {
log.Printf("MonthList failed: %v", err)
return c.JSON(http.StatusBadRequest, err)
err = fmt.Errorf("ListMonth failed: %w", err)
return echo.NewHTTPError(http.StatusBadRequest, err)
}
return c.JSON(http.StatusOK, e)
}

// EventGet handles getting all signups and roles for a given event
func EventGet(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
func (r *Repos) EventGet(c echo.Context) error {
id, err := strconv.Atoi(c.Param("eventid"))
if err != nil {
return c.String(http.StatusBadRequest, "Bad video ID")
return echo.NewHTTPError(http.StatusBadRequest, "Invalid event ID")
}
e, err := event.Get(id)
e, err := r.event.Get(c.Request().Context(), id)
if err != nil {
log.Printf("EventGet failed: %v", err)
return c.NoContent(http.StatusInternalServerError)
err = fmt.Errorf("EventGet failed: %w", err)
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
return c.JSON(http.StatusOK, e)
}

// EventNew handles creating a new event
func EventNew(c echo.Context) error {
e := event.Event{}
func (r *Repos) EventNew(c echo.Context) error {
e := clapper.Event{}
err := c.Bind(&e)
if err != nil {
return c.JSON(http.StatusBadRequest, err)
err = fmt.Errorf("EventNew: failed to bind to request json: %w", err)
return echo.NewHTTPError(http.StatusBadRequest, err)
}
p, err := people.GetToken(c)
if err != nil {
return c.JSON(http.StatusInternalServerError, err)
err = fmt.Errorf("EventNew: failed to get token: %w", err)
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
eventID, err := event.New(&e, p.UserID)
eventID, err := r.event.New(c.Request().Context(), &e, p.UserID)
if err != nil {
log.Printf("EventNew failed: %v", err)
return c.JSON(http.StatusInternalServerError, err)
err = fmt.Errorf("EventNew: failed to insert event: %w", err)
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
return c.JSON(http.StatusCreated, eventID)
}

// EventUpdate updates an existing event
func EventUpdate(c echo.Context) error {
e := event.Event{}
func (r *Repos) EventUpdate(c echo.Context) error {
e := clapper.Event{}
err := c.Bind(&e)
if err != nil {
return c.JSON(http.StatusBadRequest, err)
err = fmt.Errorf("EventUpdate: failed to bind to request json: %w", err)
return echo.NewHTTPError(http.StatusBadRequest, err)
}
p, err := people.GetToken(c)
if err != nil {
return c.JSON(http.StatusInternalServerError, err)
err = fmt.Errorf("EventUpdate: failed to get token: %w", err)
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
err = event.Update(&e, p.UserID)
err = r.event.Update(c.Request().Context(), &e, p.UserID)
if err != nil {
if err == sql.ErrNoRows {
return c.JSON(http.StatusBadRequest, err)
if errors.Is(err, sql.ErrNoRows) {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
log.Printf("PositionUpdate failed: %v", err)
return c.JSON(http.StatusInternalServerError, err)
err = fmt.Errorf("EventUpdate: failed to update: %w", err)
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
return c.NoContent(http.StatusOK)
}

// TODO add delete
45 changes: 25 additions & 20 deletions controllers/v1/clapper/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,57 @@ package clapper

import (
"database/sql"
"log"
"errors"
"fmt"
"net/http"

"github.com/labstack/echo/v4"
"github.com/ystv/web-api/services/clapper/position"
"github.com/ystv/web-api/services/clapper"
)

// PositionList handles listing all possible positions
func PositionList(c echo.Context) error {
p, err := position.List()
func (r *Repos) PositionList(c echo.Context) error {
p, err := r.position.List(c.Request().Context())
if err != nil {
log.Printf("PositionList failed: %v", err)
return c.JSON(http.StatusInternalServerError, err)
err = fmt.Errorf("PositionList: failed to list: %w", err)
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
return c.JSON(http.StatusOK, p)
}

// PositionNew handles creating a new position
func PositionNew(c echo.Context) error {
p := position.Position{}
func (r *Repos) PositionNew(c echo.Context) error {
p := clapper.Position{}
err := c.Bind(&p)
if err != nil {
return c.JSON(http.StatusBadRequest, err)
err = fmt.Errorf("PositionNew: failed to bind to request json: %w", err)
return echo.NewHTTPError(http.StatusBadRequest, err)
}
err = position.New(&p)
positionID, err := r.position.New(c.Request().Context(), &p)
if err != nil {
log.Printf("PositionNew failed: %+v", err)
return c.JSON(http.StatusInternalServerError, err)
err = fmt.Errorf("PositionNew: failed to insert position: %w", err)
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
return c.NoContent(http.StatusCreated)
return c.JSON(http.StatusCreated, positionID)
}

// PositionUpdate updates an existing position
func PositionUpdate(c echo.Context) error {
p := position.Position{}
func (r *Repos) PositionUpdate(c echo.Context) error {
p := clapper.Position{}
err := c.Bind(&p)
if err != nil {
return c.JSON(http.StatusBadRequest, err)
err = fmt.Errorf("PositionUpdate: failed to bind to request json: %w", err)
return echo.NewHTTPError(http.StatusBadRequest, err)
}
err = position.Update(&p)
err = r.position.Update(c.Request().Context(), &p)
if err != nil {
if err == sql.ErrNoRows {
return c.JSON(http.StatusBadRequest, err)
if errors.Is(err, sql.ErrNoRows) {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
log.Printf("PositionUpdate failed: %v", err)
err = fmt.Errorf("PositionUpdate failed: %w", err)
return c.JSON(http.StatusInternalServerError, err)
}
return c.NoContent(http.StatusOK)
}

// TODO add delete
46 changes: 46 additions & 0 deletions controllers/v1/clapper/signup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package clapper

import (
"database/sql"
"errors"
"fmt"
"net/http"
"strconv"

"github.com/labstack/echo/v4"
"github.com/ystv/web-api/services/clapper"
)

// SignupNew
func (r *Repos) SignupNew(c echo.Context) error {
// Validate event ID
eventID, err := strconv.Atoi(c.Param("eventID"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Bad event ID")
}
// Bind request json to signup
s := clapper.Signup{}
err = c.Bind(&s)
if err != nil {
err = fmt.Errorf("SignupNew: failed to bind to request json: %w", err)
return echo.NewHTTPError(http.StatusBadRequest, err)
}

// Check event exists
// TODO we might want to move this inside the service
e, err := r.event.Get(c.Request().Context(), eventID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return echo.NewHTTPError(http.StatusNotFound, "No event found")
}
return echo.NewHTTPError(http.StatusInternalServerError, err)
}

// Insert new signup sheet
signupID, err := r.signup.New(c.Request().Context(), e.EventID, s)
if err != nil {
err = fmt.Errorf("SignupNew: failed to insert new signup: %w", err)
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
return c.JSON(http.StatusCreated, signupID)
}
42 changes: 31 additions & 11 deletions controllers/v1/creator/creator.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
package creator

import (
"context"
"fmt"
"net/http"

"github.com/aws/aws-sdk-go/service/s3"
"github.com/jmoiron/sqlx"
"github.com/labstack/echo/v4"
"github.com/ystv/web-api/services/creator"
"github.com/ystv/web-api/services/creator/breadcrumb"
"github.com/ystv/web-api/services/creator/encode"
"github.com/ystv/web-api/services/creator/playlist"
"github.com/ystv/web-api/services/creator/series"
"github.com/ystv/web-api/services/creator/video"
)

// FileUpload Handles uploading a file
func FileUpload(c echo.Context) error {
creator.CreateBucket("pending", "ystv-wales-1")
url, err := creator.GenerateUploadURL("pending", c.Param("id"))
if err != nil {
return c.JSON(http.StatusInternalServerError, err.Error())
// Repos represents all our data repositories
type Repos struct {
video creator.VideoRepo
series creator.SeriesRepo
playlist creator.PlaylistRepo
breadcrumb creator.BreadcrumbRepo
encode creator.EncodeRepo
creator creator.StatRepo
}

// NewRepos creates our data repositories
func NewRepos(db *sqlx.DB, cdn *s3.S3) *Repos {
return &Repos{
video.NewStore(db, cdn),
series.NewController(db, cdn),
playlist.NewStore(db),
breadcrumb.NewController(db, cdn),
encode.NewStore(db),
creator.NewStore(db),
}
return c.JSON(http.StatusOK, url)
}

// Stats handles sending general stats about the video library
func Stats(c echo.Context) error {
s, err := creator.Stats(context.Background())
func (r *Repos) Stats(c echo.Context) error {
s, err := r.creator.GlobalVideo(c.Request().Context())
if err != nil {
return c.JSON(http.StatusBadRequest, err)
err = fmt.Errorf("stats failed: %w", err)
return echo.NewHTTPError(http.StatusBadRequest, err)
}
return c.JSON(http.StatusOK, s)
}
Loading

0 comments on commit 550b456

Please sign in to comment.