From 386f051d678ec7882f6f66110a500802f15a933e Mon Sep 17 00:00:00 2001 From: Nedim Date: Tue, 12 Sep 2023 18:28:18 +0200 Subject: [PATCH 1/3] Added route for saving device info --- controllers/DevicesController.go | 31 ++++++++++++++++++++++++ main.go | 5 +--- models/device.go | 1 + models/device_info.go | 39 +++++++++++++++++++++++++++++++ routes/publicRoutes.go | 2 +- services/sensor/sensor_service.go | 12 ---------- shared/database.go | 2 +- 7 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 controllers/DevicesController.go create mode 100644 models/device_info.go delete mode 100644 services/sensor/sensor_service.go diff --git a/controllers/DevicesController.go b/controllers/DevicesController.go new file mode 100644 index 0000000..540c848 --- /dev/null +++ b/controllers/DevicesController.go @@ -0,0 +1,31 @@ +package controllers + +import ( + "encoding/json" + "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 + } + + // Convert raw JSON bytes to string + deviceInfo.RawJSON = string(rawData) + + // Save deviceInfo to your database here + db :=shared.GetDb() + db.Create(&deviceInfo) + + c.JSON(http.StatusOK, gin.H{"message": "Successfully received device info", "data": deviceInfo}) +} diff --git a/main.go b/main.go index a283752..cfb5b3e 100644 --- a/main.go +++ b/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 diff --git a/models/device.go b/models/device.go index 7e03d18..7eebea5 100644 --- a/models/device.go +++ b/models/device.go @@ -7,6 +7,7 @@ type Device struct { DeviceName string DeviceConfiguration string `gorm:"type:json"` CompanyID uint + DeviceInfos []DeviceInfo } func (Device)Update() (bool, error) { diff --git a/models/device_info.go b/models/device_info.go new file mode 100644 index 0000000..8c66742 --- /dev/null +++ b/models/device_info.go @@ -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 +} diff --git a/routes/publicRoutes.go b/routes/publicRoutes.go index c26c1a8..9069c1b 100644 --- a/routes/publicRoutes.go +++ b/routes/publicRoutes.go @@ -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) } diff --git a/services/sensor/sensor_service.go b/services/sensor/sensor_service.go deleted file mode 100644 index 829e248..0000000 --- a/services/sensor/sensor_service.go +++ /dev/null @@ -1,12 +0,0 @@ -package sensor - -import ( - "fmt" -) - -func SensorService(ch chan string) { - for msg := range ch { - fmt.Println("Sensor Service: ", msg) - - } -} diff --git a/shared/database.go b/shared/database.go index e070c8d..7bf023e 100644 --- a/shared/database.go +++ b/shared/database.go @@ -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 -- 2.47.3 From 1353404aed5b1e8d71e1b98fc815a872bcaff752 Mon Sep 17 00:00:00 2001 From: Nedim Date: Wed, 13 Sep 2023 21:54:43 +0200 Subject: [PATCH 2/3] Added logic for finding devices --- config/config.go | 10 ++-- controllers/CompaniesController.go | 19 ------- controllers/DevicesController.go | 29 ++++++++-- controllers/DevicesController_test.go | 81 +++++++++++++++++++++++++++ go.mod | 3 + models/device.go | 2 + 6 files changed, 115 insertions(+), 29 deletions(-) delete mode 100644 controllers/CompaniesController.go create mode 100644 controllers/DevicesController_test.go diff --git a/config/config.go b/config/config.go index 69e6155..b975d4d 100644 --- a/config/config.go +++ b/config/config.go @@ -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", " "), }, diff --git a/controllers/CompaniesController.go b/controllers/CompaniesController.go deleted file mode 100644 index e93863a..0000000 --- a/controllers/CompaniesController.go +++ /dev/null @@ -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}) -// } \ No newline at end of file diff --git a/controllers/DevicesController.go b/controllers/DevicesController.go index 540c848..b6e9736 100644 --- a/controllers/DevicesController.go +++ b/controllers/DevicesController.go @@ -2,6 +2,7 @@ package controllers import ( "encoding/json" + "log" "net/http" "github.com/gin-gonic/gin" @@ -20,12 +21,30 @@ func SaveDeviceInfofunc(c *gin.Context) { return } - // Convert raw JSON bytes to string + // 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) - // Save deviceInfo to your database here - db :=shared.GetDb() - db.Create(&deviceInfo) + // 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) + } - c.JSON(http.StatusOK, gin.H{"message": "Successfully received device info", "data": deviceInfo}) + + // 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}) } diff --git a/controllers/DevicesController_test.go b/controllers/DevicesController_test.go new file mode 100644 index 0000000..a3fde9d --- /dev/null +++ b/controllers/DevicesController_test.go @@ -0,0 +1,81 @@ +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() + + 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) + }) +} diff --git a/go.mod b/go.mod index 0654b15..0d76ab8 100644 --- a/go.mod +++ b/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 diff --git a/models/device.go b/models/device.go index 7eebea5..f18c8cb 100644 --- a/models/device.go +++ b/models/device.go @@ -5,6 +5,8 @@ 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 -- 2.47.3 From dcf1652dd97bcc59b769fddcc4fb83e8f77686e9 Mon Sep 17 00:00:00 2001 From: Nedim Date: Thu, 14 Sep 2023 08:10:33 +0200 Subject: [PATCH 3/3] Fixed naming --- controllers/{AuthController.go => auth_controller.go} | 0 controllers/{DevicesController.go => devices_controller.go} | 0 .../{DevicesController_test.go => devices_controller_test.go} | 1 + controllers/{SampleController.go => simple_controller.go} | 0 routes/{protectedRoutes.go => protected_routes.go} | 0 routes/{publicRoutes.go => public_routes.go} | 0 routes/{utilityRoutes.go => utility_routes.go} | 0 shared/{userData.go => user_data.go} | 0 8 files changed, 1 insertion(+) rename controllers/{AuthController.go => auth_controller.go} (100%) rename controllers/{DevicesController.go => devices_controller.go} (100%) rename controllers/{DevicesController_test.go => devices_controller_test.go} (98%) rename controllers/{SampleController.go => simple_controller.go} (100%) rename routes/{protectedRoutes.go => protected_routes.go} (100%) rename routes/{publicRoutes.go => public_routes.go} (100%) rename routes/{utilityRoutes.go => utility_routes.go} (100%) rename shared/{userData.go => user_data.go} (100%) diff --git a/controllers/AuthController.go b/controllers/auth_controller.go similarity index 100% rename from controllers/AuthController.go rename to controllers/auth_controller.go diff --git a/controllers/DevicesController.go b/controllers/devices_controller.go similarity index 100% rename from controllers/DevicesController.go rename to controllers/devices_controller.go diff --git a/controllers/DevicesController_test.go b/controllers/devices_controller_test.go similarity index 98% rename from controllers/DevicesController_test.go rename to controllers/devices_controller_test.go index a3fde9d..3587e95 100644 --- a/controllers/DevicesController_test.go +++ b/controllers/devices_controller_test.go @@ -36,6 +36,7 @@ func runTestServer() *httptest.Server { 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": `) diff --git a/controllers/SampleController.go b/controllers/simple_controller.go similarity index 100% rename from controllers/SampleController.go rename to controllers/simple_controller.go diff --git a/routes/protectedRoutes.go b/routes/protected_routes.go similarity index 100% rename from routes/protectedRoutes.go rename to routes/protected_routes.go diff --git a/routes/publicRoutes.go b/routes/public_routes.go similarity index 100% rename from routes/publicRoutes.go rename to routes/public_routes.go diff --git a/routes/utilityRoutes.go b/routes/utility_routes.go similarity index 100% rename from routes/utilityRoutes.go rename to routes/utility_routes.go diff --git a/shared/userData.go b/shared/user_data.go similarity index 100% rename from shared/userData.go rename to shared/user_data.go -- 2.47.3