Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
config.yml
README.md
.idea
4 changes: 2 additions & 2 deletions api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ COPY go.mod go.sum ./
RUN go mod download

# Add the directories which contain the golang scripts
COPY cmd ./
COPY . .
COPY config.prod.yml ./config.yml

# Build
RUN CGO_ENABLED=0 GOOS=linux go build -o /api
RUN CGO_ENABLED=0 GOOS=linux go build -C cmd -o /api

# Optional:
# To bind to a TCP port, runtime parameters must be supplied to the docker command.
Expand Down
66 changes: 17 additions & 49 deletions api/cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,70 +1,38 @@
package main

import (
"api/controllers"
"api/repositories"
"api/services"
"fmt"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"log"
"net/http"
)

var db = make(map[string]string)

func setupRouter() *gin.Engine {
// Disable Console Color
// gin.DisableConsoleColor()
//Setup Gin
r := gin.Default()

//Setup Repositories
gamesRepository := repositories.GameRepository()
gamesService := services.GameService(gamesRepository)
gamesController := controllers.GameController(gamesService)

// Ping test
r.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong")
})

// Get user value
r.GET("/user/:name", func(c *gin.Context) {
user := c.Params.ByName("name")
value, ok := db[user]
if ok {
c.JSON(http.StatusOK, gin.H{"user": user, "value": value})
} else {
c.JSON(http.StatusOK, gin.H{"user": user, "status": "no value"})
}
})

// Authorized group (uses gin.BasicAuth() middleware)
// Same than:
// authorized := r.Group("/")
// authorized.Use(gin.BasicAuth(gin.Credentials{
// "foo": "bar",
// "manu": "123",
//}))
authorized := r.Group("/", gin.BasicAuth(gin.Accounts{
"foo": "bar", // user:foo password:bar
"manu": "123", // user:manu password:123
}))

/* example curl for /admin with basicauth header
Zm9vOmJhcg== is base64("foo:bar")

curl -X POST \
http://localhost:8080/admin \
-H 'authorization: Basic Zm9vOmJhcg==' \
-H 'content-type: application/json' \
-d '{"value":"bar"}'
*/
authorized.POST("admin", func(c *gin.Context) {
user := c.MustGet(gin.AuthUserKey).(string)

// Parse JSON
var json struct {
Value string `json:"value" binding:"required"`
}

if c.Bind(&json) == nil {
db[user] = json.Value
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}
})
//Upload a game
r.POST("/games/", gamesController.UploadGame)
//Get all uploaded games
r.GET("/games", gamesController.GetAllGames)
//Get a specific game by its id
r.GET("/games/:id", gamesController.GetGameById)
//Delete a specific game, identified by its id
r.DELETE("/games/:id", gamesController.DeleteGameById)

return r
}
Expand Down
114 changes: 114 additions & 0 deletions api/controllers/gameController.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package controllers

import (
"api/dtos"
"api/services"
"fmt"
"github.com/dranikpg/dto-mapper"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"net/http"
)

type IGameController interface {
GetAllGames(c *gin.Context)
GetGameById(c *gin.Context)
UploadGame(c *gin.Context)
DeleteGameById(c *gin.Context)
}

type gameController struct {
service services.IGameService
}

func (g gameController) GetAllGames(c *gin.Context) {
//Get Games
games, err := g.service.FindAll()
if err != nil { //TODO handle different errors
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return
}

//Map to dto
resultDto := []dtos.GetGameByIdResponseBody{}
err = dto.Map(&resultDto, games)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return
}

c.IndentedJSON(http.StatusOK, resultDto)
}

func (g gameController) GetGameById(c *gin.Context) {
_uuid := getUUIDFromRequest(c)
if _uuid != uuid.Nil {
//Get game by uuid
game, err := g.service.FindByID(_uuid)
if err != nil { //TODO handle different errors
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return
}

//Map to dto
resultDto := dtos.GetGameByIdResponseBody{}
err = dto.Map(&resultDto, game)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return
}

c.IndentedJSON(http.StatusOK, resultDto)
}
}

func (g gameController) UploadGame(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
}

