server
This commit is contained in:
parent
05d9046e15
commit
b5f71890a6
@ -1,84 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"web-service-gin/models"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func FindAlbums(c *gin.Context) {
|
||||
var albums []models.Album
|
||||
models.DB.Find(&albums)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": albums})
|
||||
}
|
||||
|
||||
func FindAlbum(c *gin.Context) {
|
||||
var album models.Album
|
||||
|
||||
if err := models.DB.Where("id = ?", c.Param("id")).First(&album).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": album})
|
||||
}
|
||||
|
||||
func CreateAlbum(c *gin.Context) {
|
||||
var input models.CreateAlbumInput
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var price float64
|
||||
if f, err := strconv.ParseFloat(input.Price, 64); err == nil {
|
||||
price = f
|
||||
}
|
||||
|
||||
album := models.Album{
|
||||
Title: input.Title,
|
||||
Artist: input.Artist,
|
||||
Price: price,
|
||||
}
|
||||
models.DB.Create(&album)
|
||||
c.JSON(http.StatusOK, gin.H{"data": album})
|
||||
}
|
||||
|
||||
func UpdateAlbum(c *gin.Context) {
|
||||
var album models.Album
|
||||
if err := models.DB.Where("id = ?", c.Param("id")).First(&album).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
|
||||
}
|
||||
|
||||
var input models.UpdateAlbumInput
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
models.DB.Model(&album).Updates(input)
|
||||
c.JSON(http.StatusOK, gin.H{"data": album})
|
||||
}
|
||||
|
||||
func DeleteAlbum(c *gin.Context) {
|
||||
var album models.Album
|
||||
if err := models.DB.Where("id = ?", c.Param("id")).First(&album).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
|
||||
return
|
||||
}
|
||||
|
||||
models.DB.Delete(&album)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": true})
|
||||
}
|
||||
|
||||
func ApplyAlbumRouter(router *gin.Engine) *gin.Engine {
|
||||
router.GET("/albums", FindAlbums)
|
||||
router.POST("/albums", CreateAlbum)
|
||||
router.GET("/albums/:id", FindAlbum)
|
||||
router.PATCH("/albums/:id", UpdateAlbum)
|
||||
router.DELETE("/albums/:id", DeleteAlbum)
|
||||
return router
|
||||
}
|
8
go.mod
8
go.mod
@ -4,8 +4,9 @@ go 1.19
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
gorm.io/driver/mysql v1.4.1
|
||||
gorm.io/gorm v1.24.0
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/guregu/null v4.0.0+incompatible
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
)
|
||||
|
||||
require (
|
||||
@ -13,10 +14,7 @@ require (
|
||||
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/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
|
18
go.sum
18
go.sum
@ -22,11 +22,10 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/guregu/null v4.0.0+incompatible h1:4zw0ckM7ECd6FNNddc3Fu4aty9nTlpkkzH7dPn4/4Gw=
|
||||
github.com/guregu/null v4.0.0+incompatible/go.mod h1:ePGpQaN9cw0tj45IR5E5ehMvsFlLlQZAkkOXZurJ3NM=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@ -39,8 +38,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
@ -91,8 +94,3 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.4.1 h1:4InA6SOaYtt4yYpV1NF9B2kvUKe9TbvUd1iWrvxnjic=
|
||||
gorm.io/driver/mysql v1.4.1/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.24.0 h1:j/CoiSm6xpRpmzbFJsQHYj+I8bGYWLXVHeYEyyKlF74=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
|
177
internal/controllers/album.go
Normal file
177
internal/controllers/album.go
Normal file
@ -0,0 +1,177 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"net/http"
|
||||
"web-service-gin/internal/forms"
|
||||
models2 "web-service-gin/internal/models"
|
||||
)
|
||||
|
||||
// Album provides the handlers for the album entity.
|
||||
type Album struct {
|
||||
albumService models2.AlbumService
|
||||
}
|
||||
|
||||
// NewAlbum creates the controller using the given data mapper for
|
||||
// albums.
|
||||
func NewAlbum(albumService models2.AlbumService) *Album {
|
||||
return &Album{
|
||||
albumService: albumService,
|
||||
}
|
||||
}
|
||||
|
||||
// Post will create a new album from the given data, if the form is valid.
|
||||
func (p *Album) Post(c *gin.Context) {
|
||||
var form forms.CreateAlbum
|
||||
if c.ShouldBindWith(&form, binding.JSON) != nil {
|
||||
// TODO: Give a better error message.
|
||||
c.JSON(
|
||||
http.StatusNotAcceptable,
|
||||
gin.H{"message": "invalid data."},
|
||||
)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
album, err := p.albumService.Create(&form)
|
||||
if err != nil {
|
||||
// TODO: An error middleware should log the error,
|
||||
// and email admin.
|
||||
c.Error(err)
|
||||
c.JSON(
|
||||
http.StatusInternalServerError,
|
||||
gin.H{"message": "internal error"},
|
||||
)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: use a view if part of the album data should not be
|
||||
// returned to the client.
|
||||
c.JSON(
|
||||
http.StatusCreated,
|
||||
album,
|
||||
)
|
||||
}
|
||||
|
||||
// Put will perform an update of a album.
|
||||
func (p *Album) Put(c *gin.Context) {
|
||||
var form forms.CreateAlbum
|
||||
if err := c.ShouldBindWith(&form, binding.JSON); err != nil {
|
||||
// TODO: Give a better error message.
|
||||
c.JSON(
|
||||
http.StatusNotAcceptable,
|
||||
gin.H{
|
||||
"message": "invalid data.",
|
||||
"form": form,
|
||||
"error": err.Error(),
|
||||
},
|
||||
)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
id := c.Param("id")
|
||||
|
||||
album, err := p.albumService.GetByID(id)
|
||||
if err == models2.ErrNotFound {
|
||||
c.JSON(
|
||||
http.StatusNotFound,
|
||||
gin.H{"message": "user not found"},
|
||||
)
|
||||
c.Abort()
|
||||
return
|
||||
} else if err != nil {
|
||||
c.Error(err)
|
||||
c.JSON(
|
||||
http.StatusInternalServerError,
|
||||
gin.H{"message": "internal error."},
|
||||
)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
album.ApplyForm(&form)
|
||||
err = p.albumService.Update(album)
|
||||
if err != nil {
|
||||
c.Error(err)
|
||||
c.JSON(
|
||||
http.StatusInternalServerError,
|
||||
gin.H{"message": "internal error."},
|
||||
)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(
|
||||
http.StatusOK,
|
||||
gin.H{"message": "updated"},
|
||||
)
|
||||
}
|
||||
|
||||
// Get will fetch an album by ID.
|
||||
func (p *Album) Get(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
album, err := p.albumService.GetByID(id)
|
||||
if err == models2.ErrNotFound {
|
||||
c.JSON(
|
||||
http.StatusNotFound,
|
||||
gin.H{"message": "user not found"},
|
||||
)
|
||||
c.Abort()
|
||||
return
|
||||
} else if err != nil {
|
||||
c.Error(err)
|
||||
c.JSON(
|
||||
http.StatusInternalServerError,
|
||||
gin.H{"message": "internal error."},
|
||||
)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(
|
||||
http.StatusOK,
|
||||
album,
|
||||
)
|
||||
}
|
||||
|
||||
// GetAll will fetch all Albums.
|
||||
// TODO: Pagination
|
||||
func (p *Album) GetAll(c *gin.Context) {
|
||||
albums, err := p.albumService.GetAll()
|
||||
if err != nil {
|
||||
c.Error(err)
|
||||
c.JSON(
|
||||
http.StatusInternalServerError,
|
||||
gin.H{"message": "internal error."},
|
||||
)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.JSON(
|
||||
http.StatusOK,
|
||||
albums,
|
||||
)
|
||||
}
|
||||
|
||||
// Delete will remove a album from the DB.
|
||||
func (p *Album) Delete(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
err := p.albumService.Delete(id)
|
||||
|
||||
if err != nil {
|
||||
c.Error(err)
|
||||
c.JSON(
|
||||
http.StatusInternalServerError,
|
||||
gin.H{"message": "internal error."},
|
||||
)
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
c.JSON(
|
||||
http.StatusOK,
|
||||
gin.H{"message": "deleted"},
|
||||
)
|
||||
}
|
12
internal/forms/album.go
Normal file
12
internal/forms/album.go
Normal file
@ -0,0 +1,12 @@
|
||||
package forms
|
||||
|
||||
type CreateAlbum struct {
|
||||
ID *int64 `form:"id" json:"id" binding:"required"`
|
||||
Title *string `form:"title" json:"title" binding:"required"`
|
||||
Artist *string `form:"artist" json:"artist" binding:"required"`
|
||||
Price *float64 `form:"price" json:"price" binding:"required"`
|
||||
|
||||
CreatedAt *string `form:"created_at" json:"created_at"`
|
||||
UpdatedAt *string `form:"updated_at" json:"updated_at"`
|
||||
DeletedAt *string `form:"deleted_at" json:"deleted_at"`
|
||||
}
|
34
internal/models/album.go
Normal file
34
internal/models/album.go
Normal file
@ -0,0 +1,34 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/guregu/null"
|
||||
"web-service-gin/internal/forms"
|
||||
)
|
||||
|
||||
type Album struct {
|
||||
ID int64 `db:"id"`
|
||||
CreatedAt null.String `db:"created_at"`
|
||||
UpdatedAt null.String `db:"updated_at"`
|
||||
DeletedAt null.String `db:"deleted_at"`
|
||||
Title string `db:"title"`
|
||||
Artist string `db:"artist"`
|
||||
Price float64 `db:"price"`
|
||||
}
|
||||
|
||||
func (a *Album) ApplyForm(form *forms.CreateAlbum) {
|
||||
a.ID = *form.ID
|
||||
a.Title = *form.Title
|
||||
a.Artist = *form.Artist
|
||||
a.Price = *form.Price
|
||||
a.CreatedAt = null.StringFromPtr(form.CreatedAt)
|
||||
a.UpdatedAt = null.StringFromPtr(form.UpdatedAt)
|
||||
a.DeletedAt = null.StringFromPtr(form.DeletedAt)
|
||||
}
|
||||
|
||||
type AlbumService interface {
|
||||
Create(*forms.CreateAlbum) (*Album, error)
|
||||
GetByID(string) (*Album, error)
|
||||
GetAll() (*[]Album, error)
|
||||
Update(*Album) error
|
||||
Delete(string) error
|
||||
}
|
5
internal/models/errors.go
Normal file
5
internal/models/errors.go
Normal file
@ -0,0 +1,5 @@
|
||||
package models
|
||||
|
||||
import "errors"
|
||||
|
||||
var ErrNotFound = errors.New("object not found")
|
148
internal/models/sql/album.go
Normal file
148
internal/models/sql/album.go
Normal file
@ -0,0 +1,148 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"web-service-gin/internal/forms"
|
||||
models2 "web-service-gin/internal/models"
|
||||
)
|
||||
|
||||
// AlbumService is the implementation of the album data mapping layer
|
||||
// using SQL.
|
||||
type AlbumService struct {
|
||||
conn *sqlx.DB
|
||||
}
|
||||
|
||||
// NewAlbumService creates the album service using the given
|
||||
// connection pool to a mysql DB.
|
||||
func NewAlbumService(conn *sqlx.DB) (*AlbumService, error) {
|
||||
// TODO: It would be better to use a DB management tool
|
||||
// to make migrations painless.
|
||||
|
||||
// _, err := conn.Exec(`
|
||||
//CREATE TABLE albums (
|
||||
// id bigint NOT NULL AUTO_INCREMENT,
|
||||
// title longtext,
|
||||
// artist longtext,
|
||||
// price double DEFAULT NULL,
|
||||
// PRIMARY KEY (id),
|
||||
// KEY idx_albums_deleted_at (deleted_at)
|
||||
//) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
|
||||
//`)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
return &AlbumService{conn: conn}, nil
|
||||
}
|
||||
|
||||
// Create will try to add the album to the DB.
|
||||
func (s *AlbumService) Create(form *forms.CreateAlbum) (*models2.Album, error) {
|
||||
q := `
|
||||
INSERT INTO albums(title, artist, price)
|
||||
VALUES (?, ?, ?)
|
||||
RETURNING *;`
|
||||
|
||||
var output models2.Album
|
||||
err := s.conn.Get(
|
||||
&output,
|
||||
q,
|
||||
*form.Title,
|
||||
*form.Artist,
|
||||
*form.Price,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &output, nil
|
||||
}
|
||||
|
||||
// Update will replace the values of the give album with those provided.
|
||||
func (s *AlbumService) Update(p *models2.Album) error {
|
||||
q := `
|
||||
UPDATE albums
|
||||
SET updated_at = NOW(),
|
||||
title = ?,
|
||||
artist = ?,
|
||||
price = ?
|
||||
WHERE id = ?;
|
||||
`
|
||||
|
||||
_, err := s.conn.Exec(
|
||||
q,
|
||||
p.Title,
|
||||
p.Artist,
|
||||
p.Price,
|
||||
p.ID,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByID fetches the album with the given id.
|
||||
func (s *AlbumService) GetByID(id string) (*models2.Album, error) {
|
||||
if !validID(id) {
|
||||
return nil, models2.ErrNotFound
|
||||
}
|
||||
|
||||
q := `
|
||||
SELECT *
|
||||
FROM albums
|
||||
WHERE id = ?;`
|
||||
|
||||
var output models2.Album
|
||||
err := s.conn.Get(
|
||||
&output,
|
||||
q,
|
||||
id,
|
||||
)
|
||||
// Replace the SQL error with our own error type.
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, models2.ErrNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &output, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetAll fetches all albums.
|
||||
func (s *AlbumService) GetAll() (*[]models2.Album, error) {
|
||||
q := `SELECT * FROM albums;`
|
||||
|
||||
var output []models2.Album
|
||||
err := s.conn.Select(&output, q)
|
||||
// Replace the SQL error with our own error type.
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, models2.ErrNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &output, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removes the album with the given id from the DB.
|
||||
// TODO: this should just mark the object as deleted,
|
||||
// not actually get rid of the data.
|
||||
func (s *AlbumService) Delete(id string) error {
|
||||
if !validID(id) {
|
||||
return models2.ErrNotFound
|
||||
}
|
||||
|
||||
q := `
|
||||
DELETE FROM albums
|
||||
WHERE id = ?;
|
||||
`
|
||||
|
||||
_, err := s.conn.Exec(
|
||||
q,
|
||||
id,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// Check it implements the interface
|
||||
var _ models2.AlbumService = &AlbumService{}
|
36
internal/models/sql/sql.go
Normal file
36
internal/models/sql/sql.go
Normal file
@ -0,0 +1,36 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// NewSQL creates and SQL connection using environment variables
|
||||
// to configure.
|
||||
func NewSQL() (*sqlx.DB, error) {
|
||||
//host := strings.TrimSpace(os.Getenv("MYSQL_HOST"))
|
||||
//port := strings.TrimSpace(os.Getenv("MYSQL_PORT"))
|
||||
//user := strings.TrimSpace(os.Getenv("MYSQL_USER"))
|
||||
//password := strings.TrimSpace(os.Getenv("MYSQL_PASSWORD"))
|
||||
//db := strings.TrimSpace(os.Getenv("MYSQL_DB"))
|
||||
//
|
||||
//info := fmt.Sprintf(
|
||||
// "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
|
||||
// host,
|
||||
// port,
|
||||
// user,
|
||||
// password,
|
||||
// db,
|
||||
//)
|
||||
return sqlx.Connect(
|
||||
"mysql",
|
||||
"mysql:password@tcp(127.0.0.1:3306)/db?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
)
|
||||
}
|
||||
|
||||
// validID checks if the given string is a valid id.
|
||||
func validID(id string) bool {
|
||||
_, err := strconv.Atoi(id)
|
||||
return err == nil
|
||||
}
|
45
internal/server/server.go
Normal file
45
internal/server/server.go
Normal file
@ -0,0 +1,45 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"web-service-gin/internal/controllers"
|
||||
"web-service-gin/internal/models"
|
||||
"web-service-gin/internal/models/sql"
|
||||
)
|
||||
|
||||
// Server represents all the services and controllers.
|
||||
type Server struct {
|
||||
AlbumService models.AlbumService
|
||||
Gin *gin.Engine
|
||||
}
|
||||
|
||||
// NewServer creates a new server using environment variables to
|
||||
// configure DB connection.
|
||||
func NewServer() (*Server, error) {
|
||||
db, err := sql.NewSQL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
albumService, err := sql.NewAlbumService(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := gin.Default()
|
||||
{
|
||||
route := r.Group("/albums")
|
||||
ctrl := controllers.NewAlbum(albumService)
|
||||
|
||||
route.GET("", ctrl.GetAll)
|
||||
route.POST("", ctrl.Post)
|
||||
route.PUT("/:id", ctrl.Put)
|
||||
route.GET("/:id", ctrl.Get)
|
||||
route.DELETE("/:id", ctrl.Delete)
|
||||
}
|
||||
|
||||
return &Server{
|
||||
AlbumService: albumService,
|
||||
Gin: r,
|
||||
}, nil
|
||||
}
|
35
main.go
35
main.go
@ -1,34 +1,17 @@
|
||||
package main
|
||||
|
||||
import "web-service-gin/internal/server"
|
||||
|
||||
// Initial based on:
|
||||
// https://go.dev/doc/tutorial/web-service-gin
|
||||
// https://blog.logrocket.com/how-to-build-a-rest-api-with-golang-using-gin-and-gorm/
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"web-service-gin/controllers"
|
||||
"web-service-gin/models"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetupRouter() *gin.Engine {
|
||||
router := gin.Default()
|
||||
router.GET("/ping", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"message": "pong"})
|
||||
})
|
||||
router = controllers.ApplyAlbumRouter(router)
|
||||
|
||||
return router
|
||||
}
|
||||
// Then moved to Sqlx
|
||||
// https://github.com/wetterj/gin-sqlx-crud
|
||||
|
||||
func main() {
|
||||
router := SetupRouter()
|
||||
|
||||
models.ConnectDatabase()
|
||||
|
||||
s := &http.Server{
|
||||
Addr: ":8123",
|
||||
Handler: router,
|
||||
server, err := server.NewServer()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.ListenAndServe()
|
||||
server.Gin.Run()
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
package models
|
||||
|
||||
type Album struct {
|
||||
ID string `json:"id" gorm:"primary_key"`
|
||||
Title string `json:"title"`
|
||||
Artist string `json:"artist"`
|
||||
Price float64 `json:"price"`
|
||||
}
|
||||
|
||||
type CreateAlbumInput struct {
|
||||
Title string `json:"title" binding:"required"`
|
||||
Artist string `json:"artist" binding:"required"`
|
||||
Price string `json:"price" binding:"required"`
|
||||
}
|
||||
|
||||
type UpdateAlbumInput struct {
|
||||
Title string `json:"title"`
|
||||
Artist string `json:"artist"`
|
||||
Price float64 `json:"price"`
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
func ConnectDatabase() {
|
||||
// Obviously change this to your settings and get securely.
|
||||
dsn := "mysql:password@tcp(127.0.0.1:3306)/db?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic("Failed to connect to database")
|
||||
}
|
||||
_ = db.AutoMigrate(&Album{})
|
||||
|
||||
DB = db
|
||||
}
|
12
test.http
12
test.http
@ -1,31 +1,31 @@
|
||||
GET http://localhost:8123/albums/4
|
||||
GET http://localhost:8080/albums/4
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
GET http://localhost:8123/albums
|
||||
GET http://localhost:8080/albums
|
||||
Content-Type: application/json
|
||||
|
||||
|
||||
###
|
||||
POST http://localhost:8123/albums
|
||||
POST http://localhost:8080/albums
|
||||
Content-Type: application/json
|
||||
|
||||
{"title": "Mark Tom and Travis Show", "artist": "Blink-182", "price": "18.99"}
|
||||
|
||||
###
|
||||
POST http://localhost:8123/albums
|
||||
POST http://localhost:8080/albums
|
||||
Content-Type: application/json
|
||||
|
||||
{"title": "Blue Train", "artist": "John Coltrane", "price": 56.99}
|
||||
|
||||
###
|
||||
POST http://localhost:8123/albums
|
||||
POST http://localhost:8080/albums
|
||||
Content-Type: application/json
|
||||
|
||||
{"title": "Jeru", "artist": "Gerry Mulligan", "price": 56.99}
|
||||
|
||||
###
|
||||
POST http://localhost:8123/albums
|
||||
POST http://localhost:8080/albums
|
||||
Content-Type: application/json
|
||||
|
||||
{"title": "Sarah Vaughan and Clifford Brown", "artist": "Sarah Vaughan", "price": 39.99}
|
||||
|
Loading…
Reference in New Issue
Block a user