feat(controller): custom cwmp/usp messages templates
This commit is contained in:
parent
a895e3ce31
commit
c4ac20c1cc
|
|
@ -53,6 +53,11 @@ func (a *Api) StartApi() {
|
||||||
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")
|
||||||
|
iot.HandleFunc("/message/{type}", a.addTemplate).Methods("POST")
|
||||||
|
iot.HandleFunc("/message", a.updateTemplate).Methods("PUT")
|
||||||
|
iot.HandleFunc("/message", a.getTemplate).Methods("GET")
|
||||||
|
iot.HandleFunc("/message", a.deleteTemplate).Methods("DELETE")
|
||||||
|
iot.HandleFunc("/cwmp/{sn}/generic", a.cwmpGenericMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/cwmp/{sn}/getParameterNames", a.cwmpGetParameterNamesMsg).Methods("PUT")
|
iot.HandleFunc("/cwmp/{sn}/getParameterNames", a.cwmpGetParameterNamesMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/cwmp/{sn}/getParameterValues", a.cwmpGetParameterValuesMsg).Methods("PUT")
|
iot.HandleFunc("/cwmp/{sn}/getParameterValues", a.cwmpGetParameterValuesMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/cwmp/{sn}/getParameterAttributes", a.cwmpGetParameterAttributesMsg).Methods("PUT")
|
iot.HandleFunc("/cwmp/{sn}/getParameterAttributes", a.cwmpGetParameterAttributesMsg).Methods("PUT")
|
||||||
|
|
@ -61,6 +66,7 @@ func (a *Api) StartApi() {
|
||||||
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("/filterOptions", a.filterOptions).Methods("GET")
|
iot.HandleFunc("/filterOptions", a.filterOptions).Methods("GET")
|
||||||
|
iot.HandleFunc("/{sn}/{mtp}/generic", a.deviceGenericMessage).Methods("PUT")
|
||||||
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")
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,31 @@ import (
|
||||||
|
|
||||||
var errDeviceModelNotFound = errors.New("device model not found")
|
var errDeviceModelNotFound = errors.New("device model not found")
|
||||||
|
|
||||||
|
func (a *Api) cwmpGenericMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
sn := getSerialNumberFromRequest(r)
|
||||||
|
|
||||||
|
payload, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write(utils.Marshall(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(payload) == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write(utils.Marshall("Empty payload"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, _, err := cwmpInteraction[cwmp.SoapEnvelope](sn, payload, w, a.nc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Api) cwmpGetParameterNamesMsg(w http.ResponseWriter, r *http.Request) {
|
func (a *Api) cwmpGetParameterNamesMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
sn := getSerialNumberFromRequest(r)
|
sn := getSerialNumberFromRequest(r)
|
||||||
|
|
||||||
|
|
@ -125,7 +150,7 @@ func (a *Api) cwmpDeleteObjectMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write(data)
|
w.Write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cwmpInteraction[T cwmp.SetParameterValuesResponse | cwmp.DeleteObjectResponse | cwmp.GetParameterAttributesResponse | cwmp.GetParameterNamesResponse | cwmp.GetParameterValuesResponse | cwmp.AddObjectResponse](
|
func cwmpInteraction[T cwmp.SetParameterValuesResponse | cwmp.SoapEnvelope | cwmp.DeleteObjectResponse | cwmp.GetParameterAttributesResponse | cwmp.GetParameterNamesResponse | cwmp.GetParameterValuesResponse | cwmp.AddObjectResponse](
|
||||||
sn string, payload []byte, w http.ResponseWriter, nc *nats.Conn,
|
sn string, payload []byte, w http.ResponseWriter, nc *nats.Conn,
|
||||||
) ([]byte, T, error) {
|
) ([]byte, T, error) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
"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"
|
"github.com/leandrofars/oktopus/internal/entity"
|
||||||
|
|
@ -367,3 +368,141 @@ func (a *Api) filterOptions(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(resp.Code)
|
w.WriteHeader(resp.Code)
|
||||||
w.Write(utils.Marshall(resp.Msg))
|
w.Write(utils.Marshall(resp.Msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Api) updateTemplate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
name := r.URL.Query().Get("name")
|
||||||
|
if name == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
utils.MarshallEncoder("No name provided", w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
utils.MarshallEncoder("Error to decode payload: "+err.Error(), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadLen := len(payload)
|
||||||
|
if payloadLen == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
utils.MarshallEncoder("No payload provided", w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.db.UpdateTemplate(name, string(payload))
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
json.NewEncoder(w).Encode(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) addTemplate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
name := r.URL.Query().Get("name")
|
||||||
|
if name == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
utils.MarshallEncoder("No name provided", w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
utils.MarshallEncoder("Error to decode payload: "+err.Error(), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadLen := len(payload)
|
||||||
|
if payloadLen == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
utils.MarshallEncoder("No payload provided", w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
switch vars["type"] {
|
||||||
|
case "cwmp":
|
||||||
|
err = a.db.AddTemplate(name, "cwmp", string(payload))
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
json.NewEncoder(w).Encode(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return
|
||||||
|
case "usp":
|
||||||
|
err = a.db.AddTemplate(name, "usp", string(payload))
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
json.NewEncoder(w).Encode(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
utils.MarshallEncoder("Invalid template type", w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) getTemplate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
name := r.URL.Query().Get("name")
|
||||||
|
msgType := r.URL.Query().Get("type")
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
|
||||||
|
var filter bson.D
|
||||||
|
if msgType == "" {
|
||||||
|
filter = bson.D{}
|
||||||
|
} else {
|
||||||
|
filter = bson.D{{"type", msgType}}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := a.db.AllTemplates(filter)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
json.NewEncoder(w).Encode("Error to get all templates: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
json.NewEncoder(w).Encode(result)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
t, err := a.db.FindTemplate(bson.D{{"name", name}})
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
json.NewEncoder(w).Encode("error to find message: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(t.Value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) deleteTemplate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
name := r.URL.Query().Get("name")
|
||||||
|
if name == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
json.NewEncoder(w).Encode("needs template name!")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
err := a.db.DeleteTemplate(name)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
json.NewEncoder(w).Encode("error to delete template: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
|
@ -12,6 +13,7 @@ import (
|
||||||
"github.com/leandrofars/oktopus/internal/usp/usp_utils"
|
"github.com/leandrofars/oktopus/internal/usp/usp_utils"
|
||||||
"github.com/leandrofars/oktopus/internal/utils"
|
"github.com/leandrofars/oktopus/internal/utils"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -86,6 +88,44 @@ func sendUspMsg(msg usp_msg.Msg, sn string, w http.ResponseWriter, nc *nats.Conn
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Api) deviceGenericMessage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
sn := getSerialNumberFromRequest(r)
|
||||||
|
mtp, err := getMtpFromRequest(r, w)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mtp == "" {
|
||||||
|
var ok bool
|
||||||
|
mtp, ok = deviceStateOK(w, a.nc, sn)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write(utils.Marshall(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg usp_msg.Msg
|
||||||
|
|
||||||
|
err = protojson.Unmarshal(payload, &msg)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write(utils.Marshall(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sendUspMsg(msg, sn, w, a.nc, mtp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Api) deviceGetMsg(w http.ResponseWriter, r *http.Request) {
|
func (a *Api) deviceGetMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
sn := getSerialNumberFromRequest(r)
|
sn := getSerialNumberFromRequest(r)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
type Database struct {
|
type Database struct {
|
||||||
client *mongo.Client
|
client *mongo.Client
|
||||||
users *mongo.Collection
|
users *mongo.Collection
|
||||||
|
template *mongo.Collection
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,6 +44,16 @@ func NewDatabase(ctx context.Context, mongoUri string) Database {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db.template = client.Database("general").Collection("templates")
|
||||||
|
indexField = bson.M{"name": 1}
|
||||||
|
_, err = db.template.Indexes().CreateOne(ctx, mongo.IndexModel{
|
||||||
|
Keys: indexField,
|
||||||
|
Options: options.Index().SetUnique(true),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
db.ctx = ctx
|
db.ctx = ctx
|
||||||
|
|
||||||
return db
|
return db
|
||||||
|
|
|
||||||
72
backend/services/controller/internal/db/template.go
Normal file
72
backend/services/controller/internal/db/template.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Template struct {
|
||||||
|
Name string `json:"name" bson:"name"`
|
||||||
|
Type string `json:"type" bson:"type"`
|
||||||
|
Value string `json:"value" bson:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrorTemplateExists = errors.New("message already exists")
|
||||||
|
var ErrorTemplateNotExists = errors.New("message don't exist")
|
||||||
|
|
||||||
|
func (d *Database) FindTemplate(filter interface{}) (Template, error) {
|
||||||
|
var result Template
|
||||||
|
err := d.template.FindOne(d.ctx, filter).Decode(&result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) AllTemplates(filter interface{}) ([]Template, error) {
|
||||||
|
var results []Template
|
||||||
|
|
||||||
|
cursor, err := d.template.Find(d.ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
if err = cursor.All(d.ctx, &results); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) AddTemplate(name, tr string, t string) error {
|
||||||
|
opts := options.FindOneAndReplace().SetUpsert(true)
|
||||||
|
err := d.template.FindOneAndReplace(d.ctx, bson.D{{"name", name}}, Template{Name: name, Type: tr, Value: t}, opts).Err()
|
||||||
|
if err != nil {
|
||||||
|
if err == mongo.ErrNoDocuments {
|
||||||
|
log.Printf("New message %s added to database", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("Message %s already existed, and got replaced for new payload", name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) UpdateTemplate(name, t string) error {
|
||||||
|
result, err := d.template.UpdateOne(d.ctx, bson.D{{"name", name}}, bson.D{{"$set", bson.D{{"value", t}}}})
|
||||||
|
if err == nil {
|
||||||
|
if result.MatchedCount == 0 {
|
||||||
|
return ErrorTemplateNotExists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) DeleteTemplate(name string) error {
|
||||||
|
result, err := d.template.DeleteOne(d.ctx, bson.D{{"name", name}})
|
||||||
|
if err == nil {
|
||||||
|
if result.DeletedCount == 0 {
|
||||||
|
return ErrorTemplateNotExists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user