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 }