feat(controller): remove enterprise switch + feat: filter devices

This commit is contained in:
leandrofars 2024-09-05 10:05:57 -03:00
parent 32acb8e9ad
commit badeaf9820
13 changed files with 1197 additions and 481 deletions

View File

@ -17,37 +17,30 @@ import (
) )
type Api struct { type Api struct {
port string port string
js jetstream.JetStream js jetstream.JetStream
nc *nats.Conn nc *nats.Conn
bridge bridge.Bridge bridge bridge.Bridge
db db.Database db db.Database
kv jetstream.KeyValue kv jetstream.KeyValue
ctx context.Context ctx context.Context
enterpise config.Enterprise
} }
const REQUEST_TIMEOUT = time.Second * 30 const REQUEST_TIMEOUT = time.Second * 30
func NewApi(c *config.Config, js jetstream.JetStream, nc *nats.Conn, bridge bridge.Bridge, d db.Database, kv jetstream.KeyValue) Api { func NewApi(c *config.Config, js jetstream.JetStream, nc *nats.Conn, bridge bridge.Bridge, d db.Database, kv jetstream.KeyValue) Api {
return Api{ return Api{
port: c.RestApi.Port, port: c.RestApi.Port,
js: js, js: js,
nc: nc, nc: nc,
ctx: c.RestApi.Ctx, ctx: c.RestApi.Ctx,
bridge: bridge, bridge: bridge,
db: d, db: d,
kv: kv, kv: kv,
enterpise: c.Enterprise,
} }
} }
func (a *Api) StartApi() { func (a *Api) StartApi() {
if a.enterpise.SupportPassword != "" && a.enterpise.SupportEmail != "" {
go registerEnterpriseSupport(a.enterpise.SupportEmail, a.enterpise.SupportPassword, a.db)
}
r := mux.NewRouter() r := mux.NewRouter()
authentication := r.PathPrefix("/api/auth").Subrouter() authentication := r.PathPrefix("/api/auth").Subrouter()
authentication.HandleFunc("/login", a.generateToken).Methods("PUT") authentication.HandleFunc("/login", a.generateToken).Methods("PUT")
@ -57,13 +50,6 @@ func (a *Api) StartApi() {
authentication.HandleFunc("/password", a.changePassword).Methods("PUT") authentication.HandleFunc("/password", a.changePassword).Methods("PUT")
authentication.HandleFunc("/admin/register", a.registerAdminUser).Methods("POST") authentication.HandleFunc("/admin/register", a.registerAdminUser).Methods("POST")
authentication.HandleFunc("/admin/exists", a.adminUserExists).Methods("GET") authentication.HandleFunc("/admin/exists", a.adminUserExists).Methods("GET")
if a.enterpise.Enable {
mapRoutes := r.PathPrefix("/api/map").Subrouter()
mapRoutes.HandleFunc("", a.devicesLocation).Methods("GET")
mapRoutes.Use(func(handler http.Handler) http.Handler {
return middleware.Middleware(handler)
})
}
iot := r.PathPrefix("/api/device").Subrouter() iot := r.PathPrefix("/api/device").Subrouter()
iot.HandleFunc("/alias", a.setDeviceAlias).Methods("PUT") iot.HandleFunc("/alias", a.setDeviceAlias).Methods("PUT")
iot.HandleFunc("/auth", a.deviceAuth).Methods("GET", "POST", "DELETE") iot.HandleFunc("/auth", a.deviceAuth).Methods("GET", "POST", "DELETE")
@ -74,7 +60,7 @@ func (a *Api) StartApi() {
iot.HandleFunc("/cwmp/{sn}/addObject", a.cwmpAddObjectMsg).Methods("PUT") iot.HandleFunc("/cwmp/{sn}/addObject", a.cwmpAddObjectMsg).Methods("PUT")
iot.HandleFunc("/cwmp/{sn}/deleteObject", a.cwmpDeleteObjectMsg).Methods("PUT") iot.HandleFunc("/cwmp/{sn}/deleteObject", a.cwmpDeleteObjectMsg).Methods("PUT")
iot.HandleFunc("", a.retrieveDevices).Methods("GET") iot.HandleFunc("", a.retrieveDevices).Methods("GET")
iot.HandleFunc("/{id}", a.retrieveDevices).Methods("GET") iot.HandleFunc("/filterOptions", a.filterOptions).Methods("GET")
iot.HandleFunc("/{sn}/{mtp}/get", a.deviceGetMsg).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/get", a.deviceGetMsg).Methods("PUT")
iot.HandleFunc("/{sn}/{mtp}/add", a.deviceCreateMsg).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/add", a.deviceCreateMsg).Methods("PUT")
iot.HandleFunc("/{sn}/{mtp}/del", a.deviceDeleteMsg).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/del", a.deviceDeleteMsg).Methods("PUT")
@ -84,13 +70,6 @@ func (a *Api) StartApi() {
iot.HandleFunc("/{sn}/{mtp}/instances", a.deviceGetParameterInstances).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/instances", a.deviceGetParameterInstances).Methods("PUT")
iot.HandleFunc("/{sn}/{mtp}/operate", a.deviceOperateMsg).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/operate", a.deviceOperateMsg).Methods("PUT")
iot.HandleFunc("/{sn}/{mtp}/fw_update", a.deviceFwUpdate).Methods("PUT") //TODO: put it to work and generalize for usp and cwmp iot.HandleFunc("/{sn}/{mtp}/fw_update", a.deviceFwUpdate).Methods("PUT") //TODO: put it to work and generalize for usp and cwmp
if a.enterpise.Enable {
iot.HandleFunc("/{sn}/sitesurvey", a.deviceSiteSurvey).Methods("GET")
iot.HandleFunc("/{sn}/connecteddevices", a.deviceConnectedDevices).Methods("GET")
iot.HandleFunc("/{sn}/traceroute", a.deviceTraceRoute).Methods("GET", "PUT")
iot.HandleFunc("/{sn}/speedtest", a.deviceSpeedTest).Methods("PUT")
iot.HandleFunc("/{sn}/ping", a.devicePing).Methods("PUT", "GET")
}
iot.HandleFunc("/{sn}/wifi", a.deviceWifi).Methods("PUT", "GET") iot.HandleFunc("/{sn}/wifi", a.deviceWifi).Methods("PUT", "GET")
dash := r.PathPrefix("/api/info").Subrouter() dash := r.PathPrefix("/api/info").Subrouter()
dash.HandleFunc("/vendors", a.vendorsInfo).Methods("GET") dash.HandleFunc("/vendors", a.vendorsInfo).Methods("GET")
@ -131,32 +110,3 @@ func (a *Api) StartApi() {
}() }()
log.Println("Running REST API at port", a.port) log.Println("Running REST API at port", a.port)
} }
func registerEnterpriseSupport(email, password string, d db.Database) {
user := db.User{
Email: email,
Password: password,
Name: "Enterprise Support",
Level: db.AdminUser,
}
for {
if err := user.HashPassword(password); err != nil {
return
}
err := d.RegisterUser(user)
if err != nil {
if err == db.ErrorUserExists {
log.Println("Enterprise support user already registered.")
return
}
log.Println("Error to register enterprise support user:", err)
time.Sleep(time.Second * 5)
continue
}
log.Println("Enterprise support user registered successfully.")
return
}
}

View File

@ -6,9 +6,11 @@ import (
"log" "log"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"github.com/leandrofars/oktopus/internal/bridge" "github.com/leandrofars/oktopus/internal/bridge"
"github.com/leandrofars/oktopus/internal/db" "github.com/leandrofars/oktopus/internal/db"
"github.com/leandrofars/oktopus/internal/entity"
local "github.com/leandrofars/oktopus/internal/nats" local "github.com/leandrofars/oktopus/internal/nats"
"github.com/leandrofars/oktopus/internal/utils" "github.com/leandrofars/oktopus/internal/utils"
"github.com/nats-io/nats.go/jetstream" "github.com/nats-io/nats.go/jetstream"
@ -16,6 +18,33 @@ import (
) )
func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) { func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodDelete {
id := r.URL.Query().Get("id")
if id == "" {
w.WriteHeader(http.StatusBadRequest)
err := json.NewEncoder(w).Encode("No id provided")
if err != nil {
log.Println(err)
}
return
}
ids := strings.Split(id, ",")
msg, err := bridge.NatsReq[int64](local.NATS_ADAPTER_SUBJECT+"devices.delete", utils.Marshall(ids), w, a.nc)
if err != nil {
return
}
err = json.NewEncoder(w).Encode(map[string]interface{}{
"number_of_deleted_devices": msg.Msg,
})
if err != nil {
log.Println(err)
}
return
}
const PAGE_SIZE_LIMIT = 50 const PAGE_SIZE_LIMIT = 50
const PAGE_SIZE_DEFAULT = 20 const PAGE_SIZE_DEFAULT = 20
@ -33,9 +62,36 @@ func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) {
return return
} }
statusOrderFromUser := r.URL.Query().Get("statusOrder")
var statusOrder int
if statusOrderFromUser != "" {
if statusOrderFromUser == "asc" {
statusOrder = 1
} else if statusOrderFromUser == "desc" {
statusOrder = -1
} else {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode("Status order must be 'asc' or 'desc'")
return
}
} else {
statusOrder = 1
}
sort := bson.M{}
sort["status"] = statusOrder
version := r.URL.Query().Get("version")
vendor := r.URL.Query().Get("vendor")
productClass := r.URL.Query().Get("type")
alias := r.URL.Query().Get("alias")
model := r.URL.Query().Get("model")
status := r.URL.Query().Get("status")
// Get devices with pagination // Get devices with pagination
page_n := r.URL.Query().Get("page_number") page_n := r.URL.Query().Get("page_number")
page_s := r.URL.Query().Get("page_size") page_s := r.URL.Query().Get("page_size")
var err error var err error
var page_number int64 var page_number int64
@ -70,52 +126,69 @@ func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) {
page_size = PAGE_SIZE_DEFAULT page_size = PAGE_SIZE_DEFAULT
} }
total, err := getDeviceCount(w, a.nc)
if err != nil {
return
}
skip := page_number * (page_size - 1) skip := page_number * (page_size - 1)
if total < page_size {
skip = 0 filter := map[string]interface{}{
"status_order": statusOrder,
"limit": page_size,
"skip": skip,
} }
//TODO: fix status ordering if version != "" {
statusOrder := r.URL.Query().Get("status") filter["version"] = version
if statusOrder != "" { }
if statusOrder == "asc" { if vendor != "" {
statusOrder = "1" filter["vendor"] = vendor
} else if statusOrder == "desc" { }
statusOrder = "-1" if productClass != "" {
} else { filter["productClass"] = productClass
}
if alias != "" {
filter["alias"] = alias
}
if model != "" {
filter["model"] = model
}
if status != "" {
fmtStatus, err := strconv.Atoi(status)
if err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode("Status order must be 'asc' or 'desc'") json.NewEncoder(w).Encode("Status must be an integer")
return return
} }
} filter["status"] = fmtStatus
sort := bson.M{}
sort["status"] = 1
//TODO: Create filters
filter := bson.A{
//bson.M{"$match": filter},
bson.M{"$sort": sort}, // shows online devices first
bson.M{"$skip": skip},
bson.M{"$limit": page_size},
} }
devices, err := getDevices(w, filter, a.nc) devices, err := getDevices(w, filter, a.nc)
if err != nil { if err != nil {
log.Println("Error getting devices", err)
return
}
if devices.Total == 0 {
w.WriteHeader(http.StatusNotFound)
err := json.NewEncoder(w).Encode("No devices found")
if err != nil {
log.Println(err)
}
return
}
if skip >= devices.Total {
w.WriteHeader(http.StatusBadRequest)
err := json.NewEncoder(w).Encode("Page number is out of range")
if err != nil {
log.Println(err)
}
return return
} }
err = json.NewEncoder(w).Encode(map[string]interface{}{ err = json.NewEncoder(w).Encode(map[string]interface{}{
"pages": total / page_size, "pages": devices.Total / page_size,
"page": page_number, "page": page_number,
"size": page_size, "size": page_size,
"devices": devices, "devices": devices.Devices,
"total": devices.Total,
}) })
if err != nil { if err != nil {
log.Println(err) log.Println(err)
@ -283,3 +356,14 @@ func (a *Api) setDeviceAlias(w http.ResponseWriter, r *http.Request) {
return return
} }
} }
func (a *Api) filterOptions(w http.ResponseWriter, r *http.Request) {
resp, err := bridge.NatsReq[entity.FilterOptions](local.NATS_ADAPTER_SUBJECT+"devices.filterOptions", nil, w, a.nc)
if err != nil {
return
}
w.WriteHeader(resp.Code)
w.Write(utils.Marshall(resp.Msg))
}

