Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker Bug Fix and some enchanement at database configuration #7

Merged
merged 8 commits into from
Jun 5, 2022
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
13 changes: 6 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ DATABASE_PORT="3306"
DATABASE_MAX_LIFETIME=7200
DATABASE_MAX_OPEN_CONNS=150
DATABASE_MAX_IDLE_CONNS=50
# Database SSL Mode is for postgres only (default: disable)
# disable | allow | prefer | require | verify-ca | verify-full
DATABASE_SSL_MODE="disable"

# Database Test Configuration

Expand All @@ -35,10 +38,6 @@ DATABASE_TEST_PORT="3306"
DATABASE_TEST_MAX_LIFETIME=7200
DATABASE_TEST_MAX_OPEN_CONNS=150
DATABASE_TEST_MAX_IDLE_CONNS=50

# Cloudinary Configuration

CLOUDINARY_CLOUD_NAME="your_cloud_name_here"
CLOUDINARY_API_KEY="your_api_key_here"
CLOUDINARY_API_SECRET="your_api_secret_here"
CLOUDINARY_UPLOAD_FOLDER="your_application_here"
# Database SSL Mode is for postgres only (default: disable)
# disable | allow | prefer | require | verify-ca | verify-full
DATABASE_TEST_SSL_MODE="disable"
17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
storage:
docker-compose -f docker-compose-mysql-storage.yml up -d
# Storage up if you don't have native database in your host
storage-up:
docker-compose -f docker-compose-pg-storage.yml up -d --remove-orphans

# Storage down to deactivate it
storage-down:
docker-compose -f docker-compose-pg-storage.yml down

# Storage test up (Storage that dedicated for testing)
storage-up-test:
docker-compose -f docker-compose-pg-storage-test.yml up -d --remove-orphans

# Storage down to deactivate storage for testing
storage-down-test:
docker-compose -f docker-compose-pg-storage-test.yml down

server:
go run main.go
23 changes: 0 additions & 23 deletions docker-compose-mysql-storage.yml

This file was deleted.

20 changes: 20 additions & 0 deletions docker-compose-pg-storage-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: '3.9'
services:
db:
container_name: golang-url-shortener-postgres-test
image: "postgres"
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
- PGDATA=/var/lib/postgresql/data/url_shortener/
ports:
# <Port exposed> : < Postgres Port running inside container>
- '5433:5432'
volumes:
- db:/var/lib/postgresql/data/test/url_shortener

volumes:
db:
driver: local
20 changes: 20 additions & 0 deletions docker-compose-pg-storage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: '3.9'
services:
db:
container_name: golang-url-shortener-postgres-standalone
image: "postgres"
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
- PGDATA=/var/lib/postgresql/data/url_shortener/
ports:
# <Port exposed> : < Postgres Port running inside container>
- '5433:5432'
volumes:
- db:/var/lib/postgresql/data/stand_alone/url_shortener

volumes:
db:
driver: local
20 changes: 10 additions & 10 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
version: '3.9'
services:
db:
container_name: golang_url_shortener_mysql_database
container_name: golang-url-shortener-postgres
image: "postgres"
restart: always
image: mysql:5.7
environment:
# Mysql Database
- MYSQL_DATABASE="${DATABASE_NAME}"
# Mysql Password
- MYSQL_ROOT_PASSWORD="${DATABASE_PASSWORD}"
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
- PGDATA=/var/lib/postgresql/data/url_shortener/
ports:
# <Port exposed> : < MySQL Port running inside container>
- '3306:3306'
volumes:
- my-db:/var/lib/mysql
# <Port exposed> : < Postgres Port running inside container>
- '5433:5432'
volumes:
- my-db:/var/lib/postgresql/data/composed/url_shortener
networks:
- default

