diff --git a/app.go b/app.go index 706272b..3e70303 100644 --- a/app.go +++ b/app.go @@ -85,6 +85,24 @@ func (a *App) initializeRoutes() { a.Router.HandleFunc("/api/v1/file/{id:[0-9]+}", a.updateFile).Methods("PUT") a.Router.HandleFunc("/api/v1/file/{id:[0-9]+}", a.deleteFile).Methods("DELETE") + a.Router.HandleFunc("/api/v1/tagClasses", a.getTagClasses).Methods("GET") + a.Router.HandleFunc("/api/v1/tagClass", a.createTagClass).Methods("POST") + a.Router.HandleFunc("/api/v1/tagClass/{id:[0-9]+}", a.getTagClass).Methods("GET") + a.Router.HandleFunc("/api/v1/tagClass/{id:[0-9]+}", a.updateTagClass).Methods("PUT") + a.Router.HandleFunc("/api/v1/tagClass/{id:[0-9]+}", a.deleteTagClass).Methods("DELETE") + + a.Router.HandleFunc("/api/v1/tags", a.getTags).Methods("GET") + a.Router.HandleFunc("/api/v1/tag", a.createTag).Methods("POST") + a.Router.HandleFunc("/api/v1/tag/{id:[0-9]+}", a.getTag).Methods("GET") + a.Router.HandleFunc("/api/v1/tag/{id:[0-9]+}", a.updateTag).Methods("PUT") + a.Router.HandleFunc("/api/v1/tag/{id:[0-9]+}", a.deleteTag).Methods("DELETE") + + a.Router.HandleFunc("/api/v1/tagValues", a.getTagValues).Methods("GET") + a.Router.HandleFunc("/api/v1/tagValue", a.createTagValue).Methods("POST") + a.Router.HandleFunc("/api/v1/tagValue/{id:[0-9]+}", a.getTagValue).Methods("GET") + a.Router.HandleFunc("/api/v1/tagValue/{id:[0-9]+}", a.updateTagValue).Methods("PUT") + a.Router.HandleFunc("/api/v1/tagValue/{id:[0-9]+}", a.deleteTagValue).Methods("DELETE") + //Serve public files var dir string flag.StringVar(&dir, "dir", "./public", "the directory to serve files from. Defaults to the current dir") diff --git a/db.sql b/db.sql new file mode 100644 index 0000000..10f0fd9 --- /dev/null +++ b/db.sql @@ -0,0 +1,14 @@ + + +CREATE DATABASE IF NOT EXISTS lumberjack ; + +USE lumberjack; +CREATE USER 'website'@'localhost' IDENTIFIED BY 'henrypump'; +GRANT ALL ON *.* TO 'website'@'localhost'; +CREATE USER 'website'@'127.0.0.1' IDENTIFIED BY 'henrypump'; +GRANT ALL ON *.* TO 'website'@'127.0.0.1'; +CREATE USER 'admin'@'localhost' IDENTIFIED BY 'henrypump'; +GRANT ALL ON *.* to 'admin'@'localhost'; +CREATE USER 'admin'@'%' IDENTIFIED BY 'henrypump'; +GRANT ALL ON *.* to 'admin'@'%'; +FLUSH PRIVILEGES; \ No newline at end of file diff --git a/handler_tag.go b/handler_tag.go new file mode 100644 index 0000000..a41f5c2 --- /dev/null +++ b/handler_tag.go @@ -0,0 +1,110 @@ +package main + +import ( + "database/sql" + "encoding/json" + "net/http" + "strconv" + + "github.com/gorilla/mux" +) + +func (a *App) getTag(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + respondWithError(w, http.StatusBadRequest, "Invalid tag ID") + return + } + + c := Tag{ID: id} + if err := c.getTag(a.DB); err != nil { + switch err { + case sql.ErrNoRows: + respondWithError(w, http.StatusNotFound, "Tag not found") + default: + respondWithError(w, http.StatusInternalServerError, err.Error()) + } + return + } + + respondWithJSON(w, http.StatusOK, c) +} + +func (a *App) getTags(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 + } + + tags, err := getTags(a.DB, start, count) + if err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusOK, tags) +} + +func (a *App) createTag(w http.ResponseWriter, r *http.Request) { + var c Tag + 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.createTag(a.DB); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusCreated, c) +} + +func (a *App) updateTag(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + respondWithError(w, http.StatusBadRequest, "Invalid tag ID") + return + } + + var c Tag + 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.updateTag(a.DB); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusOK, c) +} + +func (a *App) deleteTag(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + respondWithError(w, http.StatusBadRequest, "Invalid Tag ID") + return + } + + c := Tag{ID: id} + if err := c.deleteTag(a.DB); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusOK, map[string]string{"result": "success"}) +} diff --git a/handler_tagclass.go b/handler_tagclass.go new file mode 100644 index 0000000..7c3083e --- /dev/null +++ b/handler_tagclass.go @@ -0,0 +1,110 @@ +package main + +import ( + "database/sql" + "encoding/json" + "net/http" + "strconv" + + "github.com/gorilla/mux" +) + +func (a *App) getTagClass(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 := TagClass{ID: id} + if err := c.getTagClass(a.DB); err != nil { + switch err { + case sql.ErrNoRows: + respondWithError(w, http.StatusNotFound, "TagClass not found") + default: + respondWithError(w, http.StatusInternalServerError, err.Error()) + } + return + } + + respondWithJSON(w, http.StatusOK, c) +} + +func (a *App) getTagClasses(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 := getTagClasses(a.DB, start, count) + if err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusOK, configs) +} + +func (a *App) createTagClass(w http.ResponseWriter, r *http.Request) { + var c TagClass + 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.createTagClass(a.DB); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusCreated, c) +} + +func (a *App) updateTagClass(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + respondWithError(w, http.StatusBadRequest, "Invalid TagClass ID") + return + } + + var c TagClass + 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.updateTagClass(a.DB); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusOK, c) +} + +func (a *App) deleteTagClass(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + respondWithError(w, http.StatusBadRequest, "Invalid TagClass ID") + return + } + + c := TagClass{ID: id} + if err := c.deleteTagClass(a.DB); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusOK, map[string]string{"result": "success"}) +} diff --git a/handler_tagvalue.go b/handler_tagvalue.go new file mode 100644 index 0000000..73978c8 --- /dev/null +++ b/handler_tagvalue.go @@ -0,0 +1,110 @@ +package main + +import ( + "database/sql" + "encoding/json" + "net/http" + "strconv" + + "github.com/gorilla/mux" +) + +func (a *App) getTagValue(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + respondWithError(w, http.StatusBadRequest, "Invalid tagValue ID") + return + } + + c := TagValue{ID: id} + if err := c.getTagValue(a.DB); err != nil { + switch err { + case sql.ErrNoRows: + respondWithError(w, http.StatusNotFound, "TagValue not found") + default: + respondWithError(w, http.StatusInternalServerError, err.Error()) + } + return + } + + respondWithJSON(w, http.StatusOK, c) +} + +func (a *App) getTagValues(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 + } + + tagValues, err := getTagValues(a.DB, start, count) + if err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusOK, tagValues) +} + +func (a *App) createTagValue(w http.ResponseWriter, r *http.Request) { + var c TagValue + 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.createTagValue(a.DB); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusCreated, c) +} + +func (a *App) updateTagValue(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + respondWithError(w, http.StatusBadRequest, "Invalid tagValue ID") + return + } + + var c TagValue + 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.updateTagValue(a.DB); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusOK, c) +} + +func (a *App) deleteTagValue(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + respondWithError(w, http.StatusBadRequest, "Invalid TagValue ID") + return + } + + c := TagValue{ID: id} + if err := c.deleteTagValue(a.DB); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + respondWithJSON(w, http.StatusOK, map[string]string{"result": "success"}) +} diff --git a/main.go b/main.go index 1f3e79d..9551dbf 100644 --- a/main.go +++ b/main.go @@ -15,8 +15,14 @@ func main() { ensureDeviceTypeTableExists(a.DB) ensureDeviceTableExists(a.DB) ensureFileTableExists(a.DB) + ensureTagClassTableExists(a.DB) + ensureTagTableExists(a.DB) seedDeviceTypeData(a.DB) + seedDataTypeData(a.DB) + seedTagClassData(a.DB) + + seedDeviceData(a.DB) a.Run(":8080") } diff --git a/model_datatype.go b/model_datatype.go index 8029b2d..0961060 100644 --- a/model_datatype.go +++ b/model_datatype.go @@ -33,6 +33,20 @@ func ensureDataTypeTableExists(db *sql.DB) { log.Fatal(err) } } +func seedDataTypeData(db *sql.DB) { + sqlQuery := `INSERT INTO dataTypes VALUES + (1,'Floating Point','REAL','2016-10-13 15:05:32','2016-10-13 15:05:32'), + (2,'Integer','INT','2016-10-13 15:05:32','2016-10-13 15:05:32'), + (3,'Boolean','BOOL','2016-10-13 15:05:32','2016-10-13 15:05:32');` + insStmt, insErr := db.Prepare(sqlQuery) + + 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 + + insStmt.Exec() +} // getDataType : used during GET command func (c *DataType) getDataType(db *sql.DB) error { diff --git a/model_device.go b/model_device.go index d16f16a..0e8cf3d 100644 --- a/model_device.go +++ b/model_device.go @@ -37,6 +37,19 @@ func ensureDeviceTableExists(db *sql.DB) { } } +func seedDeviceData(db *sql.DB) { + sqlQuery := `INSERT INTO devices VALUES + (1,'Default PLC',1,'192.168.1.10','2016-10-13 15:05:32','2016-10-13 15:05:32');` + insStmt, insErr := db.Prepare(sqlQuery) + + 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 + + insStmt.Exec() +} + // getDevice : used during GET command func (d *Device) getDevice(db *sql.DB) error { sqlQuery := `SELECT diff --git a/model_tag.go b/model_tag.go index f4ab08b..c0a6a0e 100644 --- a/model_tag.go +++ b/model_tag.go @@ -1,15 +1,22 @@ package main -import "time" +import ( + "database/sql" + "log" + "time" +) // Tag : holds information about a specific Tag type Tag struct { ID int `json:"id"` Name string `json:"name"` + TagClassID int `json:"tagClassId"` TagClass TagClass `json:"tagClass"` - Tag string `json:"tag"` + TagName string `json:"tagName"` + DeviceID int `json:"deviceId"` Device Device `json:"device"` Description string `json:"description"` + DataTypeID int `json:"dataTypeId"` DataType DataType `json:"dataType"` ChangeThreshold float32 `json:"changeThreshold"` GuaranteeSec int `json:"guaranteeSec"` @@ -23,3 +30,281 @@ type Tag struct { // Tags : a list of Tag items type Tags []Tag + +const tagTableCreationQuery = `CREATE TABLE IF NOT EXISTS tags ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(255), + tagName varchar(255), + tagClassId int(10) unsigned, + deviceId int(10) unsigned, + description varchar(255), + dataTypeId int(10) unsigned, + changeThreshold float, + guaranteeSec int(10) unsigned, + mapFunction varchar(255), + units varchar(255), + minExpected float, + maxExpected float, + createdAt datetime, + updatedAt datetime, + PRIMARY KEY (id), + CONSTRAINT fk_tagclass FOREIGN KEY (tagClassId) REFERENCES tagClasses(id), + CONSTRAINT fk_device FOREIGN KEY (deviceId) REFERENCES devices(id), + CONSTRAINT fk_datatype FOREIGN KEY (dataTypeId) REFERENCES dataTypes(id) + );` + +func ensureTagTableExists(db *sql.DB) { + if _, err := db.Exec(tagTableCreationQuery); err != nil { + log.Fatal(err) + } +} + +// getTag : used during GET command +func (t *Tag) getTag(db *sql.DB) error { + sqlQuery := `SELECT + tags.name, + tags.tagName, + tags.tagClassId, + tagClasses.id, + tagClasses.classType, + tagClasses.description, + tagClasses.updatedAt, + tagClasses.createdAt, + tags.deviceId, + devices.id, + devices.name, + devices.deviceTypeId, + deviceTypes.id, + deviceTypes.name, + deviceTypes.createdAt, + deviceTypes.updatedAt, + devices.address, + devices.createdAt, + devices.updatedAt, + tags.description, + tags.dataTypeId, + dataTypes.id, + dataTypes.dataType, + dataTypes.plcType, + dataTypes.updatedAt, + dataTypes.createdAt, + tags.changeThreshold, + tags.guaranteeSec, + tags.mapFunction, + tags.units, + tags.maxExpected, + tags.minExpected, + tags.createdAt, + tags.updatedAt + FROM tags + JOIN tagClasses ON tags.tagClassId = tagClasses.id + JOIN devices ON tags.deviceId = devices.id + JOIN deviceTypes ON devices.deviceTypeId = deviceTypes.id + JOIN dataTypes ON tags.dataTypeId = dataTypes.id + WHERE tags.id=?` + return db.QueryRow(sqlQuery, t.ID).Scan( + &t.Name, + &t.TagName, + &t.TagClassID, + &t.TagClass.ID, + &t.TagClass.ClassType, + &t.TagClass.Description, + &t.TagClass.UpdatedAt, + &t.TagClass.UpdatedAt, + &t.DeviceID, + &t.Device.ID, + &t.Device.Name, + &t.Device.DeviceTypeID, + &t.Device.DeviceType.ID, + &t.Device.DeviceType.Name, + &t.Device.DeviceType.CreatedAt, + &t.Device.DeviceType.UpdatedAt, + &t.Device.Address, + &t.Device.CreatedAt, + &t.Device.UpdatedAt, + &t.Description, + &t.DataTypeID, + &t.DataType.ID, + &t.DataType.DataType, + &t.DataType.PlcType, + &t.DataType.UpdatedAt, + &t.DataType.CreatedAt, + &t.ChangeThreshold, + &t.GuaranteeSec, + &t.MapFunction, + &t.Units, + &t.MaxExpected, + &t.MinExpected, + &t.CreatedAt, + &t.UpdatedAt) +} + +// updateTag : used during PUT command +func (t *Tag) updateTag(db *sql.DB) error { + sqlQuery := `UPDATE tags SET + name=?, + tagName=?, + tagClassId=?, + deviceId=?, + description=?, + dataTypeId=?, + changeThreshold=?, + guaranteeSec=?, + mapFunction=?, + units=?, + minExpected=?, + maxExpected=?, + updatedAt=? + WHERE id=?` + updStmt, updErr := db.Prepare(sqlQuery) + 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(t.Name, t.TagName, t.TagClassID, t.DeviceID, + t.Description, t.DataTypeID, t.ChangeThreshold, t.GuaranteeSec, + t.MapFunction, t.Units, t.MinExpected, t.MaxExpected, time.Now(), t.ID) + return err +} + +// deleteTag : used during DELETE command +func (t *Tag) deleteTag(db *sql.DB) error { + _, err := db.Exec("DELETE FROM tags WHERE id=?", t.ID) + return err +} + +// createTag : used during PUSH command +func (t *Tag) createTag(db *sql.DB) error { + sqlQuery := `INSERT INTO tags ( + name, + tagName, + tagClassId, + deviceId, + description, + dataTypeId, + changeThreshold, + guaranteeSec, + mapFunction, + units, + minExpected, + maxExpected, + createdAt, + updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` + stmtIns, insErr := db.Prepare(sqlQuery) + 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(t.Name, t.TagName, t.TagClassID, t.DeviceID, + t.Description, t.DataTypeID, t.ChangeThreshold, t.GuaranteeSec, + t.MapFunction, t.Units, t.MinExpected, t.MaxExpected, time.Now(), time.Now()) + return err +} + +// getTags : used during GET command for all +func getTags(db *sql.DB, start, count int) (Tags, error) { + sqlQuery := `SELECT + tags.ID, + tags.name, + tags.tagName, + tags.tagClassId, + tagClasses.id, + tagClasses.classType, + tagClasses.description, + tagClasses.updatedAt, + tagClasses.createdAt, + tags.deviceId, + devices.id, + devices.name, + devices.deviceTypeId, + deviceTypes.id, + deviceTypes.name, + deviceTypes.createdAt, + deviceTypes.updatedAt, + devices.address, + devices.createdAt, + devices.updatedAt, + tags.description, + tags.dataTypeId, + dataTypes.id, + dataTypes.dataType, + dataTypes.plcType, + dataTypes.updatedAt, + dataTypes.createdAt, + tags.changeThreshold, + tags.guaranteeSec, + tags.mapFunction, + tags.units, + tags.maxExpected, + tags.minExpected, + tags.createdAt, + tags.updatedAt + FROM tags + JOIN tagClasses ON tags.tagClassId = tagClasses.id + JOIN devices ON tags.deviceId = devices.id + JOIN deviceTypes ON devices.deviceTypeId = deviceTypes.id + JOIN dataTypes ON tags.dataTypeId = dataTypes.id + LIMIT ? OFFSET ?;` + + getStmt, prepErr := db.Prepare(sqlQuery) + if prepErr != nil { + panic(prepErr.Error()) // proper error handling instead of panic in your app + } + defer getStmt.Close() // Close the statement when we leave main() / the program terminates + + rows, err := getStmt.Query(count, start) + + if err != nil { + return nil, err + } + + defer rows.Close() + + tags := Tags{} + + for rows.Next() { + var t Tag + if err := rows.Scan(&t.ID, + &t.Name, + &t.TagName, + &t.TagClassID, + &t.TagClass.ID, + &t.TagClass.ClassType, + &t.TagClass.Description, + &t.TagClass.UpdatedAt, + &t.TagClass.UpdatedAt, + &t.DeviceID, + &t.Device.ID, + &t.Device.Name, + &t.Device.DeviceTypeID, + &t.Device.DeviceType.ID, + &t.Device.DeviceType.Name, + &t.Device.DeviceType.CreatedAt, + &t.Device.DeviceType.UpdatedAt, + &t.Device.Address, + &t.Device.CreatedAt, + &t.Device.UpdatedAt, + &t.Description, + &t.DataTypeID, + &t.DataType.ID, + &t.DataType.DataType, + &t.DataType.PlcType, + &t.DataType.UpdatedAt, + &t.DataType.CreatedAt, + &t.ChangeThreshold, + &t.GuaranteeSec, + &t.MapFunction, + &t.Units, + &t.MaxExpected, + &t.MinExpected, + &t.CreatedAt, + &t.UpdatedAt); err != nil { + return nil, err + } + tags = append(tags, t) + } + + return tags, nil +} diff --git a/model_tagclass.go b/model_tagclass.go index 4b1abe5..75a5d01 100644 --- a/model_tagclass.go +++ b/model_tagclass.go @@ -1,6 +1,10 @@ package main -import "time" +import ( + "database/sql" + "log" + "time" +) // TagClass : holds information about a specific TagClass type TagClass struct { @@ -13,3 +17,93 @@ type TagClass struct { // TagClasses : a list of TagClass items type TagClasses []TagClass + +const tagClassTableCreationQuery = `CREATE TABLE IF NOT EXISTS tagClasses ( + id int(10) unsigned AUTO_INCREMENT, + classType varchar(255), + description varchar(255), + createdAt datetime, + updatedAt datetime, + UNIQUE KEY classType (classType), + PRIMARY KEY (id) + )` + +func ensureTagClassTableExists(db *sql.DB) { + if _, err := db.Exec(tagClassTableCreationQuery); err != nil { + log.Fatal(err) + } +} + +func seedTagClassData(db *sql.DB) { + sqlQuery := `INSERT INTO tagClasses VALUES + (1,'normal','Historical Data','2016-10-13 15:05:32','2016-10-13 15:05:32'), + (2,'handshake','PLC Handshake','2016-10-13 15:05:32','2016-10-13 15:05:32');` + insStmt, insErr := db.Prepare(sqlQuery) + + 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 + + insStmt.Exec() +} + +// getTagClass : used during GET command +func (c *TagClass) getTagClass(db *sql.DB) error { + return db.QueryRow("SELECT classType, description, createdAt, updatedAt FROM tagClasses WHERE id=?", c.ID).Scan(&c.ClassType, &c.Description, &c.CreatedAt, &c.UpdatedAt) +} + +// updateTagClass : used during PUT command +func (c *TagClass) updateTagClass(db *sql.DB) error { + updStmt, updErr := db.Prepare("UPDATE tagClasses SET classType=?, description=?, 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.ClassType, c.Description, time.Now(), c.ID) + return err +} + +// deleteTagClass : used during DELETE command +func (c *TagClass) deleteTagClass(db *sql.DB) error { + _, err := db.Exec("DELETE FROM tagClasses WHERE id=?", c.ID) + return err +} + +// createTagClass : used during PUSH command +func (c *TagClass) createTagClass(db *sql.DB) error { + stmtIns, insErr := db.Prepare("INSERT INTO tagClasses (classType, description, 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.ClassType, c.Description, time.Now(), time.Now()) + return err +} + +// getTagClasses : used during GET command for all +func getTagClasses(db *sql.DB, start, count int) (TagClasses, error) { + rows, err := db.Query( + "SELECT id, classType, description, createdAt, updatedAt FROM tagClasses LIMIT ? OFFSET ?", + count, start) + + if err != nil { + return nil, err + } + + defer rows.Close() + + tagClasses := TagClasses{} + + for rows.Next() { + var c TagClass + if err := rows.Scan(&c.ID, &c.ClassType, &c.Description, &c.CreatedAt, &c.UpdatedAt); err != nil { + return nil, err + } + tagClasses = append(tagClasses, c) + } + + return tagClasses, nil +} diff --git a/model_tagvalue.go b/model_tagvalue.go new file mode 100644 index 0000000..72e5b89 --- /dev/null +++ b/model_tagvalue.go @@ -0,0 +1,121 @@ +package main + +import ( + "database/sql" + "log" + "time" +) + +// TagValue : holds information about a specific TagValue +type TagValue struct { + ID int `json:"id"` + TagID int `json:"tagId"` + Val string `json:"val"` + TagName int `json:"tagName"` + Units int `json:"units"` + DataType string `json:"dataType"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +// TagValues : a list of TagValue items +type TagValues []TagValue + +const tagValueTableCreationQuery = `CREATE TABLE IF NOT EXISTS tagValues ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + tagId int(10) unsigned, + val varchar(64), + createdAt datetime, + updatedAt datetime, + PRIMARY KEY (id), + CONSTRAINT fk_tag FOREIGN KEY (tagId) REFERENCES tags(id) + )` + +func ensureTagValueTableExists(db *sql.DB) { + if _, err := db.Exec(tagValueTableCreationQuery); err != nil { + log.Fatal(err) + } +} + +// getTagValue : used during GET command +func (t *TagValue) getTagValue(db *sql.DB) error { + sqlQuery := `SELECT + tagValues.tagId, + tagValues.val, + tags.name, + tags.units, + dataTypes.dataType, + tagValues.createdAt, + tagValues.updatedAt + FROM tagValues + JOIN tags ON tagValues.tagId = tags.id + JOIN dataTypes ON tags.dataTypeId = dataTypes.id + WHERE tagValues.id=?` + return db.QueryRow(sqlQuery, t.ID).Scan(&t.TagID, &t.Val, &t.TagName, &t.Units, &t.DataType, &t.CreatedAt, &t.UpdatedAt) +} + +// updateTagValue : used during PUT command +func (t *TagValue) updateTagValue(db *sql.DB) error { + updStmt, updErr := db.Prepare("UPDATE tagValues SET tagId=?, 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(t.TagID, t.Val, time.Now(), t.ID) + return err +} + +// deleteTagValue : used during DELETE command +func (t *TagValue) deleteTagValue(db *sql.DB) error { + _, err := db.Exec("DELETE FROM tagValues WHERE id=?", t.ID) + return err +} + +// createTagValue : used during PUSH command +func (t *TagValue) createTagValue(db *sql.DB) error { + stmtIns, insErr := db.Prepare("INSERT INTO tagValues (tagId, 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(t.TagID, t.Val, time.Now(), time.Now()) + return err +} + +// getTagValues : used during GET command for all +func getTagValues(db *sql.DB, start, count int) (TagValues, error) { + sqlQuery := `SELECT + tagValues.id, + tagValues.tagId, + tagValues.val, + tags.name, + tags.units, + dataTypes.dataType, + tagValues.createdAt, + tagValues.updatedAt + FROM tagValues + JOIN tags ON tagValues.tagId = tags.id + JOIN dataTypes ON tags.dataTypeId = dataTypes.id + LIMIT ? OFFSET ?` + rows, err := db.Query(sqlQuery, count, start) + + if err != nil { + return nil, err + } + + defer rows.Close() + + tagValues := TagValues{} + + for rows.Next() { + var t TagValue + if err := rows.Scan(&t.ID, &t.TagID, &t.Val, &t.TagName, &t.Units, &t.DataType, &t.CreatedAt, &t.UpdatedAt); err != nil { + return nil, err + } + tagValues = append(tagValues, t) + } + + return tagValues, nil +} diff --git a/test_test.go b/test_test.go index ba3c361..5dc8e7d 100644 --- a/test_test.go +++ b/test_test.go @@ -21,6 +21,9 @@ func TestMain(m *testing.M) { ensureDeviceTypeTableExists(a.DB) ensureDeviceTableExists(a.DB) ensureFileTableExists(a.DB) + ensureTagClassTableExists(a.DB) + ensureTagTableExists(a.DB) + ensureTagValueTableExists(a.DB) code := m.Run() @@ -29,6 +32,9 @@ func TestMain(m *testing.M) { clearDeviceTypeTable() clearDeviceTable() clearFileTable() + clearTagClassTable() + clearTagTable() + clearTagValueTable() os.Exit(code) } diff --git a/testtag_test.go b/testtag_test.go new file mode 100644 index 0000000..656ba81 --- /dev/null +++ b/testtag_test.go @@ -0,0 +1,230 @@ +package main + +import ( + "bytes" + "encoding/json" + "net/http" + "reflect" + "strconv" + "testing" + "time" +) + +func clearTagTable() { + a.DB.Exec("DELETE FROM tags") + a.DB.Exec("ALTER TABLE tags AUTO_INCREMENT = 1") +} + +func TestEmptyTagTable(t *testing.T) { + clearTagTable() + + req, _ := http.NewRequest("GET", "/api/v1/tags", 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 TestGetNonExistentTag(t *testing.T) { + clearTagTable() + + req, _ := http.NewRequest("GET", "/api/v1/tag/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"] != "Tag not found" { + t.Errorf("Expected the 'error' key of the response to be set to 'Tag not found'. Got '%s'", m["error"]) + } +} + +func TestCreateTag(t *testing.T) { + clearTagTable() + seedDataTypeData(a.DB) + seedDeviceTypeData(a.DB) + seedTagClassData(a.DB) + seedDeviceData(a.DB) + + payload := []byte(`{"name":"TestTag","tagName":"testTag","tagClassId":1,"deviceId":1,"description":"This is a test tag","dataTypeId":1,"changeThreshold":4.5,"guaranteeSec":600,"mapFunction":"double","units":"in.","minExpected":0.0,"maxExpected":1000.0}`) + + req, _ := http.NewRequest("POST", "/api/v1/tag", 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["name"] != "TestTag" { + t.Errorf("Expected tag name to be 'TestTag'. Got '%v'", m["name"]) + } + if m["tagName"] != "testTag" { + t.Errorf("Expected tag tagName to be 'testTag'. Got '%v'", m["tagName"]) + } + if m["tagClassId"] != 1.0 { + t.Errorf("Expected tag tagClassId to be '1'. Got '%v'", m["tagClassId"]) + } + if m["deviceId"] != 1.0 { + t.Errorf("Expected tag deviceId to be '1'. Got '%v'", m["deviceId"]) + } + if m["description"] != "This is a test tag" { + t.Errorf("Expected tag description to be 'This is a test tag'. Got '%v'", m["description"]) + } + if m["dataTypeId"] != 1.0 { + t.Errorf("Expected tag dataTypeId to be 'testTag'. Got '%v'", m["dataTypeId"]) + } + if m["changeThreshold"] != 4.5 { + t.Errorf("Expected tag changeThreshold to be '4.5'. Got '%v'", m["changeThreshold"]) + } + if m["guaranteeSec"] != 600.0 { + t.Errorf("Expected tag guaranteeSec to be '600'. Got '%v'", m["guaranteeSec"]) + } + if m["mapFunction"] != "double" { + t.Errorf("Expected tag mapFunction to be 'double'. Got '%v'", m["mapFunction"]) + } + if m["units"] != "in." { + t.Errorf("Expected tag units to be 'in.'. Got '%v'", m["units"]) + } + if m["minExpected"] != 0.0 { + t.Errorf("Expected tag minExpected to be '0.0'. Got '%v'", m["minExpected"]) + } + if m["maxExpected"] != 1000.0 { + t.Errorf("Expected tag maxExpected to be '1000.0'. Got '%v'", m["maxExpected"]) + } + +} + +func TestGetTag(t *testing.T) { + clearTagTable() + addTags(1) + + req, _ := http.NewRequest("GET", "/api/v1/tag/1", nil) + response := executeRequest(req) + + checkResponseCode(t, http.StatusOK, response.Code) +} + +func addTags(count int) { + if count < 1 { + count = 1 + } + sqlQuery := `INSERT INTO tags ( + name, + tagName, + tagClassId, + deviceId, + description, + dataTypeId, + changeThreshold, + guaranteeSec, + mapFunction, + units, + minExpected, + maxExpected, + createdAt, + updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` + insStmt, insErr := a.DB.Prepare(sqlQuery) + 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("Name"+strconv.Itoa(i), "tag"+strconv.Itoa(i), 1, 1, + "Description"+strconv.Itoa(i), 1, i*100.0, 600, + "mapFn"+strconv.Itoa(i), "in.", 0.0, 1000.0, time.Now(), time.Now()) + } +} + +func TestUpdateTag(t *testing.T) { + clearTagTable() + addTags(1) + + req, _ := http.NewRequest("GET", "/api/v1/tag/1", nil) + response := executeRequest(req) + var originalTag map[string]interface{} + json.Unmarshal(response.Body.Bytes(), &originalTag) + + payload := []byte(`{"name":"TestTag","tagName":"testTag","tagClassId":1,"deviceId":1,"description":"This is a test tag","dataTypeId":1,"changeThreshold":4.5,"guaranteeSec":600,"mapFunction":"double","units":"in.2","minExpected":1.0,"maxExpected":1001.0}`) + + req, _ = http.NewRequest("PUT", "/api/v1/tag/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"] != originalTag["id"] { + t.Errorf("Expected the id to remain the same (%v). Got %v", originalTag["id"], m["id"]) + } + + if m["name"] == originalTag["name"] { + t.Errorf("Expected the name to change from '%v' to '%v'. Got '%v'", originalTag["name"], m["name"], m["name"]) + } + + if m["tagName"] == originalTag["tagName"] { + t.Errorf("Expected the tagName to change from '%v' to '%v'. Got '%v'", originalTag["tagName"], m["tagName"], m["tagName"]) + } + + if int(m["tagClassId"].(float64)) == originalTag["tagClassId"] { + t.Errorf("Expected the tagClassId to change from '%v' to '%v'. Got '%v'", originalTag["tagClassId"], m["tagClassId"], m["tagClassId"]) + } + + if int(m["deviceId"].(float64)) == originalTag["deviceId"] { + t.Errorf("Expected the deviceId to change from '%v' to '%v'. Got '%v'", originalTag["deviceId"], m["deviceId"], m["deviceId"]) + } + + if m["description"] == originalTag["description"] { + t.Errorf("Expected the description to change from '%v' to '%v'. Got '%v'", originalTag["description"], m["description"], m["description"]) + } + + if int(m["dataTypeId"].(float64)) == originalTag["dataTypeId"] { + t.Errorf("Expected the dataTypeId to change from '%v' to '%v'. Got '%v'", originalTag["dataTypeId"], m["dataTypeId"], m["dataTypeId"]) + } + + if m["changeThreshold"] == originalTag["changeThreshold"] { + t.Errorf("Expected the changeThreshold to change from '%v' to '%v'. Got '%v'", originalTag["changeThreshold"], m["changeThreshold"], m["changeThreshold"]) + } + + if int(m["guaranteeSec"].(float64)) == originalTag["guaranteeSec"] { + t.Errorf("Expected the guaranteeSec to change from '%v' to '%v'. Got '%v'", originalTag["guaranteeSec"], m["guaranteeSec"], m["guaranteeSec"]) + } + + if m["mapFunction"] == originalTag["mapFunction"] { + t.Errorf("Expected the mapFunction to change from '%v' to '%v'. Got '%v'", originalTag["changeThreshold"], m["changeThreshold"], m["changeThreshold"]) + } + + if m["units"] == originalTag["units"] { + t.Errorf("Expected the units to change from '%v' to '%v'. Got '%v'", reflect.TypeOf(originalTag["units"]), reflect.TypeOf(m["units"]), reflect.TypeOf(m["units"])) + } + + if m["minExpected"] == originalTag["minExpected"] { + t.Errorf("Expected the minExpected to change from '%v' to '%v'. Got '%v'", originalTag["minExpected"], m["minExpected"], m["minExpected"]) + } + + if m["maxExpected"] == originalTag["maxExpected"] { + t.Errorf("Expected the maxExpected to change from '%v' to '%v'. Got '%v'", originalTag["maxExpected"], m["maxExpected"], m["maxExpected"]) + } +} + +func TestDeleteTag(t *testing.T) { + clearTagTable() + addTags(1) + + req, _ := http.NewRequest("GET", "/api/v1/tag/1", nil) + response := executeRequest(req) + checkResponseCode(t, http.StatusOK, response.Code) + + req, _ = http.NewRequest("DELETE", "/api/v1/tag/1", nil) + response = executeRequest(req) + + checkResponseCode(t, http.StatusOK, response.Code) + + req, _ = http.NewRequest("GET", "/api/v1/tag/1", nil) + response = executeRequest(req) + checkResponseCode(t, http.StatusNotFound, response.Code) +} diff --git a/testtagclass_test.go b/testtagclass_test.go new file mode 100644 index 0000000..142abc9 --- /dev/null +++ b/testtagclass_test.go @@ -0,0 +1,139 @@ +package main + +import ( + "bytes" + "encoding/json" + "net/http" + "strconv" + "testing" + "time" +) + +func clearTagClassTable() { + a.DB.Exec("DELETE FROM tagClasses") + a.DB.Exec("ALTER TABLE tagClasses AUTO_INCREMENT = 1") +} + +func TestEmptyTagClassTable(t *testing.T) { + clearTagClassTable() + + req, _ := http.NewRequest("GET", "/api/v1/tagClasses", 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 TestGetNonExistentTagClass(t *testing.T) { + clearTagClassTable() + + req, _ := http.NewRequest("GET", "/api/v1/tagClass/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"] != "TagClass not found" { + t.Errorf("Expected the 'error' key of the response to be set to 'TagClass not found'. Got '%s'", m["error"]) + } +} + +func TestCreateTagClass(t *testing.T) { + clearTagClassTable() + + payload := []byte(`{"classType":"Normal","description":"Historical Data"}`) + + req, _ := http.NewRequest("POST", "/api/v1/tagClass", 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["classType"] != "Normal" { + t.Errorf("Expected tagClass classType to be 'Normal'. Got '%v'", m["classType"]) + } + + if m["description"] != "Historical Data" { + t.Errorf("Expected tagClass description to be 'Historical Data'. Got '%v'", m["description"]) + } +} + +func TestGetTagClass(t *testing.T) { + clearTagClassTable() + addTagClasses(1) + + req, _ := http.NewRequest("GET", "/api/v1/tagClass/1", nil) + response := executeRequest(req) + + checkResponseCode(t, http.StatusOK, response.Code) +} + +func addTagClasses(count int) { + if count < 1 { + count = 1 + } + insStmt, insErr := a.DB.Prepare("INSERT INTO tagClasses(classType, description, 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), "TESTDESCRIPTION"+strconv.Itoa(i), time.Now(), time.Now()) + } +} + +func TestUpdateTagClass(t *testing.T) { + clearTagClassTable() + addTagClasses(1) + + req, _ := http.NewRequest("GET", "/api/v1/tagClass/1", nil) + response := executeRequest(req) + var originalTagClass map[string]interface{} + json.Unmarshal(response.Body.Bytes(), &originalTagClass) + + payload := []byte(`{"classType":"handshake","description":"PLC Handshake"}`) + + req, _ = http.NewRequest("PUT", "/api/v1/tagClass/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"] != originalTagClass["id"] { + t.Errorf("Expected the id to remain the same (%v). Got %v", originalTagClass["id"], m["id"]) + } + + if m["classType"] == originalTagClass["tagClass"] { + t.Errorf("Expected the classType to change from '%v' to '%v'. Got '%v'", originalTagClass["classType"], m["classType"], m["classType"]) + } + + if m["description"] == originalTagClass["description"] { + t.Errorf("Expected the description to change from '%v' to '%v'. Got '%v'", originalTagClass["description"], m["description"], m["description"]) + } +} + +func TestDeleteTagClass(t *testing.T) { + clearTagClassTable() + addTagClasses(1) + + req, _ := http.NewRequest("GET", "/api/v1/tagClass/1", nil) + response := executeRequest(req) + checkResponseCode(t, http.StatusOK, response.Code) + + req, _ = http.NewRequest("DELETE", "/api/v1/tagClass/1", nil) + response = executeRequest(req) + + checkResponseCode(t, http.StatusOK, response.Code) + + req, _ = http.NewRequest("GET", "/api/v1/tagClass/1", nil) + response = executeRequest(req) + checkResponseCode(t, http.StatusNotFound, response.Code) +} diff --git a/testtagvalue_test.go b/testtagvalue_test.go new file mode 100644 index 0000000..0772641 --- /dev/null +++ b/testtagvalue_test.go @@ -0,0 +1,141 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strconv" + "testing" + "time" +) + +func clearTagValueTable() { + a.DB.Exec("DELETE FROM tagValues") + a.DB.Exec("ALTER TABLE tagValues AUTO_INCREMENT = 1") +} + +func TestEmptyTagValueTable(t *testing.T) { + clearTagValueTable() + + req, _ := http.NewRequest("GET", "/api/v1/tagValues", 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 TestGetNonExistentTagValue(t *testing.T) { + clearTagValueTable() + + req, _ := http.NewRequest("GET", "/api/v1/tagValue/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"] != "TagValue not found" { + t.Errorf("Expected the 'error' key of the response to be set to 'TagValue not found'. Got '%s'", m["error"]) + } +} + +func TestCreateTagValue(t *testing.T) { + clearTagValueTable() + addTags(1) + + payload := []byte(`{"tagId":1,"val":"1234.56"}`) + + req, _ := http.NewRequest("POST", "/api/v1/tagValue", bytes.NewBuffer(payload)) + response := executeRequest(req) + fmt.Printf("\n\n%s\n\n", response.Body) + checkResponseCode(t, http.StatusCreated, response.Code) + + var m map[string]interface{} + json.Unmarshal(response.Body.Bytes(), &m) + + if m["tagId"] != 1.0 { + t.Errorf("Expected tagValue name to be '1'. Got '%v'", m["name"]) + } + if m["val"] != "1234.56" { + t.Errorf("Expected tagValue address to be '1234.56'. Got '%v'", m["address"]) + } + +} + +func TestGetTagValue(t *testing.T) { + clearTagValueTable() + addTagValues(1) + + req, _ := http.NewRequest("GET", "/api/v1/tagValue/1", nil) + response := executeRequest(req) + + checkResponseCode(t, http.StatusOK, response.Code) +} + +func addTagValues(count int) { + if count < 1 { + count = 1 + } + insStmt, insErr := a.DB.Prepare("INSERT INTO tagValues(tagId, 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(1, strconv.Itoa(i*10), time.Now(), time.Now()) + } +} + +func TestUpdateTagValue(t *testing.T) { + clearTagValueTable() + addTagValues(1) + + req, _ := http.NewRequest("GET", "/api/v1/tagValue/1", nil) + response := executeRequest(req) + var originalTagValue map[string]interface{} + json.Unmarshal(response.Body.Bytes(), &originalTagValue) + + payload := []byte(`{"tagId":1,"val":"111.111"}`) + + req, _ = http.NewRequest("PUT", "/api/v1/tagValue/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"] != originalTagValue["id"] { + t.Errorf("Expected the id to remain the same (%v). Got %v", originalTagValue["id"], m["id"]) + } + + if m["tagId"] != originalTagValue["tagId"] { + t.Errorf("Expected the id to remain the same (%v). Got %v", originalTagValue["tagId"], m["tagId"]) + } + + if m["val"] == originalTagValue["val"] { + t.Errorf("Expected the val to change from '%v' to '%v'. Got '%v'", originalTagValue["val"], m["val"], m["val"]) + } +} + +func TestDeleteTagValue(t *testing.T) { + clearTagValueTable() + addTagValues(1) + + req, _ := http.NewRequest("GET", "/api/v1/tagValue/1", nil) + response := executeRequest(req) + checkResponseCode(t, http.StatusOK, response.Code) + + req, _ = http.NewRequest("DELETE", "/api/v1/tagValue/1", nil) + response = executeRequest(req) + + checkResponseCode(t, http.StatusOK, response.Code) + + req, _ = http.NewRequest("GET", "/api/v1/tagValue/1", nil) + response = executeRequest(req) + checkResponseCode(t, http.StatusNotFound, response.Code) +}