View File

@ -1,205 +0,0 @@
package api
import (
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/leandrofars/oktopus/internal/bridge"
"github.com/leandrofars/oktopus/internal/entity"
"github.com/leandrofars/oktopus/internal/utils"
)
func (a *Api) getEnterpriseResource(
resource string,
action string,
device *entity.Device,
sn string,
w http.ResponseWriter,
body []byte,
protocol, datamodel string,
) error {
model, err := cwmpGetDeviceModel(device, w)
if err != nil {
return err
}
err = bridge.NatsEnterpriseInteraction("enterprise.v1."+protocol+"."+datamodel+"."+model+"."+sn+"."+resource+"."+action, body, w, a.nc)
return err
}
func (a *Api) getMapsResource(
action string,
w http.ResponseWriter,
body []byte,
) error {
err := bridge.NatsEnterpriseInteraction("geolocation.v1."+action, body, w, a.nc)
return err
}
func (a *Api) devicesLocation(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
a.getMapsResource("get", w, []byte{})
}
}
func (a *Api) deviceSiteSurvey(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sn := vars["sn"]
device, err := getDeviceInfo(w, sn, a.nc)
if err != nil {
return
}
if r.Method == http.MethodGet {
if device.Cwmp == entity.Online {
a.getEnterpriseResource("sitesurvey", "get", device, sn, w, []byte{}, "cwmp", "098")
return
}
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
w.WriteHeader(http.StatusNotImplemented)
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
return
}
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall("Device is Offline"))
}
}
func (a *Api) deviceConnectedDevices(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sn := vars["sn"]
device, err := getDeviceInfo(w, sn, a.nc)
if err != nil {
return
}
if r.Method == http.MethodGet {
if device.Cwmp == entity.Online {
a.getEnterpriseResource("connecteddevices", "get", device, sn, w, []byte{}, "cwmp", "098")
return
}
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
w.WriteHeader(http.StatusNotImplemented)
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
return
}
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall("Device is Offline"))
}
}
func (a *Api) deviceTraceRoute(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sn := vars["sn"]
device, err := getDeviceInfo(w, sn, a.nc)
if err != nil {
return
}
if r.Method == http.MethodGet {
if device.Cwmp == entity.Online {
w.WriteHeader(http.StatusNotImplemented)
w.Write(utils.Marshall("Get traceroute configuration is not implemented yet"))
}
}
if r.Method == http.MethodPut {
if device.Cwmp == entity.Online {
a.getEnterpriseResource("traceroute", "set", device, sn, w, []byte{}, "cwmp", "098")
return
}
}
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
w.WriteHeader(http.StatusNotImplemented)
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
return
}
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall("Device is Offline"))
}
func (a *Api) deviceSpeedTest(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sn := vars["sn"]
device, err := getDeviceInfo(w, sn, a.nc)
if err != nil {
return
}
payload, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall("Error reading request body"))
return
}
if device.Cwmp == entity.Online {
a.getEnterpriseResource("speedTest", "set", device, sn, w, payload, "cwmp", "098")
return
}
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
w.WriteHeader(http.StatusNotImplemented)
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
return
}
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall("Device is Offline"))
}
func (a *Api) devicePing(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sn := vars["sn"]
device, err := getDeviceInfo(w, sn, a.nc)
if err != nil {
return
}
if device.Cwmp != entity.Online {
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall("Device is Offline"))
}
if r.Method == http.MethodGet {
if device.Cwmp == entity.Online {
a.getEnterpriseResource("ping", "get", device, sn, w, []byte{}, "cwmp", "098")
return
}
} else {
payload, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall("Error reading request body"))
return
}
if device.Cwmp == entity.Online {
a.getEnterpriseResource("ping", "set", device, sn, w, payload, "cwmp", "098")
return
}
}
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
w.WriteHeader(http.StatusNotImplemented)
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
return
}
}

