diff --git a/go-apps/meep-iot/server/meep-iot.go b/go-apps/meep-iot/server/meep-iot.go index ab97eba47c09affcbdc0a315b9e34500d34272bc..4c5e9f63a46cea64772ab45193c02c81959dbd1d 100644 --- a/go-apps/meep-iot/server/meep-iot.go +++ b/go-apps/meep-iot/server/meep-iot.go @@ -607,7 +607,7 @@ func registerediotplatformsByIdGET(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) log.Debug("registerediotplatformsByIdGET: vars: ", vars) - iotPlatformIdParamStr := vars["registeredIotPlatformId}"] + iotPlatformIdParamStr := vars["registeredIotPlatformId"] log.Debug("systeminfoByIdGET: registerediotplatformsByIdGET: ", iotPlatformIdParamStr) // Validate query parameters @@ -755,23 +755,23 @@ func registerediotplatformsPOST(w http.ResponseWriter, r *http.Request) { return } if v.Version == "" { - log.Error("Mandatory Version parameter shall be absent") - errHandlerProblemDetails(w, "Mandatory attribute Version shall be absent in the request body.", http.StatusBadRequest) + log.Error("Mandatory Version parameter shall be present") + errHandlerProblemDetails(w, "Mandatory attribute Version shall be present in the request body.", http.StatusBadRequest) return } if v.Endpoint == nil { - log.Error("Mandatory Endpoint parameter shall be absent") - errHandlerProblemDetails(w, "Mandatory attribute Endpoint shall be absent in the request body.", http.StatusBadRequest) + log.Error("Mandatory Endpoint parameter shall be present") + errHandlerProblemDetails(w, "Mandatory attribute Endpoint shall be present in the request body.", http.StatusBadRequest) return } if v.Security == nil { - log.Error("Mandatory Security parameter shall be absent") - errHandlerProblemDetails(w, "Mandatory attribute Security shall be absent in the request body.", http.StatusBadRequest) + log.Error("Mandatory Security parameter shall be present") + errHandlerProblemDetails(w, "Mandatory attribute Security shall be present in the request body.", http.StatusBadRequest) return } if v.ImplSpecificInfo == nil { - log.Error("Mandatory ImplSpecificInfo parameter shall be absent") - errHandlerProblemDetails(w, "Mandatory attribute ImplSpecificInfo shall be absent in the request body.", http.StatusBadRequest) + log.Error("Mandatory ImplSpecificInfo parameter shall be present") + errHandlerProblemDetails(w, "Mandatory attribute ImplSpecificInfo shall be present in the request body.", http.StatusBadRequest) return } } diff --git a/go-apps/meep-iot/server/meep-iot_test.go b/go-apps/meep-iot/server/meep-iot_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2f40ab3d45628c75ee687bbe28972421d830730e --- /dev/null +++ b/go-apps/meep-iot/server/meep-iot_test.go @@ -0,0 +1,1419 @@ +/* + * Copyright (c) 2024 The AdvantEDGE Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on ance "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package server + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + //"strconv" + "strings" + "testing" + "time" + + //meepiotmgr "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-iot-mgr" + + log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" + // met "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-metrics" + mod "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-model" + mq "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-mq" + + "github.com/gorilla/mux" +) + +const testScenario string = ` +{ + "version":"1.5.3", + "name":"test-scenario", + "deployment":{ + "netChar":{ + "latency":50, + "latencyVariation":5, + "throughputDl":1000, + "throughputUl":1000 + }, + "domains":[ + { + "id":"PUBLIC", + "name":"PUBLIC", + "type":"PUBLIC", + "netChar":{ + "latency":6, + "latencyVariation":2, + "throughputDl":1000000, + "throughputUl":1000000 + }, + "zones":[ + { + "id":"PUBLIC-COMMON", + "name":"PUBLIC-COMMON", + "type":"COMMON", + "netChar":{ + "latency":5, + "latencyVariation":1, + "throughput":1000000 + }, + "networkLocations":[ + { + "id":"PUBLIC-COMMON-DEFAULT", + "name":"PUBLIC-COMMON-DEFAULT", + "type":"DEFAULT", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":50000, + "throughputUl":50000, + "packetLoss":1 + } + } + ] + } + ] + }, + { + "id":"4da82f2d-1f44-4945-8fe7-00c0431ef8c7", + "name":"operator-cell1", + "type":"OPERATOR-CELLULAR", + "netChar":{ + "latency":6, + "latencyVariation":2, + "throughputDl":1000, + "throughputUl":1000 + }, + "cellularDomainConfig":{ + "mnc":"456", + "mcc":"123", + "defaultCellId":"1234567" + }, + "zones":[ + { + "id":"operator-cell1-COMMON", + "name":"operator-cell1-COMMON", + "type":"COMMON", + "netChar":{ + "latency":5, + "latencyVariation":1, + "throughput":1000 + }, + "networkLocations":[ + { + "id":"operator-cell1-COMMON-DEFAULT", + "name":"operator-cell1-COMMON-DEFAULT", + "type":"DEFAULT", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + } + } + ] + }, + { + "id":"0836975f-a7ea-41ec-b0e0-aff43178194d", + "name":"zone1", + "type":"ZONE", + "netChar":{ + "latency":5, + "latencyVariation":1, + "throughput":1000 + }, + "networkLocations":[ + { + "id":"zone1-DEFAULT", + "name":"zone1-DEFAULT", + "type":"DEFAULT", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + }, + "physicalLocations":[ + { + "id":"97b80da7-a74a-4649-bb61-f7fa4fbb2d76", + "name":"zone1-edge1", + "type":"EDGE", + "connected":true, + "processes":[ + { + "id":"fcf1269c-a061-448e-aa80-6dd9c2d4c548", + "name":"zone1-edge1-iperf", + "type":"EDGE-APP", + "image":"meep-docker-registry:30001/iperf-server", + "commandArguments":"-c, export; iperf -s -p $IPERF_SERVICE_PORT", + "commandExe":"/bin/bash", + "serviceConfig":{ + "name":"zone1-edge1-iperf", + "meSvcName":"iperf", + "ports":[ + { + "protocol":"UDP", + "port":80 + } + ] + }, + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + }, + { + "id":"35697e68-c627-4b8d-9cd7-ad8b8e226aee", + "name":"zone1-edge1-svc", + "type":"EDGE-APP", + "image":"meep-docker-registry:30001/demo-server", + "environment":"MGM_GROUP_NAME=svc, MGM_APP_ID=zone1-edge1-svc, MGM_APP_PORT=80", + "serviceConfig":{ + "name":"zone1-edge1-svc", + "meSvcName":"svc", + "ports":[ + { + "protocol":"TCP", + "port":80 + } + ] + }, + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + } + ], + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + } + ] + }, + { + "id":"7a6f8077-b0b3-403d-b954-3351e21afeb7", + "name":"zone1-poa-cell1", + "type":"POA-4G", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + }, + "poa4GConfig":{ + "cellId":"2345678" + }, + "geoData": { + "location": { + "type": "Point", + "coordinates": [ + 7.423547, + 43.731724 + ] + }, + "radius": 400, + "path": null, + "eopMode": null, + "velocity": null + }, + "physicalLocations":[ + { + "id":"32a2ced4-a262-49a8-8503-8489a94386a2", + "name":"ue1", + "type":"UE", + "connected":true, + "wireless":true, + "processes":[ + { + "id":"9bdd6acd-f6e4-44f6-a26c-8fd9abd338a7", + "name":"ue1-iperf", + "type":"UE-APP", + "image":"meep-docker-registry:30001/iperf-client", + "commandArguments":"-c, export; iperf -u -c $IPERF_SERVICE_HOST -p $IPERF_SERVICE_PORT\n-t 3600 -b 50M;", + "commandExe":"/bin/bash", + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + } + ], + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + }, + { + "id":"b1851da5-c9e1-4bd8-ad23-5925c82ee127", + "name":"zone1-fog1", + "type":"FOG", + "connected":true, + "processes":[ + { + "id":"c2f2fb5d-4053-4cee-a0ee-e62bbb7751b6", + "name":"zone1-fog1-iperf", + "type":"EDGE-APP", + "image":"meep-docker-registry:30001/iperf-server", + "commandArguments":"-c, export; iperf -s -p $IPERF_SERVICE_PORT;", + "commandExe":"/bin/bash", + "serviceConfig":{ + "name":"zone1-fog1-iperf", + "meSvcName":"iperf", + "ports":[ + { + "protocol":"UDP", + "port":80 + } + ] + }, + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + }, + { + "id":"53b5806b-e213-4c5a-a181-f1c31c24287b", + "name":"zone1-fog1-svc", + "type":"EDGE-APP", + "image":"meep-docker-registry:30001/demo-server", + "environment":"MGM_GROUP_NAME=svc, MGM_APP_ID=zone1-fog1-svc, MGM_APP_PORT=80", + "serviceConfig":{ + "name":"zone1-fog1-svc", + "meSvcName":"svc", + "ports":[ + { + "protocol":"TCP", + "port":80 + } + ] + }, + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + } + ], + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + } + ] + }, + { + "id":"7ff90180-2c1a-4c11-b59a-3608c5d8d874", + "name":"zone1-poa-cell2", + "type":"POA-4G", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + }, + "poa4GConfig":{ + "cellId":"3456789" + }, + "geoData": { + "location": { + "type": "Point", + "coordinates": [ + 7.423547, + 43.731724 + ] + }, + "radius": 400, + "path": null, + "eopMode": null, + "velocity": null + } + } + ] + }, + { + "id":"d1f06b00-4454-4d35-94a5-b573888e7ea9", + "name":"zone2", + "type":"ZONE", + "netChar":{ + "latency":5, + "latencyVariation":1, + "throughput":1000 + }, + "networkLocations":[ + { + "id":"zone2-DEFAULT", + "name":"zone2-DEFAULT", + "type":"DEFAULT", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + }, + "physicalLocations":[ + { + "id":"fb130d18-fd81-43e0-900c-c584e7190302", + "name":"zone2-edge1", + "type":"EDGE", + "connected":true, + "processes":[ + { + "id":"5c8276ba-0b78-429d-a0bf-d96f35ba2c77", + "name":"zone2-edge1-iperf", + "type":"EDGE-APP", + "image":"meep-docker-registry:30001/iperf-server", + "commandArguments":"-c, export; iperf -s -p $IPERF_SERVICE_PORT;", + "commandExe":"/bin/bash", + "serviceConfig":{ + "name":"zone2-edge1-iperf", + "meSvcName":"iperf", + "ports":[ + { + "protocol":"UDP", + "port":80 + } + ] + }, + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + }, + { + "id":"53fa28f0-80e2-414c-8841-86db9bd37d51", + "name":"zone2-edge1-svc", + "type":"EDGE-APP", + "image":"meep-docker-registry:30001/demo-server", + "environment":"MGM_GROUP_NAME=svc, MGM_APP_ID=zone2-edge1-svc, MGM_APP_PORT=80", + "serviceConfig":{ + "name":"zone2-edge1-svc", + "meSvcName":"svc", + "ports":[ + { + "protocol":"TCP", + "port":80 + } + ] + }, + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + } + ], + "netChar":{ + "throughputDl":1000, + "throughputUl":1000 + } + } + ] + }, + { + "id":"c44b8937-58af-44b2-acdb-e4d1c4a1510b", + "name":"zone2-poa1", + "type":"POA", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":20, + "throughputUl":20 + } + } + ] + } + ] + }, + { + "id":"e29138fb-cf03-4372-8335-fd2665b77a11", + "name":"operator1", + "type":"OPERATOR", + "netChar":{ + "latency":6, + "latencyVariation":2, + "throughputDl":1000, + "throughputUl":1000 + }, + "zones":[ + { + "id":"operator1-COMMON", + "name":"operator1-COMMON", + "type":"COMMON", + "netChar":{ + "latency":5, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + }, + "networkLocations":[ + { + "id":"operator1-COMMON-DEFAULT", + "name":"operator1-COMMON-DEFAULT", + "type":"DEFAULT", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + } + } + ] + }, + { + "id":"7d8bee73-6d5c-4c5a-a3a0-49ebe3cd2c71", + "name":"zone3", + "type":"ZONE", + "netChar":{ + "latency":5, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + }, + "networkLocations":[ + { + "id":"zone3-DEFAULT", + "name":"zone3-DEFAULT", + "type":"DEFAULT", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + } + }, + { + "id":"ecc2a41b-7381-4108-a037-52862c520733", + "name":"poa1", + "type":"POA", + "netChar":{ + "latency":1, + "latencyVariation":1, + "throughputDl":1000, + "throughputUl":1000 + } + } + ] + } + ] + } + ] + } +} +` + +const redisTestAddr = "localhost:30380" +const influxTestAddr = "http://localhost:30986" +const testScenarioName = "testScenario" + +var m *mod.Model +var mqLocal *mq.MsgQueue + +func TestRegisterediotplatformsPOST(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + initializeVars() + err := Init() + if err != nil { + t.Fatalf("Error initializing test basic procedure") + } + err = Run() + if err != nil { + t.Fatalf("Error running test basic procedure") + } + fmt.Println("Set a scenario") + initialiseScenario(testScenario) + time.Sleep(1000 * time.Millisecond) + updateScenario("mobility1") + + /****************************** + * expected response section + ******************************/ + var expected_adresses = []Addresses{} + expected_adresses = append(expected_adresses, Addresses{ + Host: "172.29.10.56", + Port: 1883, + }) + var expected_endpoint = EndPointInfo{ + Addresses: expected_adresses, + } + var expected_userTransportInfo = []MbTransportInfo{} + var mb_transportInfo TransportType + mb_transportInfo = MB_TOPIC_BASED + var security = SecurityInfo{} + var implSpecificInfo = ImplSpecificInfo{} + expected_userTransportInfo = append(expected_userTransportInfo, MbTransportInfo{ + Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", + Name: "MQTT", + Type_: &mb_transportInfo, + Description: "MQTT", + Protocol: "MQTT", + Version: "2", + Endpoint: &expected_endpoint, + Security: &security, + ImplSpecificInfo: &implSpecificInfo, + }) + var expected_adresses_1 = []Addresses{} + expected_adresses_1 = append(expected_adresses_1, Addresses{ + Host: "172.29.10.20", + Port: 31110, + }) + var expected_customServicesTransportInfo = []TransportInfo{} + var expected_endPointInfo_1 = EndPointInfo{ + Addresses: expected_adresses_1, + } + var transportInfo TransportType + transportInfo = REST_HTTP + expected_customServicesTransportInfo = append(expected_customServicesTransportInfo, TransportInfo{ + Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", + Name: "/laboai-acme-ic-cse", + Type_: &transportInfo, + Description: "ACME oneM2M CSE", + Protocol: "REST_HTTP", + Version: "4", + Endpoint: &expected_endPointInfo_1, + Security: &security, + }) + var expected_iotPlatformInfo = IotPlatformInfo{ + IotPlatformId: "523f2df1-8927-429f-906c-56ba92d13762", + UserTransportInfo: expected_userTransportInfo, + CustomServicesTransportInfo: expected_customServicesTransportInfo, + Enabled: true, + } + fmt.Println("Create an IotPlatformInfo: ", expected_iotPlatformInfo) + expected_iotPlatformInfo_str, err := json.Marshal(expected_iotPlatformInfo) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("expected_iotPlatformInfo: ", string(expected_iotPlatformInfo_str)) + + /****************************** + * request body section + ******************************/ + var adresses = []Addresses{} + adresses = append(adresses, Addresses{ + Host: "172.29.10.56", + Port: 1883, + }) + var endpoint = EndPointInfo{ + Addresses: adresses, + } + var userTransportInfo = []MbTransportInfo{} + userTransportInfo = append(userTransportInfo, MbTransportInfo{ + Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", + Name: "MQTT", + Description: "MQTT", + Type_: &mb_transportInfo, + Protocol: "MQTT", + Version: "2", + Endpoint: &endpoint, + Security: &security, + ImplSpecificInfo: &implSpecificInfo, + }) + var adresses_1 = []Addresses{} + adresses_1 = append(adresses_1, Addresses{ + Host: "172.29.10.20", + Port: 31110, + }) + var customServicesTransportInfo = []TransportInfo{} + var endPointInfo_1 = EndPointInfo{ + Addresses: adresses_1, + } + customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ + Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", + Name: "/laboai-acme-ic-cse", + Description: "ACME oneM2M CSE", + Type_: &transportInfo, + Protocol: "REST_HTTP", + Version: "4", + Endpoint: &endPointInfo_1, + Security: &security, + }) + var iotPlatformInfo = IotPlatformInfo{ + IotPlatformId: "523f2df1-8927-429f-906c-56ba92d13762", + UserTransportInfo: userTransportInfo, + CustomServicesTransportInfo: customServicesTransportInfo, + Enabled: true, + } + fmt.Println("Create an IotPlatformInfo: ", iotPlatformInfo) + body, err := json.Marshal(iotPlatformInfo) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("body: ", string(body)) + /****************************** + * request execution section + ******************************/ + rr, err := sendRequest(http.MethodPost, "/registered_devices", bytes.NewBuffer(body), nil, nil, nil, http.StatusCreated, RegisterediotplatformsPOST) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("Respone: rr: ", rr) + var resp IotPlatformInfo + err = json.Unmarshal([]byte(rr), &resp) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("Respone: resp: ", resp) + if !validateIotPlatformInfo(resp, iotPlatformInfo) { + t.Errorf("handler returned unexpected body: got %v want %v", rr, expected_iotPlatformInfo_str) + } + + fmt.Println("Received expected response") + /****************************** + * back to initial state section + ******************************/ + _ = deleteRegisterediotplatforms(resp.IotPlatformId) + terminateScenario() +} + +func TestRegisterediotplatformsPOSTFail_1(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + initializeVars() + err := Init() + if err != nil { + t.Fatalf("Error initializing test basic procedure") + } + err = Run() + if err != nil { + t.Fatalf("Error running test basic procedure") + } + fmt.Println("Set a scenario") + initialiseScenario(testScenario) + time.Sleep(1000 * time.Millisecond) + updateScenario("mobility1") + + /****************************** + * expected response section + ******************************/ + + /****************************** + * request body section + ******************************/ + var mb_transportInfo TransportType + mb_transportInfo = MB_TOPIC_BASED + var transportInfo TransportType + transportInfo = REST_HTTP + //var security = SecurityInfo{} // Missing Security + var implSpecificInfo = ImplSpecificInfo{} + var adresses = []Addresses{} + adresses = append(adresses, Addresses{ + Host: "172.29.10.56", + Port: 1883, + }) + var endpoint = EndPointInfo{ + Addresses: adresses, + } + var userTransportInfo = []MbTransportInfo{} + userTransportInfo = append(userTransportInfo, MbTransportInfo{ // Missing Security + Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", + Name: "MQTT", + Description: "MQTT", + Type_: &mb_transportInfo, + Protocol: "MQTT", + Version: "2", + Endpoint: &endpoint, + ImplSpecificInfo: &implSpecificInfo, + }) + var adresses_1 = []Addresses{} + adresses_1 = append(adresses_1, Addresses{ + Host: "172.29.10.20", + Port: 31110, + }) + var customServicesTransportInfo = []TransportInfo{} + var endPointInfo_1 = EndPointInfo{ + Addresses: adresses_1, + } + customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ + Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", + Name: "/laboai-acme-ic-cse", + Description: "ACME oneM2M CSE", + Type_: &transportInfo, + Protocol: "REST_HTTP", + Version: "4", + Endpoint: &endPointInfo_1, + }) + var iotPlatformInfo = IotPlatformInfo{ + IotPlatformId: "523f2df1-8927-429f-906c-56ba92d13762", + UserTransportInfo: userTransportInfo, + CustomServicesTransportInfo: customServicesTransportInfo, + Enabled: true, + } + fmt.Println("Create an IotPlatformInfo: ", iotPlatformInfo) + body, err := json.Marshal(iotPlatformInfo) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("body: ", string(body)) + /****************************** + * request execution section + ******************************/ + _, err = sendRequest(http.MethodPost, "/registered_devices", bytes.NewBuffer(body), nil, nil, nil, http.StatusBadRequest, RegisterediotplatformsPOST) + if err != nil { + t.Fatalf("Shall received 400 Bad Request due to Security missing field") + } + fmt.Println("Received expected response") + /****************************** + * back to initial state section + ******************************/ + terminateScenario() +} + +func TestRegisterediotplatformsPOSTFail_2(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + initializeVars() + err := Init() + if err != nil { + t.Fatalf("Error initializing test basic procedure") + } + err = Run() + if err != nil { + t.Fatalf("Error running test basic procedure") + } + fmt.Println("Set a scenario") + initialiseScenario(testScenario) + time.Sleep(1000 * time.Millisecond) + updateScenario("mobility1") + + /****************************** + * expected response section + ******************************/ + + /****************************** + * request body section + ******************************/ + var mb_transportInfo TransportType + mb_transportInfo = MB_TOPIC_BASED + var transportInfo TransportType + transportInfo = REST_HTTP + var security = SecurityInfo{} + //var implSpecificInfo = ImplSpecificInfo{} // Missing ImplSpecificInfo + var adresses = []Addresses{} + adresses = append(adresses, Addresses{ + Host: "172.29.10.56", + Port: 1883, + }) + var endpoint = EndPointInfo{ + Addresses: adresses, + } + var userTransportInfo = []MbTransportInfo{} + userTransportInfo = append(userTransportInfo, MbTransportInfo{ // Missing ImplSpecificInfo + Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", + Name: "MQTT", + Description: "MQTT", + Type_: &mb_transportInfo, + Protocol: "MQTT", + Version: "2", + Endpoint: &endpoint, + Security: &security, + }) + var adresses_1 = []Addresses{} + adresses_1 = append(adresses_1, Addresses{ + Host: "172.29.10.20", + Port: 31110, + }) + var customServicesTransportInfo = []TransportInfo{} + var endPointInfo_1 = EndPointInfo{ + Addresses: adresses_1, + } + customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ + Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", + Name: "/laboai-acme-ic-cse", + Description: "ACME oneM2M CSE", + Type_: &transportInfo, + Protocol: "REST_HTTP", + Version: "4", + Endpoint: &endPointInfo_1, + Security: &security, + }) + var iotPlatformInfo = IotPlatformInfo{ + IotPlatformId: "523f2df1-8927-429f-906c-56ba92d13762", + UserTransportInfo: userTransportInfo, + CustomServicesTransportInfo: customServicesTransportInfo, + Enabled: true, + } + fmt.Println("Create an IotPlatformInfo: ", iotPlatformInfo) + body, err := json.Marshal(iotPlatformInfo) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("body: ", string(body)) + /****************************** + * request execution section + ******************************/ + _, err = sendRequest(http.MethodPost, "/registered_devices", bytes.NewBuffer(body), nil, nil, nil, http.StatusBadRequest, RegisterediotplatformsPOST) + if err != nil { + t.Fatalf("Shall received 400 Bad Request due to ImplSpecificInfo missing field") + } + fmt.Println("Received expected response") + /****************************** + * back to initial state section + ******************************/ + terminateScenario() +} + +func TestRegisterediotplatformsPOSTFail_3(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + initializeVars() + err := Init() + if err != nil { + t.Fatalf("Error initializing test basic procedure") + } + err = Run() + if err != nil { + t.Fatalf("Error running test basic procedure") + } + fmt.Println("Set a scenario") + initialiseScenario(testScenario) + time.Sleep(1000 * time.Millisecond) + updateScenario("mobility1") + + /****************************** + * expected response section + ******************************/ + + /****************************** + * request body section + ******************************/ + //var mb_transportInfo TransportType // Missing TransportType + //mb_transportInfo = MB_TOPIC_BASED + var transportInfo TransportType + transportInfo = REST_HTTP + var security = SecurityInfo{} + var implSpecificInfo = ImplSpecificInfo{} + var adresses = []Addresses{} + adresses = append(adresses, Addresses{ + Host: "172.29.10.56", + Port: 1883, + }) + var endpoint = EndPointInfo{ + Addresses: adresses, + } + var userTransportInfo = []MbTransportInfo{} + userTransportInfo = append(userTransportInfo, MbTransportInfo{ // Missing TransportType + Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", + Name: "MQTT", + Description: "MQTT", + Protocol: "MQTT", + Version: "2", + Endpoint: &endpoint, + Security: &security, + ImplSpecificInfo: &implSpecificInfo, + }) + var adresses_1 = []Addresses{} + adresses_1 = append(adresses_1, Addresses{ + Host: "172.29.10.20", + Port: 31110, + }) + var customServicesTransportInfo = []TransportInfo{} + var endPointInfo_1 = EndPointInfo{ + Addresses: adresses_1, + } + customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ + Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", + Name: "/laboai-acme-ic-cse", + Description: "ACME oneM2M CSE", + Type_: &transportInfo, + Protocol: "REST_HTTP", + Version: "4", + Endpoint: &endPointInfo_1, + Security: &security, + }) + var iotPlatformInfo = IotPlatformInfo{ + IotPlatformId: "523f2df1-8927-429f-906c-56ba92d13762", + UserTransportInfo: userTransportInfo, + CustomServicesTransportInfo: customServicesTransportInfo, + Enabled: true, + } + fmt.Println("Create an IotPlatformInfo: ", iotPlatformInfo) + body, err := json.Marshal(iotPlatformInfo) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("body: ", string(body)) + /****************************** + * request execution section + ******************************/ + _, err = sendRequest(http.MethodPost, "/registered_devices", bytes.NewBuffer(body), nil, nil, nil, http.StatusBadRequest, RegisterediotplatformsPOST) + if err != nil { + t.Fatalf("Shall received 400 Bad Request due to TransportType missing field") + } + fmt.Println("Received expected response") + /****************************** + * back to initial state section + ******************************/ + terminateScenario() +} + +func TestRegisterediotplatformsGET(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + initializeVars() + err := Init() + if err != nil { + t.Fatalf("Error initializing test basic procedure") + } + err = Run() + if err != nil { + t.Fatalf("Error running test basic procedure") + } + fmt.Println("Set a scenario") + initialiseScenario(testScenario) + time.Sleep(1000 * time.Millisecond) + updateScenario("mobility1") + + /****************************** + * expected response section + ******************************/ + expected_iotPlatformInfo, err := createRegisterediotplatforms() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + fmt.Println("expected_iotPlatformInfo: ", expected_iotPlatformInfo) + + /****************************** + * request body section + ******************************/ + + /****************************** + * request execution section + ******************************/ + rr, err := sendRequest(http.MethodGet, "/registered_devices", nil, nil, nil, nil, http.StatusOK, RegisterediotplatformsGET) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("Respone: rr: ", rr) + var resp []IotPlatformInfo + err = json.Unmarshal([]byte(rr), &resp) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("Respone: resp: ", resp) + if len(resp) != 1 { + t.Fatalf("Invalid response length") + } + if !validateIotPlatformInfo(resp[0], expected_iotPlatformInfo) { + t.Errorf("Response mismatch") + } + + fmt.Println("Received expected response") + /****************************** + * back to initial state section + ******************************/ + _ = deleteRegisterediotplatforms(expected_iotPlatformInfo.IotPlatformId) + terminateScenario() +} + +func TestRegisterediotplatformsByIdGET(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + initializeVars() + err := Init() + if err != nil { + t.Fatalf("Error initializing test basic procedure") + } + err = Run() + if err != nil { + t.Fatalf("Error running test basic procedure") + } + fmt.Println("Set a scenario") + initialiseScenario(testScenario) + time.Sleep(1000 * time.Millisecond) + updateScenario("mobility1") + + /****************************** + * expected response section + ******************************/ + expected_iotPlatformInfo, err := createRegisterediotplatforms() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + fmt.Println("expected_iotPlatformInfo: ", expected_iotPlatformInfo) + + /****************************** + * request vars section + ******************************/ + vars := make(map[string]string) + vars["registeredIotPlatformId"] = expected_iotPlatformInfo.IotPlatformId + + /****************************** + * request body section + ******************************/ + + /****************************** + * request execution section + ******************************/ + rr, err := sendRequest(http.MethodGet, "/registered_devices", nil, vars, nil, nil, http.StatusOK, RegisterediotplatformsByIdGET) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("Respone: rr: ", rr) + var resp IotPlatformInfo + err = json.Unmarshal([]byte(rr), &resp) + if err != nil { + t.Fatalf(err.Error()) + } + fmt.Println("Respone: resp: ", resp) + if !validateIotPlatformInfo(resp, expected_iotPlatformInfo) { + t.Errorf("Response mismatch") + } + + fmt.Println("Received expected response") + /****************************** + * back to initial state section + ******************************/ + _ = deleteRegisterediotplatforms(expected_iotPlatformInfo.IotPlatformId) + terminateScenario() +} + +func TestRegisterediotplatformsDelete(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + initializeVars() + err := Init() + if err != nil { + t.Fatalf("Error initializing test basic procedure") + } + err = Run() + if err != nil { + t.Fatalf("Error running test basic procedure") + } + fmt.Println("Set a scenario") + initialiseScenario(testScenario) + time.Sleep(1000 * time.Millisecond) + updateScenario("mobility1") + + /****************************** + * expected response section + ******************************/ + expected_iotPlatformInfo, err := createRegisterediotplatforms() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + fmt.Println("expected_iotPlatformInfo: ", expected_iotPlatformInfo) + + /****************************** + * request vars section + ******************************/ + vars := make(map[string]string) + vars["registeredIotPlatformId"] = expected_iotPlatformInfo.IotPlatformId + + /****************************** + * request body section + ******************************/ + + /****************************** + * request execution section + ******************************/ + _, err = sendRequest(http.MethodDelete, "/registered_devices", nil, vars, nil, nil, http.StatusNoContent, RegisterediotplatformsByIdDELETE) + if err != nil { + t.Fatalf(err.Error()) + } + + fmt.Println("Received expected response") + /****************************** + * back to initial state section + ******************************/ + terminateScenario() +} + +func createRegisterediotplatforms() (resp IotPlatformInfo, err error) { + var mb_transportInfo TransportType + mb_transportInfo = MB_TOPIC_BASED + var security = SecurityInfo{} + var implSpecificInfo = ImplSpecificInfo{} + var transportInfo TransportType + transportInfo = REST_HTTP + var adresses = []Addresses{} + adresses = append(adresses, Addresses{ + Host: "172.29.10.56", + Port: 1883, + }) + var endpoint = EndPointInfo{ + Addresses: adresses, + } + var userTransportInfo = []MbTransportInfo{} + userTransportInfo = append(userTransportInfo, MbTransportInfo{ + Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", + Name: "MQTT", + Description: "MQTT", + Type_: &mb_transportInfo, + Protocol: "MQTT", + Version: "2", + Endpoint: &endpoint, + Security: &security, + ImplSpecificInfo: &implSpecificInfo, + }) + var adresses_1 = []Addresses{} + adresses_1 = append(adresses_1, Addresses{ + Host: "172.29.10.20", + Port: 31110, + }) + var customServicesTransportInfo = []TransportInfo{} + var endPointInfo_1 = EndPointInfo{ + Addresses: adresses_1, + } + customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ + Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", + Name: "/laboai-acme-ic-cse", + Description: "ACME oneM2M CSE", + Type_: &transportInfo, + Protocol: "REST_HTTP", + Version: "4", + Endpoint: &endPointInfo_1, + Security: &security, + }) + var iotPlatformInfo = IotPlatformInfo{ + IotPlatformId: "523f2df1-8927-429f-906c-56ba92d13762", + UserTransportInfo: userTransportInfo, + CustomServicesTransportInfo: customServicesTransportInfo, + Enabled: true, + } + fmt.Println("Create an IotPlatformInfo: ", iotPlatformInfo) + body, err := json.Marshal(iotPlatformInfo) + if err != nil { + return resp, err + } + fmt.Println("body: ", string(body)) + + rr, err := sendRequest(http.MethodPost, "/registered_devices", bytes.NewBuffer(body), nil, nil, nil, http.StatusCreated, RegisterediotplatformsPOST) + if err != nil { + return resp, err + } + fmt.Println("Respone: rr: ", rr) + err = json.Unmarshal([]byte(rr), &resp) + if err != nil { + return resp, err + } + + return resp, nil +} + +func deleteRegisterediotplatforms(iotPlatformId string) (err error) { + vars := make(map[string]string) + vars["registeredIotPlatformId"] = iotPlatformId + _, err = sendRequest(http.MethodDelete, "/registered_devices", nil, vars, nil, nil, http.StatusNoContent, RegisterediotplatformsByIdDELETE) + if err != nil { + return err + } + + return nil +} + +func validateIotPlatformInfo(received IotPlatformInfo, expected IotPlatformInfo) bool { + fmt.Println("validateIotPlatformInfo: received: ", received) + fmt.Println("validateIotPlatformInfo: expected: ", expected) + + if received.IotPlatformId != expected.IotPlatformId { + fmt.Println("received.IotPlatformId != IotPlatformId") + return false + } + + if len(received.UserTransportInfo) != len(expected.UserTransportInfo) { + fmt.Println("len(received.UserTransportInfo) mismatch") + return false + } else { + } + + if len(received.CustomServicesTransportInfo) != len(expected.CustomServicesTransportInfo) { + fmt.Println("len(received.CustomServicesTransportInfo) mismatch") + return false + } else { + } + + if received.Enabled != expected.Enabled { + fmt.Println("received.Enabled != Enabled") + return false + } + + fmt.Println("validateIotPlatformInfo: succeed") + return true +} + +func initializeVars() { + mod.DbAddress = redisTestAddr + redisAddr = redisTestAddr + influxAddr = influxTestAddr + sandboxName = testScenarioName + os.Setenv("MEEP_PREDICT_MODEL_SUPPORTED", "true") + os.Setenv("MEEP_SANDBOX_NAME", testScenarioName) + os.Setenv("MEEP_PUBLIC_URL", "http://localhost") +} + +func initialiseScenario(testScenario string) { + + //clear DB + cleanUp() + + cfg := mod.ModelCfg{ + Name: testScenarioName, + Namespace: sandboxName, + Module: "test-mod", + UpdateCb: nil, + DbAddr: redisAddr, + } + var err error + m, err = mod.NewModel(cfg) + if err != nil { + log.Error("Failed to create model: ", err) + return + } + fmt.Println("initialiseScenario: model created") + + // Create message queue + mqLocal, err = mq.NewMsgQueue(mq.GetLocalName(testScenarioName), "test-mod", testScenarioName, redisAddr) + if err != nil { + log.Error("Failed to create Message Queue with error: ", err) + return + } + fmt.Println("Message Queue created") + + fmt.Println("Set Model") + err = m.SetScenario([]byte(testScenario)) + if err != nil { + log.Error("Failed to set model: ", err) + return + } + + err = m.Activate() + if err != nil { + log.Error("Failed to activate scenario with err: ", err.Error()) + return + } + + msg := mqLocal.CreateMsg(mq.MsgScenarioActivate, mq.TargetAll, testScenarioName) + err = mqLocal.SendMsg(msg) + if err != nil { + log.Error("Failed to send message: ", err) + return + } + + time.Sleep(100 * time.Millisecond) + +} + +func updateScenario(testUpdate string) { + + switch testUpdate { + case "mobility1": + // mobility event of ue1 to zone2-poa1 + elemName := "ue1" + destName := "zone2-poa1" + + _, _, err := m.MoveNode(elemName, destName, nil) + if err != nil { + log.Error("Error sending mobility event") + } + + msg := mqLocal.CreateMsg(mq.MsgScenarioUpdate, mq.TargetAll, testScenarioName) + err = mqLocal.SendMsg(msg) + if err != nil { + log.Error("Failed to send message: ", err) + } + case "mobility2": + // mobility event of ue1 to zone2-poa1 + elemName := "ue1" + destName := "zone1-poa-cell1" + + _, _, err := m.MoveNode(elemName, destName, nil) + if err != nil { + log.Error("Error sending mobility event") + } + + msg := mqLocal.CreateMsg(mq.MsgScenarioUpdate, mq.TargetAll, testScenarioName) + err = mqLocal.SendMsg(msg) + if err != nil { + log.Error("Failed to send message: ", err) + } + case "mobility3": + // mobility event of ue1 to zone1-poa-cell2 + elemName := "ue1" + destName := "zone1-poa-cell2" + + _, _, err := m.MoveNode(elemName, destName, nil) + if err != nil { + log.Error("Error sending mobility event") + } + + msg := mqLocal.CreateMsg(mq.MsgScenarioUpdate, mq.TargetAll, testScenarioName) + err = mqLocal.SendMsg(msg) + if err != nil { + log.Error("Failed to send message: ", err) + } + default: + } + time.Sleep(100 * time.Millisecond) +} + +func terminateScenario() { + if mqLocal != nil { + _ = Stop() + msg := mqLocal.CreateMsg(mq.MsgScenarioTerminate, mq.TargetAll, testScenarioName) + err := mqLocal.SendMsg(msg) + if err != nil { + log.Error("Failed to send message: ", err) + } + time.Sleep(100 * time.Millisecond) + } +} + +func sendRequest(method string, url string, body io.Reader, vars map[string]string, query map[string]string, location *string, code int, f http.HandlerFunc) (string, error) { + req, err := http.NewRequest(method, url, body) + if err != nil || req == nil { + return "", err + } + if vars != nil { + req = mux.SetURLVars(req, vars) + } + if query != nil { + q := req.URL.Query() + for k, v := range query { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + rr := httptest.NewRecorder() + handler := http.HandlerFunc(f) + + // Our handlers satisfy http.Handler, so we can call their ServeHTTP method + // directly and pass in our Request and ResponseRecorder. + handler.ServeHTTP(rr, req) + + time.Sleep(50 * time.Millisecond) + + // Check the status code is what we expect. + if status := rr.Code; status != code { + s := fmt.Sprintf("Wrong status code - got %v want %v", status, code) + return "", errors.New(s) + } + + // Set Location header in case of POST + if location != nil { + s := rr.Header().Get("Location") + if rr == nil { + s := fmt.Sprintf("Header Location expected") + return "", errors.New(s) + } else if !strings.Contains(s, *location) { + s := fmt.Sprintf("Wrong Header Location - got %s want %s", s, *location) + return "", errors.New(s) + } + } + + return string(rr.Body.String()), nil +} diff --git a/go-apps/meep-sss/go.mod b/go-apps/meep-sss/go.mod index 682557e6ad7ed527a68017ab12d8162307a22f4d..61ef5f673aa4f872ecef72fc49044d95a7e5c74f 100644 --- a/go-apps/meep-sss/go.mod +++ b/go-apps/meep-sss/go.mod @@ -8,7 +8,7 @@ require ( github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-data-model v0.0.0 // indirect github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-gis-engine-client v0.0.0 // indirect github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-http-logger v0.0.0 - github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-iot-mgr v0.0.0 + github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-onem2m-mgr v0.0.0 github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger v0.0.0 github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-metrics v0.0.0 github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-model v0.0.0 @@ -29,7 +29,7 @@ replace ( github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-data-model => ../../go-packages/meep-data-model github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-gis-engine-client => ../../go-packages/meep-gis-engine-client github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-http-logger => ../../go-packages/meep-http-logger - github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-iot-mgr => ../../go-packages/meep-iot-mgr + github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-onem2m-mgr => ../../go-packages/meep-onem2m-mgr github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger => ../../go-packages/meep-logger github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-metrics => ../../go-packages/meep-metrics github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-model => ../../go-packages/meep-model diff --git a/go-apps/meep-sss/sbi/sss-sbi.go b/go-apps/meep-sss/sbi/sss-sbi.go index 7022b91c790c5909843da23d53761c3d660f7579..32aa96806d911ba44f2b2cf0597beef4dd1add09 100644 --- a/go-apps/meep-sss/sbi/sss-sbi.go +++ b/go-apps/meep-sss/sbi/sss-sbi.go @@ -19,10 +19,10 @@ package sbi import ( "sync" - tm "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-iot-mgr" log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" mod "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-model" mq "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-mq" + tm "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-onem2m-mgr" sam "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-swagger-api-mgr" ) @@ -121,12 +121,12 @@ func Init(cfg SbiCfg) (err error) { } // Connect to IOT Manager - sbi.iotMgr, err = tm.NewIotMgr(sbi.moduleName, sbi.sandboxName) + sbi.iotMgr, err = tm.NewOneM2MMgr(sbi.moduleName, sbi.sandboxName) if err != nil { - log.Error("Failed connection to IOT Manager: ", err) + log.Error("Failed connection to OneM2M Manager: ", err) return err } - log.Info("Connected to IOT Manager") + log.Info("Connected to OneM2M Manager") // Initialize service processActiveScenarioUpdate() diff --git a/go-apps/meep-sss/server/meep-sss.go b/go-apps/meep-sss/server/meep-sss.go index e7baecb400916ce8c32d9389f459cf3a966f3a0d..c99c45c39de8ca55e73549b4c1888c9f9e6177cf 100644 --- a/go-apps/meep-sss/server/meep-sss.go +++ b/go-apps/meep-sss/server/meep-sss.go @@ -565,7 +565,9 @@ func sensorDiscoveryLookupGET(w http.ResponseWriter, r *http.Request) { log.Debug("sensorDiscoveryLookupGET: type(q[sensorCharacteristicList][0]): ", reflect.TypeOf(q["sensorCharacteristicList"][0])) //q: map[geographicalArea:[[object Object]] sensorCharacteristicList:[[object Object]] sensorPropertyList:[string1,string2] type:[string]]","time":"2025-02-04T08:35:35Z"} - w.WriteHeader(http.StatusOK) + //w.WriteHeader(http.StatusOK) + err := errors.New("Not implemented") + errHandlerProblemDetails(w, err.Error(), http.StatusNotImplemented) } func sensorDiscoverySubscriptionGET(w http.ResponseWriter, r *http.Request) { diff --git a/go-packages/meep-sss-mgr/.gitignore b/go-packages/meep-sss-mgr/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..739ff46590540ad860b48535770503a5af9d0202 --- /dev/null +++ b/go-packages/meep-sss-mgr/.gitignore @@ -0,0 +1 @@ +grid_map.yaml diff --git a/go-packages/meep-sss-mgr/go.mod b/go-packages/meep-sss-mgr/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..ba299dc7160ae3aed638aa557b125dc040553b21 --- /dev/null +++ b/go-packages/meep-sss-mgr/go.mod @@ -0,0 +1,17 @@ +module github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-onem2m-mgr + +go 1.16 + +require ( + github.com/BurntSushi/toml v1.2.0 // indirect + github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger v0.0.0 + github.com/eclipse/paho.mqtt.golang v1.4.2 + github.com/google/uuid v1.6.0 + github.com/gorilla/mux v1.8.1 + github.com/lib/pq v1.10.7 + github.com/roymx/viper v1.3.3-0.20190416163942-b9a223fc58a3 + github.com/streadway/amqp v1.1.0 + +) + +replace github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger => ../../go-packages/meep-logger diff --git a/go-packages/meep-sss-mgr/go.sum b/go-packages/meep-sss-mgr/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..006aacd6c4a21fb65386d78a4b64559473586a18 --- /dev/null +++ b/go-packages/meep-sss-mgr/go.sum @@ -0,0 +1,106 @@ +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4= +github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA= +github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik= +github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/roymx/viper v1.3.3-0.20190416163942-b9a223fc58a3 h1:lBNvYUFo7d4fHs8BXUmoTzbdUo4usq6PlP5qn894sGA= +github.com/roymx/viper v1.3.3-0.20190416163942-b9a223fc58a3/go.mod h1:jo59Sv6xirZtbxbaZbCtrQd1CSufmcxJZIC8hm2tepw= +github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM= +github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U= +golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/go-packages/meep-sss-mgr/onem2m-mgr.go b/go-packages/meep-sss-mgr/onem2m-mgr.go new file mode 100644 index 0000000000000000000000000000000000000000..ad1ca95d64d1c65e9b712164c5eeed36d52bc2b8 --- /dev/null +++ b/go-packages/meep-sss-mgr/onem2m-mgr.go @@ -0,0 +1,1036 @@ +/* + * Copyright (c) 2024 The AdvantEDGE Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package onem2mmgr + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "reflect" + "strconv" + "strings" + "sync" + "time" + + log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" + + uuid "github.com/google/uuid" + "github.com/gorilla/mux" +) + +// IOT Manager +type IotMgr struct { + name string + namespace string + mutex sync.Mutex + wg sync.WaitGroup + refreshTicker *time.Ticker +} + +type IotPlatformInfo struct { + IotPlatformId string + UserTransportInfo []MbTransportInfo + CustomServicesTransportInfo []TransportInfo + Enabled bool + DeviceInfo *DeviceInfo +} + +type MbTransportInfo struct { + Id string + Name string + Description string + Type_ *string + Protocol string + Version string + Endpoint *EndPointInfo + Security *SecurityInfo + ImplSpecificInfo *ImplSpecificInfo +} + +type TransportInfo struct { + Id string + Name string + Description string + Type_ *string + Protocol string + Version string + Endpoint *EndPointInfo + Security *SecurityInfo + ImplSpecificInfo string +} + +type EndPointInfo struct { + Uris []string + Fqdn []string + Addresses []Addresses + Alternative string +} + +type Addresses struct { + Host string + Port int32 +} + +type SecurityInfo struct { + OAuth2Info *OAuth2Info + Extensions string +} + +type OAuth2Info struct { + GrantTypes []string + TokenEndpoint string +} + +type ImplSpecificInfo struct { + EventTopics []string + UplinkTopics []string + DownlinkTopics []string +} + +// ETSI GS MEC 046 V3.1.1 (2024-04) Clause 6.2.1 Type: SensorDiscoveryInfo +type SensorCharacteristic struct { + CharacteristicName string + CharacteristicValue string + CharacteristicUnitOfMeasure *string +} + +type TrafficRuleDescriptor struct { + TrafficRuleId string + FilterType string + Priority int32 + TrafficFilter []TrafficFilter + Action string + DstInterface *InterfaceDescriptor +} + +type InterfaceDescriptor struct { + InterfaceType string + //TunnelInfo *TunnelInfo FSCOM Not supported + SrcMACAddress string + DstMACAddress string + DstIPAddress string +} + +type TrafficFilter struct { + SrcAddress []string + DstAddress []string + SrcPort []string + DstPort []string + Protocol []string + Tag []string + Uri []string + PacketLabel []string + SrcTunnelAddress []string + TgtTunnelAddress []string + SrcTunnelPort []string + DstTunnelPort []string + QCI int32 + TC int32 +} + +type KeyValuePair struct { + Key string + Value string +} + +type DeviceInfo struct { + DeviceAuthenticationInfo string + DeviceMetadata []KeyValuePair + Gpsi string + Pei string + Supi string + Msisdn string + Imei string + Imsi string + Iccid string + DeviceId string + RequestedMecTrafficRule []TrafficRuleDescriptor + RequestedIotPlatformId string + RequestedUserTransportId string + //DeviceSpecificMessageFormats *DeviceSpecificMessageFormats + //DownlinkInfo *DownlinkInfo + ClientCertificate string + Enabled bool + // SensorIdentifier string + // SensorStatusType string + // SensorPropertyList []string + // SensorCharacteristicList []SensorCharacteristic +} + +var registeredIotPlatformsMap = map[string]IotPlatformInfo{} // List of discovered IOT Plateform +var devicesMap = map[string]DeviceInfo{} // Map device by deviceId +var devicesPerPlatformMap = map[string][]string{} // Map deviceIds per platform +var platformPerUserTransportIdMap = map[string][]string{} // Map userTransportId per platform + +// Timer to refresh devices list for all IoT platform +const refreshTickerExpeary = 10 // In seconds + +// Enable profiling +const profiling = false + +var profilingTimers map[string]time.Time + +const ( + headerAccept = "application/json" + headerContentType = "application/json" +) + +// NewIotMgr - Creates and initializes a new IOT Traffic Manager +func NewIotMgr(name string, namespace string) (tm *IotMgr, err error) { + if name == "" { + err = errors.New("Missing connector name") + return nil, err + } + + // Create new Traffic Manager + tm = new(IotMgr) + tm.name = name + if namespace != "" { + tm.namespace = namespace + } else { + tm.namespace = "default" + } + + tm.init() + + return tm, nil +} + +// Profiling init +func (tm *IotMgr) init() { + if profiling { + profilingTimers = make(map[string]time.Time) + } + + registeredIotPlatformsMap = make(map[string]IotPlatformInfo, 0) + devicesMap = make(map[string]DeviceInfo, 0) + devicesPerPlatformMap = make(map[string][]string, 0) + platformPerUserTransportIdMap = make(map[string][]string, 0) + + tm.refreshTicker = nil +} + +// DeleteIotMgr - +func (tm *IotMgr) DeleteIotMgr() (err error) { + tm.stopRefreshTicker() + + return nil +} + +func (tm *IotMgr) startRefreshTicker() { + log.Debug("Starting refresh loop") + tm.refreshTicker = time.NewTicker(refreshTickerExpeary * time.Second) + go func() { + if tm.refreshTicker != nil { + for range tm.refreshTicker.C { + // Refresh the list of devices + tm.wg.Add(1) + err := tm.populateDevicesPerIotPlatforms() + if err != nil { + log.Error(err) + } + tm.wg.Done() + } + } + }() +} + +func (tm *IotMgr) stopRefreshTicker() { + if tm.refreshTicker != nil { + // Refresh the list of devices + tm.wg.Add(1) + tm.refreshTicker.Stop() + tm.refreshTicker = nil + registeredIotPlatformsMap = nil + devicesMap = nil + devicesPerPlatformMap = nil + platformPerUserTransportIdMap = nil + tm.wg.Done() + log.Debug("Refresh loop stopped") + } +} + +func (tm *IotMgr) RegisterIotPlatformInfo(iotPlatformInfo IotPlatformInfo) (err error) { + if profiling { + profilingTimers["RegisterIotPlatformInfo"] = time.Now() + } + + log.Info(">>> RegisterIotPlatformInfo: iotPlatformId: ", iotPlatformInfo) + if iotPlatformInfo.Enabled { + registeredIotPlatformsMap[iotPlatformInfo.IotPlatformId] = iotPlatformInfo + } // else, Skip disabled platform + + if len(registeredIotPlatformsMap) == 1 { + log.Info("RegisterIotPlatformInfo: Starting timer") + tm.startRefreshTicker() + log.Info("RegisterIotPlatformInfo: timer running") + } + + if profiling { + now := time.Now() + log.Debug("RegisterIotPlatformInfo: ", now.Sub(profilingTimers["RegisterIotPlatformInfo"])) + } + return nil +} + +func (tm *IotMgr) DeregisterIotPlatformInfo(iotPlatformId string) (err error) { + if profiling { + profilingTimers["DeregisterIotPlatformInfo"] = time.Now() + } + + tm.wg.Wait() + log.Info("DeregisterIotPlatformInfo: After Synchro") + + log.Info(">>> DeregisterIotPlatformInfo: iotPlatformId: ", iotPlatformId) + // Remove the list of the devices for this IoT platform + if val, ok := devicesPerPlatformMap[iotPlatformId]; ok { + // Free resources from devicesMap map + for _, dev := range val { + delete(devicesMap, dev) + } // End of 'for' statement + delete(devicesPerPlatformMap, iotPlatformId) + log.Info("DeregisterIotPlatformInfo: platformPerUserTransportIdMap (before): ", iotPlatformId) + for _, rule := range platformPerUserTransportIdMap { + for idx, pltf := range rule { + if pltf == iotPlatformId { + rule = append(rule[:idx], rule[idx+1:]...) + } + } // End of 'for' statement + } // End of 'for' statement + log.Info("DeregisterIotPlatformInfo: platformPerUserTransportIdMap (after): ", iotPlatformId) + } + if _, ok := registeredIotPlatformsMap[iotPlatformId]; ok { + delete(registeredIotPlatformsMap, iotPlatformId) + if len(registeredIotPlatformsMap) == 0 { + tm.stopRefreshTicker() + } + } + + if profiling { + now := time.Now() + log.Debug("DeregisterIotPlatformInfo: ", now.Sub(profilingTimers["DeregisterIotPlatformInfo"])) + } + return nil +} + +func (tm *IotMgr) GetDevices() (devices []DeviceInfo, err error) { + if profiling { + profilingTimers["GetDevices"] = time.Now() + } + + log.Info(">>> GetDevices") + + tm.wg.Wait() + log.Info("GetDevices: After Synchro") + + devices = make([]DeviceInfo, 0) + if len(registeredIotPlatformsMap) == 0 { + return devices, nil + } + + for _, v := range devicesMap { + log.Info("GetDevices: adding device: ", v) + devices = append(devices, v) + } // End of 'for' statement + log.Info("GetDevices: devices: ", devices) + + if profiling { + now := time.Now() + log.Debug("GetDevices: ", now.Sub(profilingTimers["GetDevices"])) + } + + return devices, nil +} + +func (tm *IotMgr) GetDevice(deviceId string) (device DeviceInfo, err error) { + if profiling { + profilingTimers["GetDevice"] = time.Now() + } + + log.Info(">>> GetDevice: deviceId: ", deviceId) + + tm.wg.Wait() + log.Info("GetDevice: After Synchro") + + if val, ok := devicesMap[deviceId]; !ok { + err = errors.New("Wrong Device identifier") + return device, err + } else { + device = val + } + + if profiling { + now := time.Now() + log.Debug("GetDevice: ", now.Sub(profilingTimers["GetDevice"])) + } + log.Info("GetDevice: device: ", device) + + return device, nil +} + +func (tm *IotMgr) CreateDevice(device DeviceInfo) (deviceResp DeviceInfo, err error) { + log.Info(">>> CreateDevice: ", device) + + // RequestedMecTrafficRule is not supported yet + if len(device.RequestedMecTrafficRule) != 0 { + err = errors.New("Unsupported traffic rule provided") + log.Error(err.Error()) + return deviceResp, nil + } + if len(device.RequestedIotPlatformId) != 0 { + deviceResp, err = tm.createDeviceWithIotPlatformId(device, device.RequestedIotPlatformId) + } else { + deviceResp, err = tm.createDeviceWithRequestedUserTransportId(device, device.RequestedUserTransportId) + } + if err != nil { + log.Error(err.Error()) + return deviceResp, err + } + log.Info("CreateDevice: deviceResp: ", deviceResp) + + return deviceResp, nil +} + +/* + * func populateDevicesPerIotPlatforms IoT devices for all registered Iot platform + * @return {struct} nil on success, error otherwise + */ +func (tm *IotMgr) populateDevicesPerIotPlatforms() error { + + tm.mutex.Lock() + defer tm.mutex.Unlock() + + if profiling { + profilingTimers["populateDevicesPerIotPlatforms"] = time.Now() + } + + if len(registeredIotPlatformsMap) == 0 { + return nil + } + + // Refresh the list of devices for all registered Iot platform + for _, iotPlatform := range registeredIotPlatformsMap { + log.Debug("populateDevicesPerIotPlatforms: processing: ", iotPlatform.IotPlatformId) + err := tm.populateDevices(iotPlatform) + if err != nil { + log.Error("populateDevicesPerIotPlatforms: ", err) + continue + } + } // End of 'for' statement + + if profiling { + now := time.Now() + log.Debug("populateDevicesPerIotPlatforms: ", now.Sub(profilingTimers["populateDevicesPerIotPlatforms"])) + } + + return nil +} + +/* + * func PopulateDevices IoT devices for the specified Iot platform + * @param {string} iotPlatformId contains the IoT platform identifier + * @return {struct} nil on success, error otherwise + */ +func (tm *IotMgr) populateDevices(iotPlatformInfo IotPlatformInfo) error { + if profiling { + profilingTimers["populateDevices"] = time.Now() + } + + log.Info(">>> populateDevices: iotPlatformId=", iotPlatformInfo.IotPlatformId) + + // 1. Get the list of the AE + // Build the URL + t := registeredIotPlatformsMap[iotPlatformInfo.IotPlatformId].CustomServicesTransportInfo[0] + log.Debug("populateDevices: t.Endpoint.Addresses[0]=", t.Endpoint.Addresses[0]) + url := "http://" + t.Endpoint.Addresses[0].Host + ":" + strconv.Itoa(int(t.Endpoint.Addresses[0].Port)) + "/" + t.Name + log.Debug("populateDevices: url=", url) + // Build the headers + var headers = http.Header{} + headers["Accept"] = []string{headerAccept} + headers["Content-Type"] = []string{headerContentType} + headers["X-M2M-Origin"] = []string{"CAdmin"} // FIXME FSCOM How to get it + headers["X-M2M-RI"] = []string{uuid.New().String()} + headers["X-M2M-RVI"] = []string{t.Version} + // Build the queries + queries := map[string]string{} + queries["fu"] = "1" // Filter usage + queries["ty"] = "4" // Filter on oneM2M CIN for device + // Send the request + response, err := sendRequest("GET", url, headers, nil, nil, queries, 200) + if err != nil { + log.Error("populateDevices: ", err.Error()) + return err + } + log.Debug("populateDevices: response: ", string(response)) + + var oneM2M_uril map[string][]string + err = json.Unmarshal(response, &oneM2M_uril) + if err != nil { + log.Error("populateDevices: ", err.Error()) + return err + } + log.Debug("populateDevices: oneM2M_uril: ", len(oneM2M_uril)) + log.Debug(oneM2M_uril) + if _, ok := oneM2M_uril["m2m:uril"]; !ok { + err := errors.New("populateDevices: Key not found: m2m:uril") + log.Error(err.Error()) + return err + } + // Loop for each CIN and build the device list + for _, v := range oneM2M_uril["m2m:uril"] { + log.Debug("populateDevices: Processing key: ", v) + + url := "http://" + t.Endpoint.Addresses[0].Host + ":" + strconv.Itoa(int(t.Endpoint.Addresses[0].Port)) + "/" + v + log.Debug("populateDevices: url=", url) + // Build the headers + var headers = http.Header{} + headers["Accept"] = []string{headerAccept} + headers["Content-Type"] = []string{headerContentType} + headers["X-M2M-Origin"] = []string{"CAdmin"} // FIXME FSCOM How to get it + headers["X-M2M-RI"] = []string{uuid.New().String()} + headers["X-M2M-RVI"] = []string{t.Version} + // Build the queries + queries := map[string]string{} + queries["fu"] = "2" // Filter usage + // Send the request + response, err := sendRequest("GET", url, headers, nil, nil, queries, 200) + if err != nil { + log.Error("populateDevices: ", err.Error()) + return err + } + log.Debug("populateDevices: response: ", string(response)) + var oneM2M_cin map[string]map[string]interface{} + err = json.Unmarshal(response, &oneM2M_cin) + if err != nil { + log.Error("populateDevices: ", err.Error()) + continue + } + log.Debug("populateDevices: type(oneM2M_cin): ", reflect.TypeOf(oneM2M_cin)) + log.Debug("populateDevices: len(oneM2M_cin): ", len(oneM2M_cin)) + log.Debug("populateDevices: oneM2M_cin: ", oneM2M_cin) + for _, m := range oneM2M_cin { + //log.Debug("==> ", i, " value is ", m) + var device = DeviceInfo{} + device.RequestedIotPlatformId = iotPlatformInfo.IotPlatformId + device.RequestedUserTransportId = registeredIotPlatformsMap[iotPlatformInfo.IotPlatformId].UserTransportInfo[0].Id + device.Enabled = true + + // m is a map[string]interface. + // loop over keys and values in the map. + for k, v := range m { + log.Debug(k, " value is ", v) + log.Debug("populateDevices: type(v): ", reflect.TypeOf(v)) + + if item, ok := v.(string); ok { + device.DeviceMetadata = append( + device.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: string(item), + }) + } else if item, ok := v.(float64); ok { + device.DeviceMetadata = append( + device.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: strconv.FormatFloat(item, 'f', -1, 64), + }) + } else if item, ok := v.(int64); ok { + device.DeviceMetadata = append( + device.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: strconv.FormatInt(item, 10), + }) + } else if item, ok := v.(bool); ok { + device.DeviceMetadata = append( + device.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: strconv.FormatBool(item), + }) + } else if item, ok := v.([]string); ok { + device.DeviceMetadata = append( + device.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: strings.Join(item, ","), + }) + } else { + log.Error("populateDevices: Failed to process ", k) + } + + // if k == "rn" { + // if item, ok := v.(string); ok { + // device.DeviceId = item + // } else { + // log.Error("populateDevices: Failed to process ", k) + // } + // } else if k == "ri" { + // if item, ok := v.(string); ok { + // device.SensorIdentifier = item + // } else { + // log.Error("populateDevices: Failed to process ", k) + // } + // } else if k == "ty" { + // if item, ok := v.(float64); ok { + // device.SensorStatusType = strconv.FormatFloat(item, 'f', -1, 64) + // } else { + // log.Error("populateDevices: Failed to process ", k) + // } + // } else { // default: if k == "lt" || k == "et" || k == "ct" || k == "st" || k == "pi" || k == "lbl" { + // if item, ok := v.(string); ok { + // device.SensorCharacteristicList = append( + // device.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: string(item), + // CharacteristicUnitOfMeasure: nil, + // }) + // } else if item, ok := v.(float64); ok { + // device.SensorCharacteristicList = append( + // device.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: strconv.FormatFloat(item, 'f', -1, 64), + // CharacteristicUnitOfMeasure: nil, + // }) + // } else if item, ok := v.([]string); ok { + // device.SensorCharacteristicList = append( + // device.SensorCharacteristicList, + // SensorCharacteristic{ + // CharacteristicName: k, + // CharacteristicValue: strings.Join(item, ","), + // CharacteristicUnitOfMeasure: nil, + // }) + // } else { + // log.Error("populateDevices: Failed to process ", k) + // } + // } + } // End of 'for' loop + log.Info("populateDevices: device: ", device) + devicesMap[device.DeviceId] = device + devicesPerPlatformMap[device.RequestedIotPlatformId] = append(devicesPerPlatformMap[device.RequestedIotPlatformId], device.DeviceId) + } // End of 'for' loop + + } // End of 'for' statement + log.Info("populateDevices: devicesMap: ", devicesMap) + log.Info("populateDevices: devicesPerPlatformMap: ", devicesPerPlatformMap) + + if profiling { + now := time.Now() + log.Debug("populateDevices: ", now.Sub(profilingTimers["populateDevices"])) + } + + return nil +} + +func (tm *IotMgr) createDeviceWithIotPlatformId(device DeviceInfo, requestedIotPlatformId string) (deviceResp DeviceInfo, err error) { + log.Info(">>> createDeviceWithIotPlatformId: ", device) + + tm.wg.Wait() + log.Info("createDeviceWithIotPlatformId: After Synchro") + + // Sanity checks + if _, ok := registeredIotPlatformsMap[requestedIotPlatformId]; !ok { + err = errors.New("Invalid IotPlatform identifier") + return deviceResp, err + } + + deviceResp, err = tm.oneM2M_create(device, requestedIotPlatformId, "ae") + if err != nil { + log.Error("createDeviceWithIotPlatformId: ", err.Error()) + return device, err + } + log.Debug("createDeviceWithIotPlatformId: deviceResp: ", deviceResp) + + return deviceResp, nil +} + +func (tm *IotMgr) createDeviceWithRequestedUserTransportId(device DeviceInfo, requestedUserTransportId string) (deviceResp DeviceInfo, err error) { + log.Info(">>> createDeviceWithRequestedUserTransportId: ", device) + + tm.wg.Wait() + log.Info("createDeviceWithIotPlatformId: After Synchro") + + if val, ok := platformPerUserTransportIdMap[requestedUserTransportId]; ok { + deviceResp, err = tm.createDeviceWithIotPlatformId(device, val[0]) + } else { + err = errors.New("Invalid UserTransportId") + } + if err != nil { + log.Error("createDeviceWithIotPlatformId: ", err.Error()) + return deviceResp, err + } + log.Info("createDeviceWithIotPlatformId: deviceResp: ", deviceResp) + + return deviceResp, nil +} + +func (tm *IotMgr) oneM2M_create(device DeviceInfo, requestedIotPlatformId string, type_ string) (deviceResp DeviceInfo, err error) { + // FIXME FSCOM: requestedIotPlatformId should be useless + + t := registeredIotPlatformsMap[requestedIotPlatformId].CustomServicesTransportInfo[0] + log.Debug("oneM2M_create: t.Endpoint.Addresses[0]=", t.Endpoint.Addresses[0]) + // Build the headers + var headers = http.Header{} + headers["Accept"] = []string{headerAccept} + headers["X-M2M-Origin"] = []string{"C" + requestedIotPlatformId} // FIXME FSCOM How to get it + headers["X-M2M-RI"] = []string{uuid.New().String()} + headers["X-M2M-RVI"] = []string{t.Version} + var s string + if type_ == "AE" { + s = headerContentType + ";ty=2" + } else if type_ == "CNT" { + s = headerContentType + ";ty=4" + } + headers["Content-Type"] = []string{s} + // Build the url and the body + var url string + var bodyMap = map[string]map[string]interface{}{} + // Initialize the entry + if type_ == "AE" { // FIXME FSCOM Clarify how to map Deviceinfo with oneM2M AE/CNT/fexContainer + bodyMap["m2m:ae"] = make(map[string]interface{}, 0) + bodyMap["m2m:ae"]["api"] = "Norg.etsi." + requestedIotPlatformId + "." + device.DeviceId + bodyMap["m2m:ae"]["rn"] = device.DeviceId + bodyMap["m2m:ae"]["rr"] = false + bodyMap["m2m:ae"]["srv"] = []string{t.Version} + url = "http://" + t.Endpoint.Addresses[0].Host + ":" + strconv.Itoa(int(t.Endpoint.Addresses[0].Port)) + "/" + t.Name + } else if type_ == "CNT" { + bodyMap["m2m:cnt"] = make(map[string]interface{}, 0) + bodyMap["m2m:cnt"]["mbs"] = 10000 + bodyMap["m2m:cnt"]["mni"] = 10 + bodyMap["m2m:cnt"]["rn"] = device.DeviceId + bodyMap["m2m:cnt"]["srv"] = []string{t.Version} + // Add metadata + if len(device.DeviceMetadata) != 0 { + for _, val := range device.DeviceMetadata { + log.Debug("oneM2M_create: Adding CNT metadata: ", val) + // FIXME FSCOM Add metadata + } // End of 'for' statement + } + url = "http://" + t.Endpoint.Addresses[0].Host + ":" + strconv.Itoa(int(t.Endpoint.Addresses[0].Port)) + "/" + t.Name + } else { + err = errors.New("oneM2M_create: Invalid type") + log.Error("oneM2M_create: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_create: url=", url) + log.Debug("oneM2M_create: bodyMap=", bodyMap) + body, err := json.Marshal(bodyMap) + if err != nil { + log.Error("oneM2M_create: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_create: Request body: ", string(body)) + // Send the request + response, err := sendRequest("POST", url, headers, bytes.NewBuffer(body), nil, nil, 201) + if err != nil { + log.Error("oneM2M_create: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_create: response: ", string(response)) + var d map[string]map[string]interface{} + err = json.Unmarshal(response, &d) + if err != nil { + log.Error("oneM2M_create: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_create: d: ", d) + + // Add additional entries + deviceResp, err = tm.oneM2M_deserialize(deviceResp, d) + if err != nil { + log.Error("oneM2M_create: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_create: deviceResp: ", deviceResp) + + return deviceResp, nil +} + +func (tm *IotMgr) oneM2M_discovery(device DeviceInfo, requestedIotPlatformId string, type_ string) (deviceResp DeviceInfo, err error) { + // FIXME FSCOM: requestedIotPlatformId should be useless + + // 1. Get the list of the AE + // Build the URL + t := registeredIotPlatformsMap[requestedIotPlatformId].CustomServicesTransportInfo[0] + log.Debug("oneM2M_discovery: t.Endpoint.Addresses[0]=", t.Endpoint.Addresses[0]) + url := "http://" + t.Endpoint.Addresses[0].Host + ":" + strconv.Itoa(int(t.Endpoint.Addresses[0].Port)) + "/" + t.Name + log.Debug("oneM2M_discovery: url=", url) + // Build the headers + var headers = http.Header{} + headers["Accept"] = []string{headerAccept} + headers["Content-Type"] = []string{headerContentType} + headers["X-M2M-Origin"] = []string{"CAdmin"} // FIXME FSCOM How to get it + headers["X-M2M-RI"] = []string{uuid.New().String()} + headers["X-M2M-RVI"] = []string{t.Version} + // Build the queries + queries := map[string]string{} + if type_ == "CN" { + queries["fu"] = "1" // Filter usage + queries["ty"] = "4" // Filter on oneM2M CIN for device + } else { + err = errors.New("oneM2M_discovery: Invalid type") + log.Error("oneM2M_discovery: ", err.Error()) + return deviceResp, err + } + // Send the request + response, err := sendRequest("GET", url, headers, nil, nil, queries, 200) + if err != nil { + log.Error("oneM2M_discovery: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_discovery: response: ", string(response)) + + var d map[string]map[string]interface{} + err = json.Unmarshal(response, &d) + if err != nil { + log.Error("oneM2M_discovery: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_discovery: d: ", d) + // Add additional entries + deviceResp, err = tm.oneM2M_deserialize(device, d) + if err != nil { + log.Error("oneM2M_discovery: ", err.Error()) + return deviceResp, err + } + // log.Debug("oneM2M_discovery: deviceResp: ", deviceResp) + return deviceResp, nil +} + +func (tm *IotMgr) oneM2M_get(device DeviceInfo, requestedIotPlatformId string, type_ string) (deviceResp DeviceInfo, err error) { + // FIXME FSCOM: requestedIotPlatformId should be useless + + ri := "" + for _, val := range device.DeviceMetadata { + if val.Key == "ri" { + ri = val.Value + break + } + } + if ri == "" { + err = errors.New("oneM2M_get: Cannot find \"ri\" value") + log.Error("oneM2M_get: ", err.Error()) + return deviceResp, err + } + + // 1. Get the list of the AE + // Build the URL + t := registeredIotPlatformsMap[requestedIotPlatformId].CustomServicesTransportInfo[0] + log.Debug("oneM2M_get: t.Endpoint.Addresses[0]=", t.Endpoint.Addresses[0]) + url := "http://" + t.Endpoint.Addresses[0].Host + ":" + strconv.Itoa(int(t.Endpoint.Addresses[0].Port)) + "/" + ri + log.Debug("oneM2M_get: url=", url) + // Build the headers + var headers = http.Header{} + headers["Accept"] = []string{headerAccept} + headers["Content-Type"] = []string{headerContentType} + headers["X-M2M-Origin"] = []string{"CAdmin"} // FIXME FSCOM How to get it + headers["X-M2M-RI"] = []string{uuid.New().String()} + headers["X-M2M-RVI"] = []string{t.Version} + // Build the queries + queries := map[string]string{} + if type_ == "AE" { + queries = nil + } else if type_ == "CN" { + queries["fu"] = "1" // Filter usage + queries["ty"] = "4" // Filter on oneM2M CIN for device + } else { + err = errors.New("oneM2M_get: Invalid type") + log.Error("oneM2M_get: ", err.Error()) + return deviceResp, err + } + // Send the request + response, err := sendRequest("GET", url, headers, nil, nil, queries, 200) + if err != nil { + log.Error("oneM2M_get: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_get: response: ", string(response)) + + var d map[string]map[string]interface{} + err = json.Unmarshal(response, &d) + if err != nil { + log.Error("oneM2M_get: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_get: d: ", d) + + // Add additional entries + deviceResp, err = tm.oneM2M_deserialize(device, d) + if err != nil { + log.Error("oneM2M_get: ", err.Error()) + return deviceResp, err + } + log.Debug("oneM2M_get: deviceResp: ", deviceResp) + return deviceResp, nil +} + +func (tm *IotMgr) oneM2M_delete(device DeviceInfo, requestedIotPlatformId string, type_ string) (err error) { + // FIXME FSCOM: requestedIotPlatformId should be useless + + ri := "" + for _, val := range device.DeviceMetadata { + if val.Key == "ri" { + ri = val.Value + break + } + } + if ri == "" { + err = errors.New("oneM2M_delete: Cannot find \"ri\" value") + log.Error("oneM2M_delete: ", err.Error()) + return err + } + + // Build the URL + t := registeredIotPlatformsMap[requestedIotPlatformId].CustomServicesTransportInfo[0] + log.Debug("oneM2M_delete: t.Endpoint.Addresses[0]=", t.Endpoint.Addresses[0]) + url := "http://" + t.Endpoint.Addresses[0].Host + ":" + strconv.Itoa(int(t.Endpoint.Addresses[0].Port)) + "/" + ri + log.Debug("oneM2M_delete: url=", url) + // Build the headers + var headers = http.Header{} + headers["Accept"] = []string{headerAccept} + headers["Content-Type"] = []string{headerContentType} + headers["X-M2M-Origin"] = []string{"C" + requestedIotPlatformId} // FIXME FSCOM How to get it + headers["X-M2M-RI"] = []string{uuid.New().String()} + headers["X-M2M-RI"] = []string{uuid.New().String()} + headers["X-M2M-RVI"] = []string{t.Version} + // Send the request + _, err = sendRequest("DELETE", url, headers, nil, nil, nil, 200) + if err != nil { + log.Error("oneM2M_delete: ", err.Error()) + return err + } + + return nil +} + +func (tm *IotMgr) oneM2M_deserialize(device DeviceInfo, response map[string]map[string]interface{}) (deviceResp DeviceInfo, err error) { + deviceResp = device // Same data type + + log.Debug("oneM2M_deserialize: type(response): ", reflect.TypeOf(response)) + log.Debug("oneM2M_deserialize: len(response): ", len(response)) + log.Debug("oneM2M_deserialize: response: ", response) + + if val, ok := response["m2m:ae"]; ok { + log.Debug("oneM2M_deserialize: val: ", val) + } else { + log.Error("oneM2M_deserialize: Key not found") + } + + for i, m := range response { + log.Debug("==> ", i, " value is ", m) + // m is a map[string]interface. + // loop over keys and values in the map. + for k, v := range m { + log.Debug(k, " value is ", v) + log.Debug("oneM2M_deserialize: type(v): ", reflect.TypeOf(v)) + if item, ok := v.(string); ok { + deviceResp.DeviceMetadata = append( + deviceResp.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: string(item), + }) + } else if item, ok := v.(float64); ok { + deviceResp.DeviceMetadata = append( + deviceResp.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: strconv.FormatFloat(item, 'f', -1, 64), + }) + } else if item, ok := v.(int64); ok { + deviceResp.DeviceMetadata = append( + deviceResp.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: strconv.FormatInt(item, 10), + }) + } else if item, ok := v.(bool); ok { + deviceResp.DeviceMetadata = append( + deviceResp.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: strconv.FormatBool(item), + }) + } else if item, ok := v.([]string); ok { + deviceResp.DeviceMetadata = append( + deviceResp.DeviceMetadata, + KeyValuePair{ + Key: k, + Value: strings.Join(item, ","), + }) + } else if item, ok := v.([]int64); ok { + log.Error("oneM2M_deserialize: Failed to convert list of int64 into string: ", item) + } else if _, ok := v.([]interface{}); ok { + log.Error("oneM2M_deserialize: Failed to convert []interface {} into string: ", v.([]interface{})) + } else { + log.Error("oneM2M_deserialize: Failed to process: ", k) + } + } // End of 'for' loop + + } // End of 'for' loop + + return deviceResp, nil +} + +func sendRequest(method string, url string, headers http.Header, body io.Reader, vars map[string]string, query map[string]string, code int) ([]byte, error) { + //log.Debug(">>> sendRequest: url: ", url) + //log.Debug(">>> sendRequest: headers: ", headers) + + req, err := http.NewRequest(method, url, body) + if err != nil || req == nil { + return nil, err + } + if vars != nil { + req = mux.SetURLVars(req, vars) + } + if query != nil { + q := req.URL.Query() + for k, v := range query { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + req.Header = headers + req.Close = true + + //log.Debug("sendRequest: req: ", req) + rr, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + // Check the status code is what we expect. + //log.Debug("sendRequest: rr: ", rr) + if status := rr.StatusCode; status != code { + s := fmt.Sprintf("Wrong status code - got %v want %v", status, code) + return nil, errors.New(s) + } + responseData, err := ioutil.ReadAll(rr.Body) + if err != nil { + return nil, err + } + //log.Debug("sendRequest: responseData: ", responseData) + + return responseData, nil +} diff --git a/go-packages/meep-sss-mgr/onem2m-mgr_test.go b/go-packages/meep-sss-mgr/onem2m-mgr_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fd19a4c466155ba3df8c380a5fc2b89000c34764 --- /dev/null +++ b/go-packages/meep-sss-mgr/onem2m-mgr_test.go @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2025 The AdvantEDGE Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on ance "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package onem2mmgr + +import ( + "fmt" + "testing" + + log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" +) + +const tmName = "meep-iot" +const tmNamespace = "sandboxtest" + +func TestNewIotMgr(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + + // Invalid Connector + fmt.Println("Invalid IOT Asset Manager") + tm, err := NewIotMgr("", tmNamespace) + if err == nil || tm != nil { + t.Fatalf("DB connection should have failed") + } + + // Valid Connector + fmt.Println("Create valid IOT Asset Manager") + tm, err = NewIotMgr(tmName, tmNamespace) + if err != nil || tm == nil { + t.Fatalf("Failed to create IOT Asset Manager") + } + + // Cleanup + err = tm.DeleteIotMgr() + if err != nil { + t.Fatalf("Failed to cleanup IOT Asset Manager") + } +} + +func TestRegisterIotPlatformInfo(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + + // Valid Connector + fmt.Println("Create valid IOT Asset Manager") + tm, err := NewIotMgr(tmName, tmNamespace) + if err != nil || tm == nil { + t.Fatalf("Failed to create IOT Asset Manager") + } + + // Set a valid platform + var adresses = []Addresses{} + adresses = append(adresses, Addresses{ + Host: "172.29.10.56", + Port: 1883, + }) + var endpoint = EndPointInfo{ + Addresses: adresses, + } + var userTransportInfo = []MbTransportInfo{} + userTransportInfo = append(userTransportInfo, MbTransportInfo{ + Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", + Name: "MQTT", + Description: "MQTT", + Protocol: "MQTT", + Version: "2", + Endpoint: &endpoint, + }) + var adresses_1 = []Addresses{} + adresses_1 = append(adresses_1, Addresses{ + Host: "172.29.10.20", + Port: 31110, + }) + var customServicesTransportInfo = []TransportInfo{} + var endPointInfo_1 = EndPointInfo{ + Addresses: adresses_1, + } + customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ + Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", + Name: "/laboai-acme-ic-cse", + Description: "ACME oneM2M CSE", + Protocol: "REST_HTTP", + Version: "4", + Endpoint: &endPointInfo_1, + }) + var iotPlatformInfo = IotPlatformInfo{ + IotPlatformId: "523f2df1-8927-429f-906c-56ba92d13762", + UserTransportInfo: userTransportInfo, + CustomServicesTransportInfo: customServicesTransportInfo, + Enabled: true, + } + fmt.Println("Create an IotPlatformInfo: ", iotPlatformInfo) + err = tm.RegisterIotPlatformInfo(iotPlatformInfo) + if err != nil { + t.Fatalf("Failed to register new IoT platform") + } + + // Cleanup + err = tm.DeregisterIotPlatformInfo(iotPlatformInfo.IotPlatformId) + if err != nil { + t.Fatalf("Failed to register new IoT platform") + } + err = tm.DeleteIotMgr() + if err != nil { + t.Fatalf("Failed to cleanup IOT Asset Manager") + } + + // t.Fatalf("DONE") +} + +func TestOneM2mCreateAE(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + + // Valid Connector + fmt.Println("Create valid IOT Asset Manager") + tm, err := NewIotMgr(tmName, tmNamespace) + if err != nil || tm == nil { + t.Fatalf("Failed to create IOT Asset Manager") + } + + // Set a valid platform + var adresses = []Addresses{} + adresses = append(adresses, Addresses{ + Host: "172.29.10.56", + Port: 1883, + }) + var endpoint = EndPointInfo{ + Addresses: adresses, + } + var userTransportInfo = []MbTransportInfo{} + userTransportInfo = append(userTransportInfo, MbTransportInfo{ + Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", + Name: "MQTT", + Description: "MQTT", + Protocol: "MQTT", + Version: "2", + Endpoint: &endpoint, + }) + var adresses_1 = []Addresses{} + adresses_1 = append(adresses_1, Addresses{ + Host: "172.29.10.20", + Port: 31110, + }) + var customServicesTransportInfo = []TransportInfo{} + var endPointInfo_1 = EndPointInfo{ + Addresses: adresses_1, + } + customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ + Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", + Name: "/laboai-acme-ic-cse", + Description: "ACME oneM2M CSE", + Protocol: "REST_HTTP", + Version: "4", + Endpoint: &endPointInfo_1, + }) + var iotPlatformInfo = IotPlatformInfo{ + IotPlatformId: "523f2df1-8927-429f-906c-56ba92d13762", + UserTransportInfo: userTransportInfo, + CustomServicesTransportInfo: customServicesTransportInfo, + Enabled: true, + } + // Create a new IotPlatformInfo + fmt.Println("Create a new IotPlatformInfo") + err = tm.RegisterIotPlatformInfo(iotPlatformInfo) + if err != nil { + t.Fatalf("Failed to register new IoT platform") + } + // Create an AE for this new IotPlatform + fmt.Println("Create an AE for this new IotPlatform") + requestedIotPlatformId := iotPlatformInfo.IotPlatformId + var device = DeviceInfo{ + RequestedIotPlatformId: requestedIotPlatformId, + DeviceId: requestedIotPlatformId, + Enabled: true, + } + deviceResp, err := tm.oneM2M_create(device, requestedIotPlatformId, "AE") + if err != nil { + t.Fatalf("oneM2M_create failure") + } + fmt.Println("deviceResp: ", deviceResp) + // Get the new IotPlatform entry + fmt.Println("Get the new IotPlatform entry") + deviceResp_1, err := tm.oneM2M_get(deviceResp, requestedIotPlatformId, "AE") + if err != nil { + t.Fatalf("oneM2M_get failure") + } + + // Check deviceResp vs. deviceResp_1 + if !validate_device_info(deviceResp, deviceResp_1) { + t.Fatalf("validate_device_info failure") + } + + // Delete the new IotPlatform entry + fmt.Println("Delete the new IotPlatform entry") + err = tm.oneM2M_delete(deviceResp, requestedIotPlatformId, "AE") + if err != nil { + t.Fatalf("oneM2M_create failure") + } + + // Cleanup + err = tm.DeleteIotMgr() + if err != nil { + t.Fatalf("Failed to cleanup IOT Asset Manager") + } + + // t.Fatalf("DONE") +} + +func TestOneM2mCreateAEAndCNT(t *testing.T) { + fmt.Println("--- ", t.Name()) + log.MeepTextLogInit(t.Name()) + + // Valid Connector + fmt.Println("Create valid IOT Asset Manager") + tm, err := NewIotMgr(tmName, tmNamespace) + if err != nil || tm == nil { + t.Fatalf("Failed to create IOT Asset Manager") + } + + // Register new IotPlatform and create AE entry + fmt.Println("Register new IotPlatform and create AE entry") + iotPlatformInfo, err := registerIotPltfAndCreateAE(tm) + if err != nil { + t.Fatalf("registerIotPltfAndCreateAE failure") + } + // Get the new IotPlatform and create AE entry + fmt.Println("Get the new IotPlatform and create AE entry") + deviceResp_1, err := tm.oneM2M_get(*iotPlatformInfo.DeviceInfo, iotPlatformInfo.IotPlatformId, "AE") + if err != nil { + t.Fatalf("oneM2M_get failure") + } + // Check deviceResp vs. deviceResp_1 + fmt.Println("Check deviceResp vs. deviceResp_1") + if !validate_device_info(*iotPlatformInfo.DeviceInfo, deviceResp_1) { + t.Fatalf("validate_device_info failure") + } + + // Create a device for the IotPlatform + var device = DeviceInfo{ + RequestedIotPlatformId: iotPlatformInfo.IotPlatformId, + DeviceId: "Device1", + Enabled: true, + //DeviceMetadata: [KeyValuePair{Key: "pi", Value: *iotPlatformInfo.DeviceInfo.DeviceMetadata[]}] + } + device, err = tm.oneM2M_create(device, iotPlatformInfo.IotPlatformId, "CNT") + if err != nil { + t.Fatalf("oneM2M_create failed to create a device") + } + fmt.Println("device: ", device) + + // Delete the new IotPlatform entry + fmt.Println("Delete the new IotPlatform entry") + err = tm.oneM2M_delete(*iotPlatformInfo.DeviceInfo, iotPlatformInfo.IotPlatformId, "AE") + if err != nil { + t.Fatalf("oneM2M_create failure") + } + + // Cleanup + err = tm.DeleteIotMgr() + if err != nil { + t.Fatalf("Failed to cleanup IOT Asset Manager") + } + + // t.Fatalf("DONE") +} + +func registerIotPltfAndCreateAE(tm *IotMgr) (iotPlatformInfo IotPlatformInfo, err error) { + + // Set a valid platform + var adresses = []Addresses{} + adresses = append(adresses, Addresses{ + Host: "172.29.10.56", + Port: 1883, + }) + var endpoint = EndPointInfo{ + Addresses: adresses, + } + var userTransportInfo = []MbTransportInfo{} + userTransportInfo = append(userTransportInfo, MbTransportInfo{ + Id: "d5673793-c55c-4969-b5bc-2121f84b9f8d", + Name: "MQTT", + Description: "MQTT", + Protocol: "MQTT", + Version: "2", + Endpoint: &endpoint, + }) + var adresses_1 = []Addresses{} + adresses_1 = append(adresses_1, Addresses{ + Host: "172.29.10.20", + Port: 31110, + }) + var customServicesTransportInfo = []TransportInfo{} + var endPointInfo_1 = EndPointInfo{ + Addresses: adresses_1, + } + customServicesTransportInfo = append(customServicesTransportInfo, TransportInfo{ + Id: "2ddb713c-2b41-4ded-a7ad-a5a047c5df13", + Name: "/laboai-acme-ic-cse", + Description: "ACME oneM2M CSE", + Protocol: "REST_HTTP", + Version: "4", + Endpoint: &endPointInfo_1, + }) + iotPlatformInfo = IotPlatformInfo{ + IotPlatformId: "523f2df1-8927-429f-906c-56ba92d13762", + UserTransportInfo: userTransportInfo, + CustomServicesTransportInfo: customServicesTransportInfo, + Enabled: true, + } + err = tm.RegisterIotPlatformInfo(iotPlatformInfo) + if err != nil { + return iotPlatformInfo, err + } + + // OneM2M create MEC pltf as an AE + requestedIotPlatformId := iotPlatformInfo.IotPlatformId + var device = DeviceInfo{ + RequestedIotPlatformId: requestedIotPlatformId, + DeviceId: requestedIotPlatformId, + Enabled: true, + } + d, err := tm.oneM2M_create(device, requestedIotPlatformId, "AE") + if err != nil { + return iotPlatformInfo, err + } + iotPlatformInfo.DeviceInfo = new(DeviceInfo) + *iotPlatformInfo.DeviceInfo = d + fmt.Println("iotPlatformInfo.DeviceInfo: ", *iotPlatformInfo.DeviceInfo) + + return iotPlatformInfo, nil +} + +func validate_device_info(expected_device DeviceInfo, received_deviceResp DeviceInfo) bool { + if expected_device.DeviceId != received_deviceResp.DeviceId { + fmt.Println(" received_deviceResp.DeviceId != DeviceId") + return false + } + if expected_device.Enabled != received_deviceResp.Enabled { + fmt.Println(" received_deviceResp.Enabled != Enabled") + return false + } + // TODO To be continued + + return true +}