Added GetProduct, GetProducts, UpdateProduct, DeleteProduct

Vic
Anis Benziane 2 years ago
parent ca00c36e25
commit 2bbef5ad4e

12
.gitignore vendored

@ -1,4 +1,8 @@
dist
.env
.air.toml
tmp/
# build, generated files
dist
tmp/
.air.toml
# environment variable files
app.env

@ -1,2 +1,2 @@
dev:
dev:
gin --appPort 5432 -i run main.go

@ -1,15 +0,0 @@
POSTGRES_HOST=127.0.0.1
POSTGRES_DB=shelf
POSTGRES_TEST_DB=shelf_test
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_PORT=5432
ENV=dev
POSTGRES_DRIVER=postgres
POSTGRES_SOURCE=postgresql://postgres:postgres@localhost:5432/shelf?sslmode=disable
BCRYPT_PASSWORD=speak-friend-and-enter
SALT_ROUNDS=10
TOKEN_SECRET=alohomora123!
TOKEN_SECRET_TEST=eyJhbGciOiJIUzI1NiIsInR

@ -1,27 +1,27 @@
package config
import (
"github.com/spf13/viper"
)
type Config struct {
PostgreDriver string `mapstructure:"POSTGRES_DRIVER"`
PostgresSource string `mapstructure:"POSTGRES_SOURCE"`
PostgresPort string `mapstructure:"POSTGRES_PORT"`
}
func LoadConfig(path string) (config Config, err error) {
viper.AddConfigPath(path)
viper.SetConfigType("env")
viper.SetConfigName("app")
viper.AutomaticEnv()
err = viper.ReadInConfig()
if err != nil {
return
}
err = viper.Unmarshal(&config)
return
}
package config
import (
"github.com/spf13/viper"
)
type Config struct {
PostgresDriver string `mapstructure:"POSTGRES_DRIVER"`
PostgresSource string `mapstructure:"POSTGRES_SOURCE"`
PostgresPort string `mapstructure:"POSTGRES_PORT"`
}
func LoadConfig(path string) (config Config, err error) {
viper.AddConfigPath(path)
viper.SetConfigType("env")
viper.SetConfigName("app")
viper.AutomaticEnv()
err = viper.ReadInConfig()
if err != nil {
return
}
err = viper.Unmarshal(&config)
return
}

@ -0,0 +1,145 @@
package controllers
import (
"database/sql"
"net/http"
"strconv"
"git.sp4ke.xyz/AnisB/shelf_api_go/db_client"
"github.com/gin-gonic/gin"
)
type Product struct {
Id int64 `db:"id"`
Name string `db:"name"`
Price float64 `db:"price"`
}
// SQL
// row := db_client.ConnectDB.QueryRow("", id)
// SQLX
// db_client.ConnectDB.Get() / SINGLE ROW
// db_client.ConnectDB.Select() / SLICE OF ROWS ( query )
func GetProducts(c *gin.Context) {
var products []Product
err := db_client.ConnectDB.Select(&products, "SELECT * FROM products")
if err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{
"error": true,
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, products)
}
func GetProduct(c *gin.Context) {
idStr := c.Param("id")
id, _ := strconv.Atoi(idStr)
var product Product
err := db_client.ConnectDB.Get(&product, "SELECT * FROM products WHERE id=($1)", id)
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, gin.H{
"error": true,
"message": "Product not found",
})
return
}
c.JSON(http.StatusOK, product)
}
func CreateProduct(c *gin.Context) {
var reqBody Product
if err := c.ShouldBindJSON(&reqBody); err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{
"error": true,
"message": "Invalid request body",
})
return
}
res, err := db_client.ConnectDB.Exec("INSERT INTO products (name, price) VALUES ($1, $2)",
reqBody.Name,
reqBody.Price)
// Tow key methods we get form res
// res.LastInsertId
// res.RowsAffected
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": true,
"message": "Could Create Product",
})
return
}
id, _ := res.LastInsertId()
c.JSON(http.StatusCreated, gin.H{
"error": false,
"id": id,
"message": "New product created",
})
}
func UpdateProduct(c *gin.Context) {
idStr := c.Param("id")
id, _ := strconv.Atoi(idStr)
var reqBody Product
if err := c.ShouldBindJSON(&reqBody); err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{
"error": true,
"message": "Invalid request body",
})
return
}
res, err := db_client.ConnectDB.Exec("UPDATE products SET name=$1, price=$2 WHERE id=$3 RETURNING *", reqBody.Name, reqBody.Price, id)
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, gin.H{
"error": true,
"message": "Product not found",
})
return
}
AffectedId, _ := res.RowsAffected()
c.JSON(http.StatusCreated, gin.H{
"error": false,
"id": AffectedId,
"message": "Product have been updated successfully",
})
}
func DeleteProduct(c *gin.Context) {
idStr := c.Param("id")
id, _ := strconv.Atoi(idStr)
res, err := db_client.ConnectDB.Exec("DELETE FROM products WHERE id=($1)", id)
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, gin.H{
"error": true,
"message": "Product not found",
})
return
}
AffectedId, _ := res.RowsAffected()
c.JSON(http.StatusCreated, gin.H{
"error": false,
"id": AffectedId,
"message": "Product have been deleted successfully",
})
}