View File

@ -176,7 +176,7 @@ func (a *Api) registerAdminUser(w http.ResponseWriter, r *http.Request) {
utils.MarshallEncoder(err, w) utils.MarshallEncoder(err, w)
} }
if !adminUserExists(users, a.enterpise.SupportEmail) { if !adminUserExists(users) {
var user db.User var user db.User
err = json.NewDecoder(r.Body).Decode(&user) err = json.NewDecoder(r.Body).Decode(&user)
if err != nil { if err != nil {
@ -235,14 +235,14 @@ func (a *Api) registerAdminUser(w http.ResponseWriter, r *http.Request) {
} }
} }
func adminUserExists(users []map[string]interface{}, supportEmail string) bool { func adminUserExists(users []map[string]interface{}) bool {
if len(users) == 0 { if len(users) == 0 {
return false return false
} }
for _, x := range users { for _, x := range users {
if db.UserLevels(x["level"].(int32)) == db.AdminUser && x["email"].(string) != supportEmail { if db.UserLevels(x["level"].(int32)) == db.AdminUser {
return true return true
} }
} }
@ -257,7 +257,7 @@ func (a *Api) adminUserExists(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
adminExits := adminUserExists(users, a.enterpise.SupportEmail) adminExits := adminUserExists(users)
json.NewEncoder(w).Encode(adminExits) json.NewEncoder(w).Encode(adminExits)
return return
} }

View File

@ -10,7 +10,6 @@ import (
local "github.com/leandrofars/oktopus/internal/nats" local "github.com/leandrofars/oktopus/internal/nats"
"github.com/leandrofars/oktopus/internal/utils" "github.com/leandrofars/oktopus/internal/utils"
"github.com/nats-io/nats.go" "github.com/nats-io/nats.go"
"go.mongodb.org/mongo-driver/bson/primitive"
) )
var errInvalidMtp = errors.New("Invalid MTP, valid options are: mqtt, ws, stomp") var errInvalidMtp = errors.New("Invalid MTP, valid options are: mqtt, ws, stomp")
@ -105,8 +104,8 @@ func getDeviceCount(w http.ResponseWriter, nc *nats.Conn) (int64, error) {
return msg.Msg, err return msg.Msg, err
} }
func getDevices(w http.ResponseWriter, filter primitive.A, nc *nats.Conn) (*[]entity.Device, error) { func getDevices(w http.ResponseWriter, filter map[string]interface{}, nc *nats.Conn) (*entity.DevicesList, error) {
msg, err := bridge.NatsReq[[]entity.Device]( msg, err := bridge.NatsReq[entity.DevicesList](
local.NATS_ADAPTER_SUBJECT+"devices.retrieve", local.NATS_ADAPTER_SUBJECT+"devices.retrieve",
utils.Marshall(filter), utils.Marshall(filter),
w, w,

View File

@ -1,7 +1,6 @@
package api package api
import ( import (
"io"
"log" "log"
"net/http" "net/http"
"strings" "strings"
@ -213,11 +212,6 @@ func (a *Api) deviceWifi(w http.ResponseWriter, r *http.Request) {
if device.Cwmp == entity.Online { if device.Cwmp == entity.Online {
if a.enterpise.Enable {
a.getEnterpriseResource("wifi", "get", device, sn, w, []byte{}, "cwmp", "098")
return
}
var ( var (
NUMBER_OF_WIFI_PARAMS_TO_GET = 5 NUMBER_OF_WIFI_PARAMS_TO_GET = 5
) )
@ -347,17 +341,6 @@ func (a *Api) deviceWifi(w http.ResponseWriter, r *http.Request) {
if device.Cwmp == entity.Online { if device.Cwmp == entity.Online {
if a.enterpise.Enable {
payload, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write(utils.Marshall(err.Error()))
return
}
a.getEnterpriseResource("wifi", "set", device, sn, w, payload, "cwmp", "098")
return
}
var body []WiFi var body []WiFi
err := utils.MarshallDecoder(&body, r.Body) err := utils.MarshallDecoder(&body, r.Body)

View File

@ -153,7 +153,6 @@ func NatsReq[T entity.DataType](
err = json.Unmarshal(msg.Data, &answer) err = json.Unmarshal(msg.Data, &answer)
if err != nil { if err != nil {
var errMsg *entity.MsgAnswer[*string] var errMsg *entity.MsgAnswer[*string]
err = json.Unmarshal(msg.Data, &errMsg) err = json.Unmarshal(msg.Data, &errMsg)

View File

@ -30,17 +30,10 @@ type RestApi struct {
Ctx context.Context Ctx context.Context
} }
type Enterprise struct {
Enable bool
SupportPassword string
SupportEmail string
}
type Config struct { type Config struct {
RestApi RestApi RestApi RestApi
Nats Nats Nats Nats
Mongo Mongo Mongo Mongo
Enterprise Enterprise
} }
type Tls struct { type Tls struct {
@ -62,9 +55,6 @@ func NewConfig() *Config {
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection") serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
flApiPort := flag.String("api_port", lookupEnvOrString("REST_API_PORT", "8000"), "Rest api port") flApiPort := flag.String("api_port", lookupEnvOrString("REST_API_PORT", "8000"), "Rest api port")
mongoUri := flag.String("mongo_uri", lookupEnvOrString("MONGO_URI", "mongodb://localhost:27017"), "uri for mongodb server") mongoUri := flag.String("mongo_uri", lookupEnvOrString("MONGO_URI", "mongodb://localhost:27017"), "uri for mongodb server")
enterpise := flag.Bool("enterprise", lookupEnvOrBool("ENTERPRISE", false), "enterprise version enable")
enterprise_support_password := flag.String("enterprise_support_password", lookupEnvOrString("ENTERPRISE_SUPPORT_PASSWORD", ""), "enterprise support password")
enterpise_support_email := flag.String("enterprise_support_email", lookupEnvOrString("ENTERPRISE_SUPPORT_EMAIL", ""), "enterprise support email")
flHelp := flag.Bool("help", false, "Help") flHelp := flag.Bool("help", false, "Help")
/* /*
@ -103,11 +93,6 @@ func NewConfig() *Config {
Uri: *mongoUri, Uri: *mongoUri,
Ctx: ctx, Ctx: ctx,
}, },
Enterprise: Enterprise{
Enable: *enterpise,
SupportPassword: *enterprise_support_password,
SupportEmail: *enterpise_support_email,
},
} }
} }

View File

@ -29,3 +29,15 @@ type StatusCount struct {
Status int `bson:"_id" json:"status"` Status int `bson:"_id" json:"status"`
Count int `bson:"count" json:"count"` Count int `bson:"count" json:"count"`
} }
type DevicesList struct {
Devices []Device `json:"devices" bson:"documents"`
Total int64 `json:"total"`
}
type FilterOptions struct {
Models []string `json:"models"`
ProductClasses []string `json:"productClasses"`
Vendors []string `json:"vendors"`
Versions []string `json:"versions"`
}

View File

@ -3,7 +3,7 @@ package entity
import "time" import "time"
type DataType interface { type DataType interface {
[]map[string]interface{} | *string | Device | int64 | []Device | []VendorsCount | []ProductClassCount | []StatusCount | time.Duration | []byte []map[string]interface{} | *string | Device | int64 | []Device | []VendorsCount | []ProductClassCount | []StatusCount | time.Duration | []byte | []string | FilterOptions | DevicesList
} }
type MsgAnswer[T DataType] struct { type MsgAnswer[T DataType] struct {

View File

@ -4,6 +4,7 @@ import (
"log" "log"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
) )
@ -41,6 +42,18 @@ type Device struct {
Cwmp Status Cwmp Status
} }
type DevicesList struct {
Devices []Device `json:"devices" bson:"documents"`
Total int64 `json:"total" bson:"totalCount"`
}
type FilterOptions struct {
Models []string `json:"models"`
ProductClasses []string `json:"productClasses"`
Vendors []string `json:"vendors"`
Versions []string `json:"versions"`
}
func (d *Database) CreateDevice(device Device) error { func (d *Database) CreateDevice(device Device) error {
var result bson.M var result bson.M
var deviceExistent Device var deviceExistent Device
@ -100,32 +113,86 @@ func (d *Database) CreateDevice(device Device) error {
} }
return err return err
} }
func (d *Database) RetrieveDevices(filter bson.A) ([]Device, error) { func (d *Database) RetrieveDevices(filter bson.A) (*DevicesList, error) {
var results []Device var results []DevicesList
cursor, err := d.devices.Aggregate(d.ctx, filter) cursor, err := d.devices.Aggregate(d.ctx, filter)
if err != nil { if err != nil {
return results, err return nil, err
} }
if cursor.Err() != nil { if cursor.Err() != nil {
return results, cursor.Err() return nil, cursor.Err()
}
defer cursor.Close(d.ctx)
if err := cursor.All(d.ctx, &results); err != nil {
log.Println(err)
return nil, err
} }
for cursor.Next(d.ctx) { //log.Printf("results: %++v", results)
var device Device
err := cursor.Decode(&device) return &results[0], err
if err != nil { }
log.Println("Error to decode device info fields")
continue
}
results = append(results, device) func (d *Database) RetrieveDeviceFilterOptions() (FilterOptions, error) {
filter := bson.A{
bson.D{
{"$group",
bson.D{
{"_id", primitive.Null{}},
{"vendors", bson.D{{"$addToSet", "$vendor"}}},
{"versions", bson.D{{"$addToSet", "$version"}}},
{"productClasses", bson.D{{"$addToSet", "$productclass"}}},
{"models", bson.D{{"$addToSet", "$model"}}},
},
},
},
bson.D{
{"$project",
bson.D{
{"_id", 0},
{"vendors", 1},
{"versions", 1},
{"productClasses", 1},
{"models", 1},
},
},
},
} }
return results, err var results []FilterOptions
cursor, err := d.devices.Aggregate(d.ctx, filter)
if err != nil {
log.Println(err)
return FilterOptions{}, err
}
defer cursor.Close(d.ctx)
if err := cursor.All(d.ctx, &results); err != nil {
log.Println(err)
return FilterOptions{}, err
}
if len(results) > 0 {
return results[0], nil
} else {
return FilterOptions{
Models: []string{},
ProductClasses: []string{},
Vendors: []string{},
Versions: []string{},
}, nil
}
}
func (d *Database) DeleteDevices(filter bson.D) (int64, error) {
result, err := d.devices.DeleteMany(d.ctx, filter)
if err != nil {
log.Println(err)
}
return result.DeletedCount, err
} }
func (d *Database) RetrieveDevice(sn string) (Device, error) { func (d *Database) RetrieveDevice(sn string) (Device, error) {

View File

@ -52,18 +52,135 @@ func StartRequestsListener(ctx context.Context, nc *nats.Conn, db db.Database) {
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.retrieve", local.ADAPTER_QUEUE, func(msg *nats.Msg) { nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.retrieve", local.ADAPTER_QUEUE, func(msg *nats.Msg) {
var filter bson.A var criteria map[string]interface{}
err := json.Unmarshal(msg.Data, &filter) err := json.Unmarshal(msg.Data, &criteria)
if err != nil { if err != nil {
respondMsg(msg.Respond, 500, err.Error()) respondMsg(msg.Respond, 500, err.Error())
} }
//log.Println(criteria)
propertiesFilter := bson.D{{}}
vendorFilter := criteria["vendor"]
if vendorFilter != nil {
log.Println("Vendor filter", vendorFilter)
propertiesFilter = append(propertiesFilter, bson.E{Key: "vendor", Value: vendorFilter})
}
versionFilter := criteria["version"]
if versionFilter != nil {
log.Println("Version filter", versionFilter)
propertiesFilter = append(propertiesFilter, bson.E{Key: "version", Value: versionFilter})
}
typeFilter := criteria["productClass"]
if typeFilter != nil {
log.Println("Type filter", typeFilter)
propertiesFilter = append(propertiesFilter, bson.E{Key: "productclass", Value: typeFilter})
}
aliasFilter := criteria["alias"]
if aliasFilter != nil {
log.Println("Type filter", aliasFilter)
propertiesFilter = append(propertiesFilter, bson.E{Key: "alias", Value: aliasFilter})
}
modelFilter := criteria["model"]
if modelFilter != nil {
log.Println("Model filter", modelFilter)
propertiesFilter = append(propertiesFilter, bson.E{Key: "model", Value: modelFilter})
}
statusFilter := criteria["status"]
if statusFilter != nil {
log.Println("Status filter", statusFilter)
propertiesFilter = append(propertiesFilter, bson.E{Key: "status", Value: statusFilter})
}
filter := bson.A{
bson.D{
{"$match",
propertiesFilter,
},
},
bson.D{
{"$facet",
bson.D{
{"totalCount",
bson.A{
bson.D{{"$count", "count"}},
},
},
{"documents",
bson.A{
bson.D{{"$sort", bson.D{{"status", criteria["status_order"]}}}},
bson.D{{"$skip", criteria["skip"]}},
bson.D{{"$limit", criteria["limit"]}},
},
},
},
},
},
bson.D{
{"$project",
bson.D{
{"totalCount",
bson.D{
{"$arrayElemAt",
bson.A{
"$totalCount.count",
0,
},
},
},
},
{"documents", 1},
},
},
},
}
devicesList, err := db.RetrieveDevices(filter) devicesList, err := db.RetrieveDevices(filter)
if err != nil { if err != nil {
respondMsg(msg.Respond, 500, err.Error()) respondMsg(msg.Respond, 500, err.Error())
} }
respondMsg(msg.Respond, 200, devicesList) respondMsg(msg.Respond, 200, &devicesList)
})
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.delete", local.ADAPTER_QUEUE, func(msg *nats.Msg) {
var serialNumbersList []string
err := json.Unmarshal(msg.Data, &serialNumbersList)
if err != nil {
respondMsg(msg.Respond, 500, err.Error())
}
var criteria bson.A
for _, sn := range serialNumbersList {
criteria = append(criteria, bson.D{{"sn", sn}})
}
// Create the filter with the $or operator
filter := bson.D{
{"$or", criteria},
}
deletedCount, err := db.DeleteDevices(filter)
if err != nil {
respondMsg(msg.Respond, 500, err.Error())
}
respondMsg(msg.Respond, 200, deletedCount)
})
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.filterOptions", local.ADAPTER_QUEUE, func(msg *nats.Msg) {
result, err := db.RetrieveDeviceFilterOptions()
if err != nil {
respondMsg(msg.Respond, 500, err.Error())
}
respondMsg(msg.Respond, 200, result)
}) })
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.class", local.ADAPTER_QUEUE, func(msg *nats.Msg) { nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.class", local.ADAPTER_QUEUE, func(msg *nats.Msg) {

File diff suppressed because it is too large Load Diff