package api
import (
"os"
"github.com/BurntSushi/toml"
)
var ENV_VARS = []string{"WORKDAY_DB_HOST", "WORKDAY_DB_NAME", "WORKDAY_DB_USER", "WORKDAY_DB_PASSWORD"}
// Represents database parameters
type DBConfig struct {
Host string
Database string
User string
Password string
}
func envVars(vars []string) bool {
for _, v := range vars {
if _, ok := os.LookupEnv(v); !ok {
return false
}
}
return true
}
// Read and parse DB configuration; environment variables have precedence over config file.
func LoadDBConfig(filepath string) (DBConfig, error) {
var c DBConfig
if envVars(ENV_VARS) {
host, _ := os.LookupEnv("WORKDAY_DB_HOST")
name, _ := os.LookupEnv("WORKDAY_DB_NAME")
user, _ := os.LookupEnv("WORKDAY_DB_USER")
password, _ := os.LookupEnv("WORKDAY_DB_PASSWORD")
c = DBConfig{host, name, user, password}
} else {
if _, err := toml.DecodeFile(filepath, &c); err != nil {
return DBConfig{}, err
}
}
return c, nil
}
package api
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// ConnectDB returns a sql.DB connection using a given DBConfig.
func ConnectDB(config DBConfig) (*sql.DB, error) {
ds := fmt.Sprintf("%v:%v@tcp(%v)/%v", config.User, config.Password, config.Host, config.Database)
db, err := sql.Open("mysql", ds)
if err != nil {
return nil, err
}
return db, nil
}
func insertNewEmployee(db *sql.DB, employee *Employee) (uint, uint, error) {
result, err := db.Exec(
"INSERT INTO employee(firstname, lastname, role, password) values(?, ?, ?, ?)",
employee.Firstname, employee.Lastname, employee.Role, employee.Password,
)
if err != nil {
return 0, 0, err
}
lastID, err := result.LastInsertId()
if err != nil {
return 0, 0, err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return 0, 0, err
}
return uint(lastID), uint(rowsAffected), nil
}
func findAllEmployees(db *sql.DB) ([]Employee, error) {
employees := make([]Employee, 0)
rows, err := db.Query("SELECT * FROM employee")
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var employee Employee
err := rows.Scan(&employee.ID, &employee.Firstname, &employee.Lastname, &employee.Role, &employee.Password)
if err != nil {
return nil, err
}
employees = append(employees, employee)
}
if err := rows.Err(); err != nil {
return nil, err
}
return employees, nil
}
func findEmployee(db *sql.DB, id uint) (Employee, error) {
var employee Employee
row := db.QueryRow("SELECT * FROM employee WHERE id=?", id)
err := row.Scan(&employee.ID, &employee.Firstname, &employee.Lastname, &employee.Role, &employee.Password)
if err != nil {
if err.Error() == "sql: no rows in result set" {
return Employee{}, nil
}
return Employee{}, err
}
return employee, nil
}
func removeEmployee(db *sql.DB, id uint) (uint, error) {
result, err := db.Exec("DELETE FROM employee WHERE id=?", id)
if err != nil {
return 0, err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return 0, err
}
return uint(rowsAffected), nil
}
func updateEmployee(db *sql.DB, emp *Employee) (uint, error) {
result, err := db.Exec("UPDATE employee SET firstname=?, lastname=?, role=?, password=? WHERE id=?",
emp.Firstname, emp.Lastname, emp.Role, emp.Password, emp.ID)
if err != nil {
return 0, err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return 0, err
}
return uint(rowsAffected), nil
}
package api
import (
"database/sql"
)
// Represents an employee
type Employee struct {
ID uint `json:"id"`
Firstname string `binding:"required" json:"firstname"`
Lastname string `binding:"required" json:"lastname"`
Role uint `binding:"required" json:role`
Password string `binding:"required" json:password`
}
// CreateEmployee saves a new employee in DB.
func CreateEmployee(db *sql.DB, emp *Employee) (uint, uint, error) {
return insertNewEmployee(db, emp)
}
// GetAllEmployees retrieves all employees from DB.
func GetAllEmployees(db *sql.DB) ([]Employee, error) {
return findAllEmployees(db)
}
// GetEmployee retrieves an employee from DB.
func GetEmployee(db *sql.DB, id uint) (Employee, error) {
return findEmployee(db, id)
}
// DeleteEmployee removes an employee from DB.
func DeleteEmployee(db *sql.DB, id uint) (uint, error) {
return removeEmployee(db, id)
}
// EditEmployee updates an employee from DB.
func EditEmployee(db *sql.DB, emp *Employee) (uint, error) {
return updateEmployee(db, emp)
}
package api
import (
"database/sql"
"fmt"
"strconv"
"net/http"
"github.com/gin-gonic/gin"
)
// Creates and returns a Gin Router
func GinRouter(db *sql.DB) *gin.Engine {
router := gin.Default()
router.GET("/status", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "OK"})
})
router.GET("/employees", func(c *gin.Context) {
emps, err := GetAllEmployees(db)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, emps)
})
router.GET("/employees/:id", func(c *gin.Context) {
id := c.Param("id")
idInt, err := strconv.Atoi(id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
emp, err := GetEmployee(db, uint(idInt))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if (Employee{}) == emp {
c.JSON(http.StatusNotFound, gin.H{})
return
}
c.JSON(http.StatusOK, emp)
})
router.DELETE("/employees/:id", func(c *gin.Context) {
id := c.Param("id")
idInt, err := strconv.Atoi(id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
_, err = DeleteEmployee(db, uint(idInt))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "OK"})
})
// To update the whole resource a!
router.PUT("/employees/:id", func(c *gin.Context) {
var form Employee
id := c.Param("id")
idInt, err := strconv.Atoi(id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
err = c.ShouldBindJSON(&form)
if err != nil {
c.JSON(http.StatusNoContent, gin.H{"error": err.Error()})
return
}
_, err = GetEmployee(db, uint(idInt))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
form.ID = uint(idInt)
rows, err := EditEmployee(db, &form)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if rows > 1 {
c.JSON(http.StatusOK, gin.H{"error": fmt.Sprintf("%d rows updated", rows)})
return
}
c.JSON(http.StatusOK, gin.H{"status": "OK"})
})
router.POST("/employees", func(c *gin.Context) {
var form Employee
err := c.ShouldBind(&form)
if err != nil {
c.JSON(http.StatusNoContent, gin.H{"message": err.Error()})
return
}
id, _, err := CreateEmployee(db, &form)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return
}
c.JSON(http.StatusCreated, gin.H{"id": id})
})
return router
}