Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
aa8c6e5
adr api database
Intrpt May 3, 2024
8ee843d
adr api orm
Intrpt May 3, 2024
76a6687
setup database and migrations
Intrpt May 7, 2024
bc354e2
Game-Services/Repository interface changes
Intrpt May 7, 2024
dbe209a
GameRepository implementation
Intrpt May 7, 2024
10778bd
readme for migrations
Intrpt May 7, 2024
622c917
added internal to gitignore
Intrpt May 7, 2024
9b6becc
gamecontroller return 404 in FindById
Intrpt May 7, 2024
ed083c2
adr api orm (2)
Intrpt May 7, 2024
16cd566
fix typo
Intrpt May 7, 2024
eef0e9c
setup docker-compose with database
Intrpt May 7, 2024
144c53b
rename "config.prod" to "config.deployment"
Intrpt May 7, 2024
1ba3cdd
rename "loadEnv" to "loadConfig"
Intrpt May 7, 2024
217618f
GameRepository.Delete() return err if game not found
Intrpt May 8, 2024
7d70e04
GameController.DeleteById() return 404 if game is not existing
Intrpt May 8, 2024
2e22ef5
spelling
Intrpt May 8, 2024
4fb3480
GameRepository.Save() check if operation was successfull
Intrpt May 9, 2024
988cd09
fix gameRepositors.FindAll() returns nil when db is empty
Intrpt May 9, 2024
b203dd0
GameRepository unit tests
Intrpt May 9, 2024
8cc2ad1
updated migrationScript
Intrpt May 9, 2024
d3fcb18
fix broke config.yml
Intrpt May 9, 2024
cf64c96
Merge branch 'develop' into feature/22-api-database
Intrpt May 9, 2024
af10482
check result of update query
Intrpt May 9, 2024
60c5aaa
Merge branch 'feature/22-api-database' of https://github.com/Austrian…
Intrpt May 9, 2024
6650e6e
added api image to workflow
Intrpt May 9, 2024
8a26dce
multistage dockerfile (build, release)
Intrpt May 9, 2024
e131118
added test stage to dockerfile
Intrpt May 9, 2024
aa49abf
Added required "title" Parameter to upload game
Intrpt May 12, 2024
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
2 changes: 2 additions & 0 deletions .github/workflows/build-test-scan-push-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
# Add your images here (name of the component + directory that contains Dockerfile)
- name: frontend
directory: frontend
- name: api
directory: api
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

api/.idea/
.idea/
api/internal/
.idea/
24 changes: 24 additions & 0 deletions adrs/03052024-api-database.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# AD: Database for the Api
**Decider:** Kai Pietruska

## Context and Problem Statement
Which database do we want to use to save the metadata of the games?

## Decision Drivers
We want to use a database to save the metadata of our games.
It should be known to everyone, so that we don't have to learn how to work with it.


## Considered Options
- MySQL
- Postgresql
- SQLite
- MongoDB
- Apache Cassandra

## Decision Outcome
MySQL is for our use case sufficient and easy to use.


## Consequences
tbd
23 changes: 23 additions & 0 deletions adrs/03052024-api-orm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# AD: Object-Relational Mapper for the api
**Decider:** Kai Pietruska

## Context and Problem Statement
What ORM do we use to implement our api?

## Decision Drivers
We want to use a ORM, so that we don't have to deal with raw SQL queries and we can modify our database easily.
It should generate SQL queries from Go code, since we already have our Go code.

## Considered Options
- GORM
- ent
- gorp

## Decision Outcome
gorp because it is lightwight and brings all the features we need for our use case.
GORM is nice but brings a lot of overhead and features we don't need.
ent has a steep learning curve.


## Consequences
tbd
26 changes: 26 additions & 0 deletions adrs/04052024-api-orm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# AD: Object-Relational Mapper for the api
**Decider:** Kai Pietruska

## Context and Problem Statement
What ORM do we use to implement our api?

## Decision Drivers
We want to use a ORM, so that we don't have to deal with raw SQL queries and we can modify our database easily.
It should generate SQL queries from Go code, since we already have our Go code.

## Considered Options
- GORM
- ent
- gorp

## Decision Outcome
We don't use any GORM.

gorp because it is lightwight and brings all the features we need for our use case, theoretically.
In practical it already failed creating the database and it doesn't support UUID prmary keys out of the box.
GORM is nice but brings a lot of overhead and features we don't need.
ent has a steep learning curve.


## Consequences
tbd
4 changes: 3 additions & 1 deletion api/.dockerignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
config.yml
README.md
.idea
.idea
internal
docker-compose.yml
28 changes: 16 additions & 12 deletions api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
FROM golang:1.22-alpine

#Stage 1: Compile and build
FROM golang:1.22-alpine as build
# Set destination for COPY
WORKDIR /app

# Download Go modules
COPY go.mod go.sum ./
RUN go mod download

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

# Build
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.
# But we can document in the Dockerfile what ports
# the application is going to listen on by default.
# https://docs.docker.com/reference/dockerfile/#expose
EXPOSE 8080
#Stage 2a: Run tests
FROM golang:1.22-alpine as test
WORKDIR /app
COPY . .
CMD ["go", "test", "./tests"]

#Stage 2b: Run Api
FROM scratch as release
#Copy config
COPY config.deployment.yml ./config.yml
#Copy migration scripts
COPY migrations migrations
#Copy build result to next stage
COPY --from=build /api /api
EXPOSE 8080
# Run
CMD ["/api"]
9 changes: 4 additions & 5 deletions api/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
## Build the docker container
``docker build . -t api:latest ``
## Run the docker container
``docker run -p 8080:8080 -i api:latest``\
The api will be exposed to port 8080, access it with `localhost:8080`.
## TL;DR
`` docker compose build; `` `` docker compose up ``