@ -0,0 +1 @@
package controllers

Binary file not shown.

@ -0,0 +1,32 @@
package db_client
import (
"log"
"git.sp4ke.xyz/AnisB/shelf_api_go/config"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
)
var ConnectDB *sqlx.DB
func InitialiseDBConnection() {
config, err := config.LoadConfig(".")
if err != nil {
log.Fatalf("could not load config: %v\n", err)
}
db, err := sqlx.Open(config.PostgresDriver, config.PostgresSource)
if err != nil {
log.Fatalf("could not connect to database: %v\n", err)
}
err = db.Ping()
if err != nil {
log.Fatalf("could not ping database: %v\n", err)
}
ConnectDB = db
}

@ -1,14 +1,14 @@
version: '3.9'
services:
postgres:
image: postgres
ports:
- '5432:5432'
env_file:
- .env
volumes:
- 'postgres:/var/lib/postgresql/data'
volumes:
version: '3.9'
services:
postgres:
image: postgres
ports:
- '5432:5432'
env_file:
- .env
volumes:
- 'postgres:/var/lib/postgresql/data'
volumes:
postgres:

@ -4,6 +4,8 @@ go 1.19
require (
github.com/gin-gonic/gin v1.8.1
github.com/jmoiron/sqlx v1.3.5
github.com/lib/pq v1.10.6
github.com/spf13/viper v1.12.0
)
@ -15,10 +17,8 @@ require (
github.com/go-playground/validator/v10 v10.11.0 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lib/pq v1.10.6 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
@ -32,7 +32,7 @@ require (
github.com/spf13/pflag v1.0.5 // 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-20220826181053-bd7e27e6170d // indirect
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
golang.org/x/text v0.3.7 // indirect

@ -74,6 +74,7 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
@ -166,6 +167,7 @@ github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamh
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@ -232,8 +234,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=

@ -1,53 +1,39 @@
package main
import (
"fmt"
"log"
"git.sp4ke.xyz/AnisB/shelf_api_go/config"
"github.com/gin-gonic/gin"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
)
var (
server *gin.Engine
db *sqlx.DB
)
func main() {
config, err := config.LoadConfig(".")
if err != nil {
log.Fatalf("could not load config: %v", err)
}
fmt.Printf("%T\n", config)
db, err := sqlx.Open(config.PostgreDriver, config.PostgresSource)
if err != nil {
log.Fatalf("could not connect to database: %v", err)
}
err = db.Ping()
fmt.Printf("Ping: %v\n", db.Ping())
if err != nil {
log.Fatalf("could not ping database: %v", err)
}
r := gin.Default()
if err := r.Run(":5000"); err != nil {
panic(err.Error())
}
/*router := server.Group("/api")
router.GET("/healthchecker", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"status": "success", "message": "Welcome to Golang with PostgreSQL"})
})
log.Fatal(server.Run(":" + config.PostgresPort))*/
}
package main
import (
"net/http"
"git.sp4ke.xyz/AnisB/shelf_api_go/controllers"
"git.sp4ke.xyz/AnisB/shelf_api_go/db_client"
"github.com/gin-gonic/gin"
)
func main() {
db_client.InitialiseDBConnection()
r := gin.Default()
r.GET("/", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"status": "success", "message": "Welcome to Golang with PostgreSQL"})
})
// Products
r.GET("/products", controllers.GetProducts)
r.GET("/products/:id", controllers.GetProduct)
r.POST("/products", controllers.CreateProduct)
r.PUT("products/:id", controllers.UpdateProduct)
r.DELETE("products/:id", controllers.DeleteProduct)
if err := r.Run(":5000"); err != nil {
panic(err.Error())
}
/*router := server.Group("/api")
router.GET("/healthchecker", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"status": "success", "message": "Welcome to Golang with PostgreSQL"})
})
log.Fatal(server.Run(":" + config.PostgresPort))*/
}

@ -1,5 +1,5 @@
CREATE TABLE IF NOT EXISTS products (
id SERIAL PRIMARY KEY,
name VARCHAR(250) NOT NULL,
price INTEGER NOT NULL
CREATE TABLE IF NOT EXISTS products (
id SERIAL PRIMARY KEY,
name VARCHAR(250) NOT NULL,
price INTEGER NOT NULL
);

@ -1,33 +0,0 @@
package main
import (
"database/sql"
"errors"
)
type product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
}
func getProducts(db *sql.DB, start, count int) ([]product, error) {
return nil, errors.New("Not implemented")
}
func (p *product) getProduct(db *sql.DB) error {
return errors.New("Not implemented")
}
func (p *product) updateProduct(db *sql.DB) error {
return errors.New("Not implemented")
}
func (p *product) deleteProduct(db *sql.DB) error {
return errors.New("Not implemented")
}
func (p *product) createProduct(db *sql.DB) error {
return errors.New("Not implemented")
}
Loading…
Cancel
Save