Expand Down
4 changes: 2 additions & 2 deletions internal/api/handler/url.handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (handler *UrlHandler) AuthorizedUpdate(c *gin.Context) {
updateModel := &models.Url{}

// smapping the update request to models
updateModel.ID, _ = strconv.ParseUint(c.Param("id"), 10, 64)
updateModel.ID = helpers.ConvertStringtoUint(c.Param("id"))
smapping.FillStruct(updateModel, smapping.MapFields(&updateRequest))

urlRepo := handler.UrlRepo
Expand All @@ -194,7 +194,7 @@ func (handler *UrlHandler) AuthorizedUpdate(c *gin.Context) {
// Func to authorized Delete existed URL (and it's own of their respective user)
func (handler *UrlHandler) AuthorizedDelete(c *gin.Context) {
deleteModel := &models.Url{}
deleteModel.ID, _ = strconv.ParseUint(c.Param("id"), 10, 64)
deleteModel.ID = helpers.ConvertStringtoUint(c.Param("id"))

urlRepo := handler.UrlRepo

Expand Down
2 changes: 2 additions & 0 deletions internal/pkg/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type DatabaseConfiguration struct {
MaxLifetime int `mapstructure:"DATABASE_MAX_LIFETIME"`
MaxOpenConns int `mapstructure:"DATABASE_MAX_OPEN_CONNS"`
MaxIdleConns int `mapstructure:"DATABASE_MAX_IDLE_CONNS"`
SslMode string `mapstructure:"DATABASE_SSL_MODE"`
}

// Struct of Database for Testing Configuration instance
Expand All @@ -51,6 +52,7 @@ type DatabaseTestConfiguration struct {
MaxLifetime int `mapstructure:"DATABASE_TEST_MAX_LIFETIME"`
MaxOpenConns int `mapstructure:"DATABASE_TEST_MAX_OPEN_CONNS"`
MaxIdleConns int `mapstructure:"DATABASE_TEST_MAX_IDLE_CONNS"`
SslMode string `mapstructure:"DATABASE_TEST_SSL_MODE"`
}

// Setup the configuration
Expand Down
69 changes: 48 additions & 21 deletions internal/pkg/db/database.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package db

import (
"errors"
"fmt"
"time"

"github.com/adiatma85/golang-rest-template-api/internal/pkg/config"
"github.com/adiatma85/golang-rest-template-api/internal/pkg/db/seeders"
"github.com/adiatma85/golang-rest-template-api/internal/pkg/models"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
Expand All @@ -17,6 +19,11 @@ var (
err error
)

// Func to Get Database Instance
func GetDB() *gorm.DB {
return DB
}

// Database instance
type Database struct {
*gorm.DB
Expand All @@ -35,24 +42,16 @@ func SetupDB() {
password := configuration.Database.Password
host := configuration.Database.Host
port := configuration.Database.Port
sslMode := configuration.Database.SslMode

// Gorm config
gormConfig := &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
}

switch driver {
case "mysql":
db, err = gorm.Open(mysql.Open(username+":"+password+"@tcp("+host+":"+port+")/"+database+"?charset=utf8&parseTime=True&loc=Local"), gormConfig)
if err != nil {
fmt.Println("db err:", err)
}
case "postgres":
db, err = gorm.Open(postgres.Open("host="+host+" port="+port+" user="+username+" dbname="+database+" sslmode=disable password="+password), gormConfig)
if err != nil {
fmt.Println("db err:", err)
}
}
// Call for subroutine
db = loadDatabase(host, username, password, port, database, driver, sslMode, gormConfig)

// Set up the connection pools
sqlDb, _ := db.DB()
sqlDb.SetMaxIdleConns(configuration.Database.MaxIdleConns)
Expand All @@ -64,23 +63,51 @@ func SetupDB() {
}

// Setup for testing database
func SetupTestingDb(host, username, password, port, database string) {
// For the sake of simplicity, right now database testing is in mysql
db, err := gorm.Open(mysql.Open(username+":"+password+"@tcp("+host+":"+port+")/"+database+"?charset=utf8&parseTime=True&loc=Local"), &gorm.Config{})
if err != nil {
fmt.Println("db err for testing :", err)
panic(err.Error())
}
func SetupTestingDb(host, username, password, port, database, driver, sslMode string) {
var db = DB
// Zero Gorm Config
gormConfig := &gorm.Config{}
db = loadDatabase(host, username, password, port, database, driver, sslMode, gormConfig)

DB = db

migration()
}

// SubFunction
func loadDatabase(host, username, password, port, database, driver, sslMode string, gormConfig *gorm.Config) *gorm.DB {
var db *gorm.DB
switch driver {
case "mysql":
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, database)
db, err = gorm.Open(mysql.Open(dsn), gormConfig)
if err != nil {
fmt.Println("db err:", err)
}
case "postgres":
dsn := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=%s password=%s", host, port, username, database, sslMode, password)
db, err = gorm.Open(postgres.Open(dsn), gormConfig)
if err != nil {
fmt.Println("db err:", err)
}
}

return db
}

// AutoMigrate project models
func migration() {
DB.AutoMigrate(&models.Url{}, &models.User{})
// Seeding
seeding()
}

func GetDB() *gorm.DB {
return DB
// Seeding
func seeding() {
// Role Seeding
if err := DB.First(&models.Role{}).Error; errors.Is(err, gorm.ErrRecordNotFound) {
for _, role := range seeders.Roles {
DB.Create(&role)
}
}
}
16 changes: 16 additions & 0 deletions internal/pkg/db/seeders/role.seeder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package seeders

import (
"github.com/adiatma85/golang-rest-template-api/internal/pkg/constant"
"github.com/adiatma85/golang-rest-template-api/internal/pkg/models"
)

// Var Roles for seeder
var Roles []models.Role = []models.Role{
{
Name: constant.ADMINROLE,
},
{
Name: constant.USERROLE,
},
}
8 changes: 4 additions & 4 deletions internal/pkg/models/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (

// Struct that derivative from gorm.Model but customized
type Model struct {
ID uint64 `gorm:"column:id;primary_key;auto_increment;" json:"id"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null;" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index;column:deleted_at;type:datetime" json:"deleted_at"`
ID uint `gorm:"column:id;primary_key;auto_increment;" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index;column:deleted_at" json:"deleted_at"`
}
2 changes: 1 addition & 1 deletion internal/pkg/models/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type Url struct {
Model
OriginalUrl string `gorm:"type:varchar(255)" json:"url"`
ShortenUrl string `gorm:"type:varchar(255)" json:"shorten_url"`
UserId uint64 `gorm:"not null" json:"-" validation:"user_id"`
UserId uint `gorm:"not null" json:"-" validation:"user_id"`
User User `gorm:"foreignkey:UserId;constraint:onUpdate:CASCADE,onDelete:CASCADE" json:"user"`
}

Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type User struct {
Name string `gorm:"type:varchar(100)" json:"name" validation:"name"`
Email string `gorm:"type:varchar(100);unique;" json:"email" validation:"email"`
Password string `gorm:"type:varchar(100)" json:"-" validation:"password"`
RoleId uint64 `gorm:"not null" json:"-" validation:"role_id"`
RoleId uint `gorm:"not null" json:"-" validation:"role_id"`
Role Role `gorm:"foreignkey:RoleId;constraint:onUpdate:CASCADE,onDelete:CASCADE" json:"role"`
}

Expand Down
4 changes: 1 addition & 3 deletions internal/pkg/repository/url.repository.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package repository

import (
"strconv"

"github.com/adiatma85/golang-rest-template-api/internal/pkg/db"
"github.com/adiatma85/golang-rest-template-api/internal/pkg/models"
"github.com/adiatma85/golang-rest-template-api/pkg/helpers"
Expand Down Expand Up @@ -83,7 +81,7 @@ func (repo *UrlRepository) GetByShortenUniqueId(uniqueId string) (*models.Url, e
func (repo *UrlRepository) GetById(urlId string) (*models.Url, error) {
var url models.Url
where := models.Url{}
where.ID, _ = strconv.ParseUint(urlId, 10, 64)
where.ID = helpers.ConvertStringtoUint(urlId)
_, err := First(&where, &url, []string{"User"})
if err != nil {
return nil, err
Expand Down
4 changes: 1 addition & 3 deletions internal/pkg/repository/user.repository.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package repository

import (
"strconv"

"github.com/adiatma85/golang-rest-template-api/internal/pkg/models"
"github.com/adiatma85/golang-rest-template-api/pkg/crypto"
"github.com/adiatma85/golang-rest-template-api/pkg/helpers"
Expand Down Expand Up @@ -84,7 +82,7 @@ func (repo *UserRepository) GetByEmail(email string) (*models.User, error) {
func (repo *UserRepository) GetById(userId string) (*models.User, error) {
var user models.User
where := models.User{}
where.ID, _ = strconv.ParseUint(userId, 10, 64)
where.ID = helpers.ConvertStringtoUint(userId)
_, err := First(&where, &user, []string{"Role"})
if err != nil {
return nil, err
Expand Down
7 changes: 7 additions & 0 deletions pkg/helpers/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package helpers

import (
"math/rand"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -47,3 +48,9 @@ func ExtractUserFromClaim(c *gin.Context) *models.User {
mapstructure.Decode(user, &exstingUser)
return &exstingUser
}

// Helper to change string to uint
func ConvertStringtoUint(id string) uint {
typeUint64, _ := strconv.ParseUint(id, 10, 64)
return uint(typeUint64)
}