The api will be exposed to port 8080, access it with `localhost:8080`.
35 changes: 29 additions & 6 deletions api/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ package main
import (
"api/controllers"
"api/repositories"
"api/scripts"
"api/services"
"database/sql"
"fmt"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
"github.com/spf13/viper"
"log"
"net/http"
)

func setupRouter() *gin.Engine {
func setupRouter(db *sql.DB) *gin.Engine {
//Setup Gin
r := gin.Default()

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

Expand All @@ -37,23 +40,43 @@ func setupRouter() *gin.Engine {
return r
}

func loadEnv() {
func loadConfig() {
viper.SetConfigFile("config.yml")
err := viper.ReadInConfig()
if err != nil {
log.Fatal(err.Error())
}
}

func setupDatabase() *sql.DB {
//Create database if it is not existing yet.
//We might have to remove this if we use an azure database
scripts.CreateDatabaseIfNotExists(viper.GetString("DATABASE.NAME"))
//Connect to the database
db := scripts.ConnectToDatabase()
//Check if database is online
err := db.Ping()
if err != nil {
log.Fatal(err.Error())
}
//Check if we have new migrations and apply them
scripts.MigrateDatabase(db)
return db
}

func main() {
//Load environment file
loadEnv()
//Load config file
loadConfig()

//Setup database
db := setupDatabase()
defer db.Close()

//Set Gin-gonic to debug or release mode
gin.SetMode(viper.GetString("GIN_MODE"))

//Setup Routes
r := setupRouter()
r := setupRouter(db)

// Listen and Server in 0.0.0.0:8080
r.Run(fmt.Sprintf(":%d", viper.GetInt("port")))
Expand Down
9 changes: 9 additions & 0 deletions api/config.deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## PRODUCTIVE CONFIG
PORT: 8080
GIN_MODE: release #["release", "debug"]
DATABASE:
NAME: api #database name
USER: root #This login name is just for local deployment. TODO set it in productive deployment
PASSWORD: Root#123 #This password is just for local deployment. TODO set it in productive deployment
HOST: mysql
PORT: 3306
3 changes: 0 additions & 3 deletions api/config.prod.yml

This file was deleted.

7 changes: 7 additions & 0 deletions api/config.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
PORT: 8080
GIN_MODE: debug #["release", "debug"]
DATABASE:
NAME: api #database name
USER: root #login name
PASSWORD: Root#123 #This password is of course not production. Just local deployment
HOST: localhost
PORT: 3306

16 changes: 15 additions & 1 deletion api/controllers/gameController.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import (
"api/dtos"
"api/services"
"database/sql"
"fmt"
"github.com/dranikpg/dto-mapper"
"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -49,6 +50,10 @@ func (g gameController) GetGameById(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return
}
if game == nil {
c.JSON(http.StatusNotFound, gin.H{"message": "Game not found"})
return
}

//Map to dto
resultDto := dtos.GetGameByIdResponseBody{}
Expand All @@ -63,12 +68,18 @@ func (g gameController) GetGameById(c *gin.Context) {
}

func (g gameController) UploadGame(c *gin.Context) {

title := c.Query("title")
if len(title) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"message": "Title is required"})
}

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

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

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
Expand All @@ -83,6 +94,9 @@ func (g gameController) DeleteGameById(c *gin.Context) {
if _uuid != uuid.Nil {
err := g.service.Delete(_uuid)
if err != nil { //TODO handle different errors
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, gin.H{"message": "Game not found"})
}
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
} else {
c.Status(http.StatusNoContent)
Expand Down
39 changes: 39 additions & 0 deletions api/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#The docker-compose is for local deployment.
#DON'T USE IT IN PRODUCTION IT CONTAINS HARDCODED PASSWORDS
services:
mysql:
image: 'bitnami/mysql:8.3.0'
environment:
- MYSQL_ROOT_USER=root
- MYSQL_ROOT_PASSWORD=Root#123
- MYSQL_DATABASE=api
ports:
- "3306:3306"
volumes:
- ./internal/database:/bitnami/mysql/data
networks:
- api
healthcheck:
test: mysql --user=root --password=Root#123 -e 'SELECT * FROM mysql.time_zone'
restart: on-failure
#command: # fix from https://github.com/bitnami/containers/issues/44854#issuecomment-1800945882
# - "/opt/bitnami/mysql/bin/mysqld"
# - "--defaults-file=/opt/bitnami/mysql/conf/my.cnf"
# - "--basedir=/opt/bitnami/mysql"
# - "--datadir=/bitnami/mysql/data"
# - "--socket=/opt/bitnami/mysql/tmp/mysql.sock"
# - "--pid-file=/opt/bitnami/mysql/tmp/mysqld.pid"
api:
build: .
ports:
- "8080:8080"
restart: on-failure
networks:
- api
depends_on:
mysql:
condition: service_healthy

networks:
api:
driver: bridge
4 changes: 4 additions & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ module api
go 1.20

require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/dranikpg/dto-mapper v0.2.1
github.com/gin-gonic/gin v1.9.1
github.com/go-sql-driver/mysql v1.8.1
github.com/google/uuid v1.4.0
github.com/spf13/viper v1.18.2
)

require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bytedance/sonic v1.11.4 // indirect
github.com/cloudwego/base64x v0.1.1 // indirect
github.com/cloudwego/iasm v0.1.1 // indirect
Expand Down Expand Up @@ -48,6 +51,7 @@ require (
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading