Initial Commit

This commit is contained in:
Patrick McDonagh
2017-09-27 11:18:17 -05:00
commit 62ed942495
16 changed files with 922 additions and 0 deletions

80
app.go Normal file
View File

@@ -0,0 +1,80 @@
// app.go
package main
import (
"database/sql"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)
// App : holds the router and db configuration
type App struct {
Router *mux.Router
DB *sql.DB
}
// Initialize : Initialize the app
func (a *App) Initialize(user, password, dbname string) {
connectionString :=
fmt.Sprintf("%s:%s@/%s?parseTime=true", user, password, dbname)
var err error
a.DB, err = sql.Open("mysql", connectionString)
if err != nil {
log.Fatal(err)
}
a.Router = mux.NewRouter()
a.initializeRoutes()
}
func respondWithError(w http.ResponseWriter, code int, message string) {
respondWithJSON(w, code, map[string]string{"error": message})
}
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}
// Run : Run the app
func (a *App) Run(addr string) {
log.Fatal(http.ListenAndServe(":8000", a.Router))
}
// initializeRoutes : initialize all routes
func (a *App) initializeRoutes() {
a.Router.HandleFunc("/api/v1/configs", a.getConfigs).Methods("GET")
a.Router.HandleFunc("/api/v1/config", a.createConfig).Methods("POST")
a.Router.HandleFunc("/api/v1/config/{id:[0-9]+}", a.getConfig).Methods("GET")
a.Router.HandleFunc("/api/v1/config/{id:[0-9]+}", a.updateConfig).Methods("PUT")
a.Router.HandleFunc("/api/v1/config/{id:[0-9]+}", a.deleteConfig).Methods("DELETE")
a.Router.HandleFunc("/api/v1/dataTypes", a.getDataTypes).Methods("GET")
a.Router.HandleFunc("/api/v1/dataType", a.createDataType).Methods("POST")
a.Router.HandleFunc("/api/v1/dataType/{id:[0-9]+}", a.getDataType).Methods("GET")
a.Router.HandleFunc("/api/v1/dataType/{id:[0-9]+}", a.updateDataType).Methods("PUT")
a.Router.HandleFunc("/api/v1/dataType/{id:[0-9]+}", a.deleteDataType).Methods("DELETE")
//Serve public files
var dir string
flag.StringVar(&dir, "dir", "./public", "the directory to serve files from. Defaults to the current dir")
flag.Parse()
a.Router.PathPrefix("/public/").Handler(http.StripPrefix("/public/", http.FileServer(http.Dir(dir))))
// Serve Root HTML Page
a.Router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./html/index.html")
})
}

110
handler_config.go Normal file
View File

@@ -0,0 +1,110 @@
package main
import (
"database/sql"
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
func (a *App) getConfig(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid config ID")
return
}
c := Config{ID: id}
if err := c.getConfig(a.DB); err != nil {
switch err {
case sql.ErrNoRows:
respondWithError(w, http.StatusNotFound, "Config not found")
default:
respondWithError(w, http.StatusInternalServerError, err.Error())
}
return
}
respondWithJSON(w, http.StatusOK, c)
}
func (a *App) getConfigs(w http.ResponseWriter, r *http.Request) {
count, _ := strconv.Atoi(r.FormValue("count"))
start, _ := strconv.Atoi(r.FormValue("start"))
if count > 10 || count < 1 {
count = 10
}
if start < 0 {
start = 0
}
configs, err := getConfigs(a.DB, start, count)
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusOK, configs)
}
func (a *App) createConfig(w http.ResponseWriter, r *http.Request) {
var c Config
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&c); err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
defer r.Body.Close()
if err := c.createConfig(a.DB); err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusCreated, c)
}
func (a *App) updateConfig(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid config ID")
return
}
var c Config
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&c); err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
defer r.Body.Close()
c.ID = id
if err := c.updateConfig(a.DB); err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusOK, c)
}
func (a *App) deleteConfig(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid Config ID")
return
}
c := Config{ID: id}
if err := c.deleteConfig(a.DB); err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusOK, map[string]string{"result": "success"})
}

110
handler_datatype.go Normal file
View File

@@ -0,0 +1,110 @@
package main
import (
"database/sql"
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
func (a *App) getDataType(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid config ID")
return
}
c := DataType{ID: id}
if err := c.getDataType(a.DB); err != nil {
switch err {
case sql.ErrNoRows:
respondWithError(w, http.StatusNotFound, "DataType not found")
default:
respondWithError(w, http.StatusInternalServerError, err.Error())
}
return
}
respondWithJSON(w, http.StatusOK, c)
}
func (a *App) getDataTypes(w http.ResponseWriter, r *http.Request) {
count, _ := strconv.Atoi(r.FormValue("count"))
start, _ := strconv.Atoi(r.FormValue("start"))
if count > 10 || count < 1 {
count = 10
}
if start < 0 {
start = 0
}
configs, err := getDataTypes(a.DB, start, count)
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusOK, configs)
}
func (a *App) createDataType(w http.ResponseWriter, r *http.Request) {
var c DataType
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&c); err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
defer r.Body.Close()
if err := c.createDataType(a.DB); err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusCreated, c)
}
func (a *App) updateDataType(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid config ID")
return
}
var c DataType
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&c); err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
defer r.Body.Close()
c.ID = id
if err := c.updateDataType(a.DB); err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusOK, c)
}
func (a *App) deleteDataType(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid DataType ID")
return
}
c := DataType{ID: id}
if err := c.deleteDataType(a.DB); err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJSON(w, http.StatusOK, map[string]string{"result": "success"})
}

14
html/index.html Normal file
View File

@@ -0,0 +1,14 @@
<html lang="en">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment-with-locales.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Lumberjack</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="/public/transformed.js"></script></body>
</html>

14
main.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "os"
func main() {
a := App{}
a.Initialize(
os.Getenv("APP_DB_USERNAME"),
os.Getenv("APP_DB_PASSWORD"),
os.Getenv("APP_DB_NAME"))
a.Run(":8080")
}

78
model_config.go Normal file
View File

@@ -0,0 +1,78 @@
package main
import (
"database/sql"
"time"
)
// Config : holds information about a specific Config
type Config struct {
ID int `json:"id"`
Parameter string `json:"parameter"`
Val string `json:"val"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// Configs : a list of Config items
type Configs []Config
// getConfig : used during GET command
func (c *Config) getConfig(db *sql.DB) error {
return db.QueryRow("SELECT parameter, val, createdAt, updatedAt FROM configs WHERE id=?", c.ID).Scan(&c.Parameter, &c.Val, &c.CreatedAt, &c.UpdatedAt)
}
// updateConfig : used during PUT command
func (c *Config) updateConfig(db *sql.DB) error {
updStmt, updErr := db.Prepare("UPDATE configs SET parameter=?, val=?, updatedAt=? WHERE id=?")
if updErr != nil {
panic(updErr.Error()) // proper error handling instead of panic in your app
}
defer updStmt.Close() // Close the statement when we leave main() / the program terminates
_, err := updStmt.Exec(c.Parameter, c.Val, time.Now(), c.ID)
return err
}
// deleteConfig : used during DELETE command
func (c *Config) deleteConfig(db *sql.DB) error {
_, err := db.Exec("DELETE FROM configs WHERE id=?", c.ID)
return err
}
// createConfig : used during PUSH command
func (c *Config) createConfig(db *sql.DB) error {
stmtIns, insErr := db.Prepare("INSERT INTO configs (parameter, val, createdAt, updatedAt) VALUES (?, ?, ?, ?);")
if insErr != nil {
panic(insErr.Error()) // proper error handling instead of panic in your app
}
defer stmtIns.Close() // Close the statement when we leave main() / the program terminates
_, err := stmtIns.Exec(c.Parameter, c.Val, time.Now(), time.Now())
return err
}
// getConfigs : used during GET command for all
func getConfigs(db *sql.DB, start, count int) (Configs, error) {
rows, err := db.Query(
"SELECT id, parameter, val, createdAt, updatedAt FROM configs LIMIT ? OFFSET ?",
count, start)
if err != nil {
return nil, err
}
defer rows.Close()
configs := Configs{}
for rows.Next() {
var c Config
if err := rows.Scan(&c.ID, &c.Parameter, &c.CreatedAt, &c.UpdatedAt); err != nil {
return nil, err
}
configs = append(configs, c)
}
return configs, nil
}

78
model_datatype.go Normal file
View File

@@ -0,0 +1,78 @@
package main
import (
"database/sql"
"time"
)
// DataType : holds information about a specific DataType
type DataType struct {
ID int `json:"id"`
DataType string `json:"dataType"`
PlcType string `json:"plcType"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// DataTypes : a list of DataType items
type DataTypes []DataType
// getDataType : used during GET command
func (c *DataType) getDataType(db *sql.DB) error {
return db.QueryRow("SELECT dataType, plcType, createdAt, updatedAt FROM dataTypes WHERE id=?", c.ID).Scan(&c.DataType, &c.PlcType, &c.CreatedAt, &c.UpdatedAt)
}
// updateDataType : used during PUT command
func (c *DataType) updateDataType(db *sql.DB) error {
updStmt, updErr := db.Prepare("UPDATE dataTypes SET dataType=?, plcType=?, updatedAt=? WHERE id=?")
if updErr != nil {
panic(updErr.Error()) // proper error handling instead of panic in your app
}
defer updStmt.Close() // Close the statement when we leave main() / the program terminates
_, err := updStmt.Exec(c.DataType, c.PlcType, time.Now(), c.ID)
return err
}
// deleteDataType : used during DELETE command
func (c *DataType) deleteDataType(db *sql.DB) error {
_, err := db.Exec("DELETE FROM dataTypes WHERE id=?", c.ID)
return err
}
// createDataType : used during PUSH command
func (c *DataType) createDataType(db *sql.DB) error {
stmtIns, insErr := db.Prepare("INSERT INTO dataTypes (dataType, plcType, createdAt, updatedAt) VALUES (?, ?, ?, ?);")
if insErr != nil {
panic(insErr.Error()) // proper error handling instead of panic in your app
}
defer stmtIns.Close() // Close the statement when we leave main() / the program terminates
_, err := stmtIns.Exec(c.DataType, c.PlcType, time.Now(), time.Now())
return err
}
// getDataTypes : used during GET command for all
func getDataTypes(db *sql.DB, start, count int) (DataTypes, error) {
rows, err := db.Query(
"SELECT id, dataType, plcType, createdAt, updatedAt FROM dataTypes LIMIT ? OFFSET ?",
count, start)
if err != nil {
return nil, err
}
defer rows.Close()
dataTypes := DataTypes{}
for rows.Next() {
var c DataType
if err := rows.Scan(&c.ID, &c.DataType, &c.CreatedAt, &c.UpdatedAt); err != nil {
return nil, err
}
dataTypes = append(dataTypes, c)
}
return dataTypes, nil
}

15
model_device.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "time"
// Device : holds information about a specific Device
type Device struct {
ID int `json:"id"`
DeviceType DeviceType `json:"deviceType"`
Address string `json:"address"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// Devices : a list of Device items
type Devices []Device

14
model_devicetype.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "time"
// DeviceType : holds information about a specific DeviceType
type DeviceType struct {
ID int `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// DeviceTypes : a list of DeviceType items
type DeviceTypes []DeviceType

16
model_file.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import "time"
// File : holds information about a specific File
type File struct {
ID int `json:"id"`
Name string `json:"name"`
Location string `json:"location"`
Description string `json:"description"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// Files : a list of File items
type Files []File

25
model_tag.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import "time"
// Tag : holds information about a specific Tag
type Tag struct {
ID int `json:"id"`
Name string `json:"name"`
TagClass TagClass `json:"tagClass"`
Tag string `json:"tag"`
Device Device `json:"device"`
Description string `json:"description"`
DataType DataType `json:"dataType"`
ChangeThreshold float32 `json:"changeThreshold"`
GuaranteeSec int `json:"guaranteeSec"`
MapFunction string `json:"mapFunction"`
Units string `json:"units"`
MinExpected float32 `json:"minExpected"`
MaxExpected float32 `json:"maxExpected"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// Tags : a list of Tag items
type Tags []Tag

15
model_tagclass.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "time"
// TagClass : holds information about a specific TagClass
type TagClass struct {
ID int `json:"id"`
ClassType string `json:"classType"`
Description string `json:"description"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// TagClasses : a list of TagClass items
type TagClasses []TagClass

0
public/transformed.js Normal file
View File

41
test_test.go Normal file
View File

@@ -0,0 +1,41 @@
package main
import (
"net/http"
"net/http/httptest"
"os"
"testing"
)
var a App
func TestMain(m *testing.M) {
a = App{}
a.Initialize(
os.Getenv("TEST_DB_USERNAME"),
os.Getenv("TEST_DB_PASSWORD"),
os.Getenv("TEST_DB_NAME"))
ensureConfigTableExists()
ensureDataTypeTableExists()
code := m.Run()
clearConfigTable()
clearDataTypeTable()
os.Exit(code)
}
func executeRequest(req *http.Request) *httptest.ResponseRecorder {
rr := httptest.NewRecorder()
a.Router.ServeHTTP(rr, req)
return rr
}
func checkResponseCode(t *testing.T, expected, actual int) {
if expected != actual {
t.Errorf("Expected response code %d. Got %d\n", expected, actual)
}
}

156
testconfig_test.go Normal file
View File

@@ -0,0 +1,156 @@
package main
import (
"bytes"
"encoding/json"
"log"
"net/http"
"strconv"
"testing"
"time"
)
const configTableCreationQuery = `CREATE TABLE IF NOT EXISTS configs (
id int(10) unsigned AUTO_INCREMENT,
parameter varchar(255),
val varchar(255),
createdAt datetime,
updatedAt datetime,
UNIQUE KEY parameter (parameter),
PRIMARY KEY (id)
)`
func ensureConfigTableExists() {
if _, err := a.DB.Exec(configTableCreationQuery); err != nil {
log.Fatal(err)
}
}
func clearConfigTable() {
a.DB.Exec("DELETE FROM configs")
a.DB.Exec("ALTER TABLE configs AUTO_INCREMENT = 1")
}
func TestEmptyConfigTable(t *testing.T) {
clearConfigTable()
req, _ := http.NewRequest("GET", "/api/v1/configs", nil)
response := executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
if body := response.Body.String(); body != "[]" {
t.Errorf("Expected an empty array. Got %s", body)
}
}
func TestGetNonExistentConfig(t *testing.T) {
clearConfigTable()
req, _ := http.NewRequest("GET", "/api/v1/config/11", nil)
response := executeRequest(req)
checkResponseCode(t, http.StatusNotFound, response.Code)
var m map[string]string
json.Unmarshal(response.Body.Bytes(), &m)
if m["error"] != "Config not found" {
t.Errorf("Expected the 'error' key of the response to be set to 'Config not found'. Got '%s'", m["error"])
}
}
func TestCreateConfig(t *testing.T) {
clearConfigTable()
payload := []byte(`{"parameter":"save_all","val":"true"}`)
req, _ := http.NewRequest("POST", "/api/v1/config", bytes.NewBuffer(payload))
response := executeRequest(req)
checkResponseCode(t, http.StatusCreated, response.Code)
var m map[string]interface{}
json.Unmarshal(response.Body.Bytes(), &m)
if m["parameter"] != "save_all" {
t.Errorf("Expected config parameter to be 'save_all'. Got '%v'", m["parameter"])
}
if m["val"] != "true" {
t.Errorf("Expected config val to be 'true'. Got '%v'", m["val"])
}
}
func TestGetConfig(t *testing.T) {
clearConfigTable()
addConfigs(1)
req, _ := http.NewRequest("GET", "/api/v1/config/1", nil)
response := executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
}
func addConfigs(count int) {
if count < 1 {
count = 1
}
insStmt, insErr := a.DB.Prepare("INSERT INTO configs(parameter, val, createdAt, updatedAt) VALUES(?, ?, ?, ?)")
if insErr != nil {
panic(insErr.Error()) // proper error handling instead of panic in your app
}
defer insStmt.Close() // Close the statement when we leave main() / the program terminates
for i := 0; i < count; i++ {
insStmt.Exec("testparam"+strconv.Itoa(i), (i+1.0)*10, time.Now(), time.Now())
}
}
func TestUpdateConfig(t *testing.T) {
clearConfigTable()
addConfigs(1)
req, _ := http.NewRequest("GET", "/api/v1/config/1", nil)
response := executeRequest(req)
var originalConfig map[string]interface{}
json.Unmarshal(response.Body.Bytes(), &originalConfig)
payload := []byte(`{"parameter":"test param","val":"11.22"}`)
req, _ = http.NewRequest("PUT", "/api/v1/config/1", bytes.NewBuffer(payload))
response = executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
var m map[string]interface{}
json.Unmarshal(response.Body.Bytes(), &m)
if m["id"] != originalConfig["id"] {
t.Errorf("Expected the id to remain the same (%v). Got %v", originalConfig["id"], m["id"])
}
if m["parameter"] == originalConfig["parameter"] {
t.Errorf("Expected the parameter to change from '%v' to '%v'. Got '%v'", originalConfig["parameter"], m["parameter"], m["parameter"])
}
if m["val"] == originalConfig["val"] {
t.Errorf("Expected the val to change from '%v' to '%v'. Got '%v'", originalConfig["val"], m["val"], m["val"])
}
}
func TestDeleteConfig(t *testing.T) {
clearConfigTable()
addConfigs(1)
req, _ := http.NewRequest("GET", "/api/v1/config/1", nil)
response := executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
req, _ = http.NewRequest("DELETE", "/api/v1/config/1", nil)
response = executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
req, _ = http.NewRequest("GET", "/api/v1/config/1", nil)
response = executeRequest(req)
checkResponseCode(t, http.StatusNotFound, response.Code)
}

156
testdatatype_test.go Normal file
View File

@@ -0,0 +1,156 @@
package main
import (
"bytes"
"encoding/json"
"log"
"net/http"
"strconv"
"testing"
"time"
)
const dataTypeTableCreationQuery = `CREATE TABLE IF NOT EXISTS dataTypes (
id int(10) unsigned AUTO_INCREMENT,
dataType varchar(255),
plcType varchar(255),
createdAt datetime,
updatedAt datetime,
UNIQUE KEY dataType (dataType),
PRIMARY KEY (id)
)`
func ensureDataTypeTableExists() {
if _, err := a.DB.Exec(dataTypeTableCreationQuery); err != nil {
log.Fatal(err)
}
}
func clearDataTypeTable() {
a.DB.Exec("DELETE FROM dataTypes")
a.DB.Exec("ALTER TABLE dataTypes AUTO_INCREMENT = 1")
}
func TestEmptyDataTypeTable(t *testing.T) {
clearDataTypeTable()
req, _ := http.NewRequest("GET", "/api/v1/dataTypes", nil)
response := executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
if body := response.Body.String(); body != "[]" {
t.Errorf("Expected an empty array. Got %s", body)
}
}
func TestGetNonExistentDataType(t *testing.T) {
clearDataTypeTable()
req, _ := http.NewRequest("GET", "/api/v1/dataType/11", nil)
response := executeRequest(req)
checkResponseCode(t, http.StatusNotFound, response.Code)
var m map[string]string
json.Unmarshal(response.Body.Bytes(), &m)
if m["error"] != "DataType not found" {
t.Errorf("Expected the 'error' key of the response to be set to 'DataType not found'. Got '%s'", m["error"])
}
}
func TestCreateDataType(t *testing.T) {
clearDataTypeTable()
payload := []byte(`{"dataType":"Floating Point","plcType":"REAL"}`)
req, _ := http.NewRequest("POST", "/api/v1/dataType", bytes.NewBuffer(payload))
response := executeRequest(req)
checkResponseCode(t, http.StatusCreated, response.Code)
var m map[string]interface{}
json.Unmarshal(response.Body.Bytes(), &m)
if m["dataType"] != "Floating Point" {
t.Errorf("Expected dataType dataType to be 'Floating Point'. Got '%v'", m["dataType"])
}
if m["plcType"] != "REAL" {
t.Errorf("Expected dataType plcType to be 'REAL'. Got '%v'", m["plcType"])
}
}
func TestGetDataType(t *testing.T) {
clearDataTypeTable()
addDataTypes(1)
req, _ := http.NewRequest("GET", "/api/v1/dataType/1", nil)
response := executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
}
func addDataTypes(count int) {
if count < 1 {
count = 1
}
insStmt, insErr := a.DB.Prepare("INSERT INTO dataTypes(dataType, plcType, createdAt, updatedAt) VALUES(?, ?, ?, ?)")
if insErr != nil {
panic(insErr.Error()) // proper error handling instead of panic in your app
}
defer insStmt.Close() // Close the statement when we leave main() / the program terminates
for i := 0; i < count; i++ {
insStmt.Exec("TESTTYPE"+strconv.Itoa(i), "TESTPLCTYPE"+strconv.Itoa(i), time.Now(), time.Now())
}
}
func TestUpdateDataType(t *testing.T) {
clearDataTypeTable()
addDataTypes(1)
req, _ := http.NewRequest("GET", "/api/v1/dataType/1", nil)
response := executeRequest(req)
var originalDataType map[string]interface{}
json.Unmarshal(response.Body.Bytes(), &originalDataType)
payload := []byte(`{"dataType":"Floating Point","plcType":"REAL"}`)
req, _ = http.NewRequest("PUT", "/api/v1/dataType/1", bytes.NewBuffer(payload))
response = executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
var m map[string]interface{}
json.Unmarshal(response.Body.Bytes(), &m)
if m["id"] != originalDataType["id"] {
t.Errorf("Expected the id to remain the same (%v). Got %v", originalDataType["id"], m["id"])
}
if m["dataType"] == originalDataType["dataType"] {
t.Errorf("Expected the dataType to change from '%v' to '%v'. Got '%v'", originalDataType["dataType"], m["dataType"], m["dataType"])
}
if m["plcType"] == originalDataType["plcType"] {
t.Errorf("Expected the plcType to change from '%v' to '%v'. Got '%v'", originalDataType["plcType"], m["plcType"], m["plcType"])
}
}
func TestDeleteDataType(t *testing.T) {
clearDataTypeTable()
addDataTypes(1)
req, _ := http.NewRequest("GET", "/api/v1/dataType/1", nil)
response := executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
req, _ = http.NewRequest("DELETE", "/api/v1/dataType/1", nil)
response = executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
req, _ = http.NewRequest("GET", "/api/v1/dataType/1", nil)
response = executeRequest(req)
checkResponseCode(t, http.StatusNotFound, response.Code)
}