Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
pratama.sumirat committed Aug 24, 2022
1 parent e503ff7 commit 178ad3b
Show file tree
Hide file tree
Showing 18 changed files with 972 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@

# Dependency directories (remove the comment below to include it)
# vendor/

.env
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/go-gin-gorm-books.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
server:
go run cmd/main.go
32 changes: 32 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"net/http"

"github.com/sumirart/go-gin-gorm-books/pkg/books"
"github.com/sumirart/go-gin-gorm-books/pkg/common/db"
)

func main() {
viper.SetConfigFile("./pkg/common/envs/.env")
viper.ReadInConfig()

port := viper.Get("PORT").(string)
dbURL := viper.Get("DB_URL").(string)

router := gin.Default()
dbHandler := db.Init(dbURL)

books.RegisterRoutes(router, dbHandler)

router.GET("/", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"port": port,
"dbURL": dbURL,
})
})

router.Run(port)
}
50 changes: 50 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module github.com/sumirart/go-gin-gorm-books

go 1.18

require (
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.8.1 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect
github.com/goccy/go-json v0.9.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.12.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.11.0 // indirect
github.com/jackc/pgx/v4 v4.16.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.12.0 // indirect
github.com/subosito/gotenv v1.3.0 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
gorm.io/driver/postgres v1.3.9 // indirect
gorm.io/gorm v1.23.8 // indirect
)
643 changes: 643 additions & 0 deletions go.sum

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions pkg/books/add_book.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package books

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/sumirart/go-gin-gorm-books/pkg/common/models"
)

type AddBookRequestBody struct {
Title string `json:"title" binding:"required"`
Author string `json:"author" binding:"required"`
Description string `json:"description" binding:"required"`
}

func (h handler) AddBook(ctx *gin.Context) {
body := AddBookRequestBody{}

if err := ctx.BindJSON(&body); err != nil {
ctx.AbortWithError(http.StatusBadRequest, err)
return
}

var book models.Book

book.Title = body.Title
book.Author = body.Author
book.Description = body.Description

if result := h.DB.Create(&book); result.Error != nil {
ctx.AbortWithError(http.StatusNotFound, result.Error)
return
}

ctx.JSON(http.StatusCreated, &book)
}
23 changes: 23 additions & 0 deletions pkg/books/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package books

import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)

type handler struct {
DB *gorm.DB
}

func RegisterRoutes(router *gin.Engine, db *gorm.DB) {
h := &handler{
DB: db,
}

routes := router.Group("/api/v1/books")
routes.POST("", h.AddBook)
routes.GET("", h.GetBooks)
routes.GET("/:id", h.GetBook)
routes.PUT("/:id", h.UpdateBook)
routes.DELETE("/:id", h.DeleteBook)
}
23 changes: 23 additions & 0 deletions pkg/books/delete_book.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package books

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/sumirart/go-gin-gorm-books/pkg/common/models"
)

func (h handler) DeleteBook(ctx *gin.Context) {
id := ctx.Param("id")

var book models.Book

if result := h.DB.First(&book, id); result.Error != nil {
ctx.AbortWithError(http.StatusNotFound, result.Error)
return
}

h.DB.Delete(&book)

ctx.Status(http.StatusNoContent)
}
22 changes: 22 additions & 0 deletions pkg/books/get_book.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package books

import (
"github.com/gin-gonic/gin"
"github.com/sumirart/go-gin-gorm-books/pkg/common/models"
"net/http"
)

func (h handler) GetBook(ctx *gin.Context) {
id := ctx.Param("id")

var book models.Book

// Jo said he rarely found something like this in Go source code
// Usually (in Go source code), create and fedine result and error first, then do if error..
if result := h.DB.First(&book, id); result.Error != nil {
ctx.AbortWithError(http.StatusNotFound, result.Error)
return
}

ctx.JSON(http.StatusOK, &book)
}
19 changes: 19 additions & 0 deletions pkg/books/get_books.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package books

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/sumirart/go-gin-gorm-books/pkg/common/models"
)

func (h handler) GetBooks(ctx *gin.Context) {
var books []models.Book

if result := h.DB.Find(&books); result.Error != nil {
ctx.AbortWithError(http.StatusNotFound, result.Error)
return
}

ctx.JSON(http.StatusOK, &books)
}
37 changes: 37 additions & 0 deletions pkg/books/update_book.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package books

import (
"github.com/gin-gonic/gin"
"github.com/sumirart/go-gin-gorm-books/pkg/common/models"
"net/http"
)

type UpdateBookRequestBody struct {
Title string `json:"title" binding:"required"`
Author string `json:"author" binding:"required"`
Description string `json:"description" binding:"required"`
}

func (h handler) UpdateBook(ctx *gin.Context) {
id := ctx.Param("id")
body := UpdateBookRequestBody{}

if err := ctx.BindJSON(&body); err != nil {
ctx.AbortWithError(http.StatusBadRequest, err)
return
}

var book models.Book

if result := h.DB.First(&book, id); result.Error != nil {
ctx.AbortWithError(http.StatusNotFound, result.Error)
return
}

book.Title = body.Title
book.Author = body.Author
book.Description = body.Description

h.DB.Save(&book)
ctx.JSON(http.StatusOK, &book)
}
25 changes: 25 additions & 0 deletions pkg/common/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package config

import "github.com/spf13/viper"

type Config struct {
Port string `mapstructure:"PORT"`
DBUrl string `mapstructure:"DB_URL"`
}

func LoadConfig() (c Config, err error) {
viper.AddConfigPath("./pkg/common/config/envs")
viper.SetConfigName("dev")
viper.SetConfigType("env")

viper.AutomaticEnv()

err = viper.ReadInConfig()

if err != nil {
return
}

err = viper.Unmarshal(&c)
return
}
20 changes: 20 additions & 0 deletions pkg/common/db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package db

import (
"log"

"github.com/sumirart/go-gin-gorm-books/pkg/common/models"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)

func Init(url string) *gorm.DB {
db, err := gorm.Open(postgres.Open(url), &gorm.Config{})
if err != nil {
log.Fatalln("Error opening database", err)
}

db.AutoMigrate(&models.Book{})

return db
}
3 changes: 3 additions & 0 deletions pkg/common/envs/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PORT=:3000
DB_URL=postgres://DB_USER:DB_PASSWORD@DB_SERVER:DB_PORT/DB_NAME
API_KEY=YOUR_API_KEY
10 changes: 10 additions & 0 deletions pkg/common/models/book.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package models

import "gorm.io/gorm"

type Book struct {
gorm.Model
Title string `json:"title"`
Author string `json:"author"`
Description string `json:"description"`
}

0 comments on commit 178ad3b

Please sign in to comment.