Added route for saving device info #7
@@ -32,11 +32,11 @@ func Load() error {
|
||||
Environment: getEnv("NOVATECH_ADMIN_SERVICE_ENVIRONMENT", "DEV"),
|
||||
},
|
||||
Database: Database{
|
||||
UserName: mustGetEnv("NOVATECH_DATABASE_USERNAME"),
|
||||
Password: mustGetEnv("NOVATECH_DATABASE_PASSWORD"),
|
||||
DatabaseName: mustGetEnv("NOVATECH_DATABASE_NAME"),
|
||||
HostName: mustGetEnv("NOVATECH_DATABASE_ADDRESS"),
|
||||
Port: mustGetEnv("NOVATECH_DATABASE_PORT"),
|
||||
UserName: getEnv("NOVATECH_DATABASE_USERNAME", "root"),
|
||||
Password: getEnv("NOVATECH_DATABASE_PASSWORD", "root"),
|
||||
DatabaseName: getEnv("NOVATECH_DATABASE_NAME", "postgres"),
|
||||
HostName: getEnv("NOVATECH_DATABASE_ADDRESS", "localhost"),
|
||||
Port: getEnv("NOVATECH_DATABASE_PORT", " "),
|
||||
|
||||
},
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package controllers
|
||||
|
||||
// import (
|
||||
// "net/http"
|
||||
// models "gitlab.com/pactual1/backend/models"
|
||||
|
||||
// "gitlab.com/pactual1/backend/models"
|
||||
// "github.com/gin-gonic/gin"
|
||||
// )
|
||||
|
||||
// func Companies(c *gin.Context) {
|
||||
// var companies []models.Company
|
||||
// // Fetch companies from DB here
|
||||
// if err := models.FetchCompanies(&companies); err != nil {
|
||||
// c.JSON(http.StatusInternalServerError, gin.H{"error": "Error fetching companies"})
|
||||
// return
|
||||
// }
|
||||
// c.JSON(http.StatusOK, gin.H{"companies": companies})
|
||||
// }
|
||||
50
controllers/devices_controller.go
Normal file
50
controllers/devices_controller.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gitlab.com/pactual1/backend/models"
|
||||
"gitlab.com/pactual1/backend/shared"
|
||||
)
|
||||
|
||||
func SaveDeviceInfofunc(c *gin.Context) {
|
||||
var deviceInfo models.DeviceInfo
|
||||
rawData, _ := c.GetRawData()
|
||||
|
||||
// Unmarshal to the important info structure
|
||||
err := json.Unmarshal(rawData, &deviceInfo)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON payload"})
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal to the deviceInfo structure
|
||||
err = json.Unmarshal(rawData, &deviceInfo)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON payload"})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
deviceInfo.RawJSON = string(rawData)
|
||||
|
||||
// Attempt to find the device by IMEI; if found, set the DeviceID
|
||||
var device models.Device
|
||||
if err := shared.GetDb().Where("imei = ?", deviceInfo.IMEI).First(&device).Error; err == nil {
|
||||
deviceInfo.DeviceID = device.ID
|
||||
} else {
|
||||
log.Printf("Could not find device with imei %v", deviceInfo.IMEI)
|
||||
}
|
||||
|
||||
|
||||
// Save deviceInfo to your database
|
||||
if err := shared.GetDb().Create(&deviceInfo).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not save device info"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Successfully received and saved device info", "data": deviceInfo})
|
||||
}
|
||||
82
controllers/devices_controller_test.go
Normal file
82
controllers/devices_controller_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"gitlab.com/pactual1/backend/config"
|
||||
"gitlab.com/pactual1/backend/shared"
|
||||
)
|
||||
|
||||
// Assume setupServer() returns your gin.Engine instance
|
||||
func runTestServer() *httptest.Server {
|
||||
// Initialize your database or any other setup
|
||||
// For the sake of this example, I'll just return the Gin engine
|
||||
|
||||
// LOAD APPLICATION CONFIGURATION
|
||||
err := config.Load()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
shared.Init()
|
||||
r := gin.Default()
|
||||
r.POST("/device_info", SaveDeviceInfofunc)
|
||||
return httptest.NewServer(r)
|
||||
}
|
||||
|
||||
func TestSaveDeviceInfofunc(t *testing.T) {
|
||||
ts := runTestServer()
|
||||
defer ts.Close()
|
||||
defer shared.CloseDb()
|
||||
|
||||
t.Run("it should return 400 when JSON payload is invalid", func(t *testing.T) {
|
||||
payload := []byte(`{"InvalidPayload": `)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/device_info", ts.URL), "application/json", bytes.NewBuffer(payload))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
|
||||
assert.Equal(t, 400, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("it should log a message when IMEI is not found", func(t *testing.T) {
|
||||
// Add a valid payload but with an unknown IMEI
|
||||
payload := []byte(`{"IMEI": "UNKNOWN", "SomeOtherField": "Value"}`)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/device_info", ts.URL), "application/json", bytes.NewBuffer(payload))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
// Here, ideally you'd want to check your logs to ensure the message was logged
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("it should return 200 when all conditions are correct", func(t *testing.T) {
|
||||
// Add a valid payload
|
||||
deviceInfo := map[string]interface{}{
|
||||
"IMEI": "SOME_VALID_IMEI",
|
||||
"SomeOtherField": "Value",
|
||||
}
|
||||
|
||||
payload, _ := json.Marshal(deviceInfo)
|
||||
resp, err := http.Post(fmt.Sprintf("%s/device_info", ts.URL), "application/json", bytes.NewBuffer(payload))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
3
go.mod
3
go.mod
@@ -8,6 +8,7 @@ require (
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/qor/admin v1.2.0
|
||||
github.com/stretchr/testify v1.8.3
|
||||
)
|
||||
|
||||
replace gitlab.com/pactual1/backend => ./
|
||||
@@ -18,6 +19,7 @@ require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/chris-ramon/douceur v0.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
@@ -40,6 +42,7 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/qor/assetfs v0.0.0-20170713023933-ff57fdc13a14 // indirect
|
||||
github.com/qor/middlewares v0.0.0-20170822143614-781378b69454 // indirect
|
||||
github.com/qor/qor v0.0.0-20210618082622-a52aba0a0ce1 // indirect
|
||||
|
||||
5
main.go
5
main.go
@@ -11,7 +11,6 @@ import (
|
||||
"gitlab.com/pactual1/backend/services/contact"
|
||||
"gitlab.com/pactual1/backend/services/erp"
|
||||
"gitlab.com/pactual1/backend/services/messaging"
|
||||
"gitlab.com/pactual1/backend/services/sensor"
|
||||
"gitlab.com/pactual1/backend/shared"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -44,6 +43,7 @@ func main() {
|
||||
company := Admin.AddResource(&models.Company{})
|
||||
company.Meta(&admin.Meta{Name: "Users", Config: &admin.SelectManyConfig{SelectMode: "bottom_sheet"}})
|
||||
company.Meta(&admin.Meta{Name: "Devices", Config: &admin.SelectManyConfig{SelectMode: "bottom_sheet"}})
|
||||
// company.Meta(&admin.Meta{Name: "DeviceInfos", Config: &admin.SelectManyConfig{SelectMode: "bottom_sheet"}})
|
||||
|
||||
// Add User and Device resources
|
||||
Admin.AddResource(&models.User{})
|
||||
@@ -63,19 +63,16 @@ func main() {
|
||||
// Initialize channels
|
||||
messagingChannel := make(chan string)
|
||||
erpChannel := make(chan string)
|
||||
sensorChannel := make(chan string)
|
||||
contactChannel := make(chan string)
|
||||
|
||||
// Start services and pass the respective channels
|
||||
go messaging.MessagingService(messagingChannel)
|
||||
go erp.ERPService(erpChannel)
|
||||
go sensor.SensorService(sensorChannel)
|
||||
go contact.ContactService(contactChannel)
|
||||
|
||||
// Sending messages via channels
|
||||
messagingChannel <- "Send an email"
|
||||
erpChannel <- "Update ERP record"
|
||||
sensorChannel <- "Trigger sensor"
|
||||
contactChannel <- "Update contact info"
|
||||
|
||||
// Initialize Gin
|
||||
|
||||
@@ -5,8 +5,11 @@ import "github.com/jinzhu/gorm"
|
||||
type Device struct {
|
||||
gorm.Model
|
||||
DeviceName string
|
||||
IMEI string `json:"imei"`
|
||||
IMSI string `json:"imsi"`
|
||||
DeviceConfiguration string `gorm:"type:json"`
|
||||
CompanyID uint
|
||||
DeviceInfos []DeviceInfo
|
||||
}
|
||||
|
||||
func (Device)Update() (bool, error) {
|
||||
|
||||
39
models/device_info.go
Normal file
39
models/device_info.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package models
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
|
||||
// Location holds latitude and longitude.
|
||||
type Location struct {
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
}
|
||||
|
||||
// ImportantInfo holds fields that are important for quick access.
|
||||
type SensorData struct {
|
||||
IMEI string `json:"imei"`
|
||||
IMSI string `json:"imsi"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
WifiLoc Location `json:"wifi_location"`
|
||||
CellLoc Location `json:"cell_location"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
}
|
||||
|
||||
|
||||
type DeviceInfo struct {
|
||||
gorm.Model
|
||||
RawJSON string `json:"raw_json" gorm:"type:json"`
|
||||
SensorData
|
||||
DeviceID uint
|
||||
}
|
||||
|
||||
func (DeviceInfo)Update() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
func (DeviceInfo)Create() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
func (DeviceInfo)Delete() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
@@ -9,6 +9,6 @@ import (
|
||||
func RegisterPublicRoutes(r *gin.Engine){
|
||||
|
||||
r.GET("/publicmessage", controllers.GetPublicText)
|
||||
// r.GET("/companies", controllers.Ge)
|
||||
r.POST("/device_info", controllers.SaveDeviceInfofunc)
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package sensor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func SensorService(ch chan string) {
|
||||
for msg := range ch {
|
||||
fmt.Println("Sensor Service: ", msg)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ func Init() error{
|
||||
return err
|
||||
}
|
||||
//TODO AUTOMIGRATE models once we have them
|
||||
db.AutoMigrate(&models.User{}, &models.Company{}, &models.Device{})
|
||||
db.AutoMigrate(&models.User{}, &models.Company{}, &models.Device{}, &models.DeviceInfo{})
|
||||
|
||||
return nil
|
||||
|
||||
|
||||
Reference in New Issue
Block a user