_, err = g.service.Save(file)

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
}

c.Header("content-location", fmt.Sprintf("%s/games/%s", c.Request.Host, file.Filename))
c.String(http.StatusCreated, "")
}

func (g gameController) DeleteGameById(c *gin.Context) {
_uuid := getUUIDFromRequest(c)
if _uuid != uuid.Nil {
err := g.service.Delete(_uuid)
if err != nil { //TODO handle different errors
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
} else {
c.Status(http.StatusNoContent)
}
}

//TODO Implement delete game
c.String(http.StatusNoContent, "")
}

func GameController(service services.IGameService) IGameController {
return &gameController{
service: service,
}
}

// Parses the UUID from the request param "uuid" and returns it.
// It returns HTTP 400 and uuid.nil if the uuid is invalid or null
func getUUIDFromRequest(c *gin.Context) uuid.UUID {
_uuid, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid game ID"})
return uuid.Nil
} else if _uuid == uuid.Nil {
c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid game ID"})
return uuid.Nil
}
return _uuid
}
20 changes: 20 additions & 0 deletions api/dtos/gameDtos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dtos

import (
"api/shared"
"github.com/google/uuid"
)

type GetAllGamesResponseBody struct {
ID uuid.UUID `json:"id"`
Title string `json:"title"`
Status shared.GameStatus `json:"status"`
Url string `json:"url"`
}

type GetGameByIdResponseBody struct {
ID uuid.UUID `json:"id"`
Title string `json:"title"`
Status shared.GameStatus `json:"status"`
Url string `json:"url"`
}
2 changes: 2 additions & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ module api
go 1.20

require (
github.com/dranikpg/dto-mapper v0.2.1
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.4.0
github.com/spf13/viper v1.18.2
)

Expand Down
4 changes: 4 additions & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/cloudwego/iasm v0.1.1/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/dranikpg/dto-mapper v0.2.1 h1:1DaphrSfBXZVlVolCP+XspMzBAFYGne91+SK594xyTg=
github.com/dranikpg/dto-mapper v0.2.1/go.mod h1:Hkidt8Lkurm7pLPYOiq3I/LlIBmDdB4J4c/VMqFXHfg=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
Expand All @@ -34,6 +36,8 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Expand Down
14 changes: 14 additions & 0 deletions api/models/game.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package models

import (
"api/shared"
"github.com/google/uuid"
)

type Game struct {
ID uuid.UUID `json:"id"`
Title string `json:"title"`
StorageLocation string `json:"storageLocation"`
Status shared.GameStatus `json:"status"`
Url string `json:"url"`
}
66 changes: 66 additions & 0 deletions api/repositories/gameRepository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package repositories

import (
"api/models"
"api/shared"
"github.com/google/uuid"
)

type IGameRepository interface {
FindAll() ([]models.Game, error)
FindByID(id uuid.UUID) (models.Game, error)
Save(game models.Game) (models.Game, error)
Delete(id uuid.UUID) error
}

type gameRepository struct {
//TODO add database
}

func GameRepository() IGameRepository {
return &gameRepository{
//TODO add database
}
}

func (g gameRepository) FindAll() ([]models.Game, error) {
//TODO implement me
return []models.Game{
{
ID: uuid.New(),
Title: "Mock1",
StorageLocation: "Mock1",
Status: shared.Status_Installed,
Url: "https://localhost:4200",
},
{
ID: uuid.New(),
Title: "Mock2",
StorageLocation: "Mock2",
Status: shared.Status_Installed,
Url: "https://localhost:4200",
},
}, nil
}

func (g gameRepository) FindByID(id uuid.UUID) (models.Game, error) {
//TODO implement me
return models.Game{
ID: id,
Title: "Mock",
StorageLocation: "Mock",
Status: shared.Status_Installed,
Url: "https://localhost:4200",
}, nil
}

func (g gameRepository) Save(game models.Game) (models.Game, error) {
//TODO implement me
game.ID = uuid.New()
return game, nil
}

func (g gameRepository) Delete(id uuid.UUID) error {
//TODO implement me
return nil
}
Loading