Added GetProduct, GetProducts, UpdateProduct, DeleteProduct
parent
ca00c36e25
commit
2bbef5ad4e
@ -1,4 +1,8 @@
|
|||||||
dist
|
# build, generated files
|
||||||
.env
|
dist
|
||||||
.air.toml
|
tmp/
|
||||||
tmp/
|
.air.toml
|
||||||
|
|
||||||
|
|
||||||
|
# environment variable files
|
||||||
|
app.env
|
@ -1,2 +1,2 @@
|
|||||||
dev:
|
dev:
|
||||||
gin --appPort 5432 -i run main.go
|
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
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
PostgreDriver string `mapstructure:"POSTGRES_DRIVER"`
|
PostgresDriver string `mapstructure:"POSTGRES_DRIVER"`
|
||||||
PostgresSource string `mapstructure:"POSTGRES_SOURCE"`
|
PostgresSource string `mapstructure:"POSTGRES_SOURCE"`
|
||||||
PostgresPort string `mapstructure:"POSTGRES_PORT"`
|
PostgresPort string `mapstructure:"POSTGRES_PORT"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(path string) (config Config, err error) {
|
func LoadConfig(path string) (config Config, err error) {
|
||||||
viper.AddConfigPath(path)
|
viper.AddConfigPath(path)
|
||||||
viper.SetConfigType("env")
|
viper.SetConfigType("env")
|
||||||
viper.SetConfigName("app")
|
viper.SetConfigName("app")
|
||||||
|
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
err = viper.ReadInConfig()
|
err = viper.ReadInConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = viper.Unmarshal(&config)
|
err = viper.Unmarshal(&config)
|
||||||
return
|
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'
|
version: '3.9'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres
|
image: postgres
|
||||||
ports:
|
ports:
|
||||||
- '5432:5432'
|
- '5432:5432'
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
volumes:
|
volumes:
|
||||||
- 'postgres:/var/lib/postgresql/data'
|
- 'postgres:/var/lib/postgresql/data'
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres:
|
postgres:
|
@ -1,53 +1,39 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"net/http"
|
||||||
"log"
|
|
||||||
|
"git.sp4ke.xyz/AnisB/shelf_api_go/controllers"
|
||||||
"git.sp4ke.xyz/AnisB/shelf_api_go/config"
|
"git.sp4ke.xyz/AnisB/shelf_api_go/db_client"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jmoiron/sqlx"
|
)
|
||||||
|
|
||||||
_ "github.com/lib/pq"
|
func main() {
|
||||||
)
|
|
||||||
|
db_client.InitialiseDBConnection()
|
||||||
var (
|
|
||||||
server *gin.Engine
|
r := gin.Default()
|
||||||
db *sqlx.DB
|
|
||||||
)
|
r.GET("/", func(ctx *gin.Context) {
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{"status": "success", "message": "Welcome to Golang with PostgreSQL"})
|
||||||
func main() {
|
})
|
||||||
config, err := config.LoadConfig(".")
|
|
||||||
|
// Products
|
||||||
if err != nil {
|
r.GET("/products", controllers.GetProducts)
|
||||||
log.Fatalf("could not load config: %v", err)
|
r.GET("/products/:id", controllers.GetProduct)
|
||||||
}
|
r.POST("/products", controllers.CreateProduct)
|
||||||
|
r.PUT("products/:id", controllers.UpdateProduct)
|
||||||
fmt.Printf("%T\n", config)
|
r.DELETE("products/:id", controllers.DeleteProduct)
|
||||||
|
|
||||||
db, err := sqlx.Open(config.PostgreDriver, config.PostgresSource)
|
if err := r.Run(":5000"); err != nil {
|
||||||
|
panic(err.Error())
|
||||||
if err != nil {
|
}
|
||||||
log.Fatalf("could not connect to database: %v", err)
|
|
||||||
}
|
/*router := server.Group("/api")
|
||||||
|
|
||||||
err = db.Ping()
|
router.GET("/healthchecker", func(ctx *gin.Context) {
|
||||||
fmt.Printf("Ping: %v\n", db.Ping())
|
ctx.JSON(http.StatusOK, gin.H{"status": "success", "message": "Welcome to Golang with PostgreSQL"})
|
||||||
if err != nil {
|
})
|
||||||
log.Fatalf("could not ping database: %v", err)
|
|
||||||
}
|
log.Fatal(server.Run(":" + config.PostgresPort))*/
|
||||||
|
}
|
||||||
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))*/
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
CREATE TABLE IF NOT EXISTS products (
|
CREATE TABLE IF NOT EXISTS products (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name VARCHAR(250) NOT NULL,
|
name VARCHAR(250) NOT NULL,
|
||||||
price INTEGER 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…
Reference in New Issue