diff --git a/go-apps/meep-app-enablement/server/app-support/app-support.go b/go-apps/meep-app-enablement/server/app-support/app-support.go index a67c0b5089fdaadf8b6353d1d4b568dd3e3b72cf..6b047a5bf8a1d8eb82fd6d320e8b5fb4a9b203cb 100644 --- a/go-apps/meep-app-enablement/server/app-support/app-support.go +++ b/go-apps/meep-app-enablement/server/app-support/app-support.go @@ -654,115 +654,144 @@ func timingCurrentTimeGET(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, string(jsonResponse)) } +// setupECSConfiguration stores the mandatory ECS configuration in Redis. +// It uses the hostUrl as the ECS address and returns an error if any step fails. func setupECSConfiguration() error { - // Mandatory ECS Configuration (only ECS Address) + // Create a map containing the mandatory ECS configuration ecsConfig := map[string]interface{}{ - "ECSAddress": hostUrl.String(), // MEC Sandbox URL as ECS Address + "ECSAddress": hostUrl.String(), // Use the MEC Sandbox URL as the ECS Address } - // Convert ECS Config to JSON + // Convert the ECS configuration to JSON ecsConfigJson, err := json.Marshal(ecsConfig) if err != nil { + log.Error("setupECSConfiguration: failed to marshal ECS configuration", "error", err.Error()) return fmt.Errorf("failed to marshal ECS configuration: %v", err) } - // Convert []byte to string before passing to JSONSetEntry + // Convert JSON bytes to a string for storage ecsConfigJsonStr := string(ecsConfigJson) - // Store in Redis + + // Define the Redis key and store the JSON configuration in Redis ecsKey := baseKey + "ecs:config" err = rc.JSONSetEntry(ecsKey, ".", ecsConfigJsonStr) if err != nil { + log.Error("setupECSConfiguration: failed to set ECS configuration in Redis", "error", err.Error()) return fmt.Errorf("failed to set ECS configuration in Redis: %v", err) } - log.Info("ECS configuration stored successfully") + log.Info("setupECSConfiguration: ECS configuration stored successfully in Redis", "key", ecsKey) return nil } +// getECSConfig retrieves the ECS configuration from Redis and returns it as a map. +// An error is returned if the configuration is missing or cannot be unmarshaled. func getECSConfig() (map[string]interface{}, error) { + // Define the Redis key for the ECS configuration ecsKey := baseKey + "ecs:config" ecsConfigJson, err := rc.JSONGetEntry(ecsKey, ".") if err != nil { + log.Error("getECSConfig: failed to get ECS configuration from Redis", "error", err.Error()) return nil, fmt.Errorf("failed to get ECS configuration: %v", err) } + // Unmarshal the JSON configuration into a map var ecsConfig map[string]interface{} err = json.Unmarshal([]byte(ecsConfigJson), &ecsConfig) if err != nil { + log.Error("getECSConfig: failed to unmarshal ECS configuration", "error", err.Error()) return nil, fmt.Errorf("failed to unmarshal ECS configuration: %v", err) } + log.Info("getECSConfig: successfully retrieved ECS configuration from Redis", "key", ecsKey) return ecsConfig, nil } +// getRegistration retrieves the EEC registration details for a given RegistrationId. +// It extracts the registrationId from the request URL, looks up the corresponding entry in Redis, +// and sends a JSON response with the registration information. func getRegistration(w http.ResponseWriter, r *http.Request) { - log.Info("Get EEC Registration by RegistrationId") + log.Info("getRegistration: Get EEC Registration by RegistrationId") w.Header().Set("Content-Type", "application/json; charset=UTF-8") + + // Extract the registrationId from the URL variables vars := mux.Vars(r) registrationId := vars["registrationId"] keyName := baseKey + "app:" + registrationId - // Find reginfo entry in redis DB + + // Retrieve the registration information from Redis eecPrevReg, err := rc.JSONGetEntry(keyName, ".") if err != nil { - err = errors.New("eecRegistration not found against the provided RegistrationId") - log.Error(err.Error()) - errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) + errMsg := "getRegistration: eecRegistration not found for the provided RegistrationId" + log.Error(errMsg, "registrationId", registrationId, "error", err.Error()) + errHandlerProblemDetails(w, errMsg, http.StatusNotFound) return } if eecPrevReg == "" { - log.Error("RegistrationId %s not found in Redis", registrationId) + log.Error("getRegistration: RegistrationId not found in Redis", "registrationId", registrationId) errHandlerProblemDetails(w, "Registration not found", http.StatusNotFound) return } + + // Convert the previously stored registration information to JSON format for the response sInfoJson := convertEecPrevRegReqInfoToJson(eecPrevReg) jsonResponse, err := json.Marshal(sInfoJson) if err != nil { - log.Error("Failed to marshal the response:", err.Error()) + log.Error("getRegistration: failed to marshal the response", "error", err.Error()) errHandlerProblemDetails(w, "Internal server error", http.StatusInternalServerError) return } - // Send the response + // Send the JSON response with a 200 OK status w.WriteHeader(http.StatusOK) w.Write(jsonResponse) + log.Info("getRegistration: successfully retrieved registration", "registrationId", registrationId) } +// updateRegistrationPut updates an existing EEC registration based on the RegistrationId provided in the URL. +// It decodes the update request, validates AcId fields, and updates the stored registration data in Redis. func updateRegistrationPut(w http.ResponseWriter, r *http.Request) { - log.Info("Update EEC Registration by RegistrationId") + log.Info("updateRegistrationPut: Update EEC Registration by RegistrationId") w.Header().Set("Content-Type", "application/json; charset=UTF-8") + + // Extract registrationId from URL vars := mux.Vars(r) registrationId := vars["registrationId"] + + // Decode the update request body into ecsRegUpdateReq var ecsRegUpdateReq RegistrationsRegistrationIdBody decoder := json.NewDecoder(r.Body) - err := decoder.Decode(&ecsRegUpdateReq) - if err != nil { - log.Error("Failed to decode the request body:", err.Error()) + if err := decoder.Decode(&ecsRegUpdateReq); err != nil { + log.Error("updateRegistrationPut: failed to decode the request body", "error", err.Error()) errHandlerProblemDetails(w, "Invalid request format", http.StatusBadRequest) return } + keyName := baseKey + "app:" + registrationId - // Find reginfo entry in redis DB + // Retrieve the current registration entry from Redis eecPrevReg, err := rc.JSONGetEntry(keyName, ".") if err != nil { - err = errors.New("eecRegistration not found against the provided RegistrationId") - log.Error(err.Error()) - errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) + errMsg := "updateRegistrationPut: eecRegistration not found for the provided RegistrationId" + log.Error(errMsg, "registrationId", registrationId, "error", err.Error()) + errHandlerProblemDetails(w, errMsg, http.StatusNotFound) return } if eecPrevReg == "" { - log.Error("RegistrationId %s not found in Redis", registrationId) + log.Error("updateRegistrationPut: RegistrationId not found in Redis", "registrationId", registrationId) errHandlerProblemDetails(w, "Registration not found", http.StatusNotFound) return } + + // Convert the current registration info to a modifiable JSON structure sInfoJson := convertEecPrevRegReqInfoToJson(eecPrevReg) - // Helper function to check if AcId is valid + // Helper function to check if an AcId is valid isValidAcId := func(acId string) bool { return acId != "" && acId != "string" } - // Check if either valid AcId or LocationInfo is provided + // Validate that at least one valid AcId is provided in the update request hasAcId := false for _, acProf := range ecsRegUpdateReq.AcProfs { if isValidAcId(acProf.AcId) { @@ -770,20 +799,23 @@ func updateRegistrationPut(w http.ResponseWriter, r *http.Request) { break } } - // Process AcId if provided + + // Process and validate each valid AcId in the update request if hasAcId { for _, acProf := range ecsRegUpdateReq.AcProfs { if isValidAcId(acProf.AcId) { appId := acProf.AcId + log.Debug("updateRegistrationPut: processing AcId", "appId", appId) appInfo, err := getAppInfo(appId) if err != nil { + log.Error("updateRegistrationPut: getAppInfo failed", "appId", appId, "error", err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) return } code, problemDetails, err := validateAppInfo(appInfo) if err != nil { - log.Error(err.Error()) + log.Error("updateRegistrationPut: validateAppInfo error", "appId", appId, "error", err.Error()) if problemDetails != "" { w.WriteHeader(code) fmt.Fprint(w, problemDetails) @@ -795,76 +827,90 @@ func updateRegistrationPut(w http.ResponseWriter, r *http.Request) { } } } + + // Update the registration JSON with new AcProfs, ExpTime, and UeMobilityReq values sInfoJson.AcProfs = ecsRegUpdateReq.AcProfs sInfoJson.ExpTime = ecsRegUpdateReq.ExpTime sInfoJson.UeMobilityReq = ecsRegUpdateReq.UeMobilityReq + // Convert the updated registration structure back to JSON for storage sInfoJson_ := convertEecRegReqInfoToJson(sInfoJson) - key := baseKey + "app:" + registrationId - err = rc.JSONSetEntry(key, ".", sInfoJson_) + err = rc.JSONSetEntry(keyName, ".", sInfoJson_) if err != nil { - log.Error("Failed to set json entry in redis db") - errHandlerProblemDetails(w, "Failed to set json entry in redis db", http.StatusInternalServerError) + log.Error("updateRegistrationPut: failed to set JSON entry in Redis DB", "registrationId", registrationId, "error", err.Error()) + errHandlerProblemDetails(w, "Failed to set JSON entry in Redis DB", http.StatusInternalServerError) return } + // Prepare and send a response indicating a successful update response := InlineResponse201{ ExpirationTime: time.Now(), } - jsonResponse, err := json.Marshal(response) if err != nil { - log.Error("Failed to marshal the response:", err.Error()) + log.Error("updateRegistrationPut: failed to marshal the response", "error", err.Error()) errHandlerProblemDetails(w, "Internal server error", http.StatusInternalServerError) return } - // Send the response w.WriteHeader(http.StatusOK) w.Write(jsonResponse) + log.Info("updateRegistrationPut: registration updated successfully", "registrationId", registrationId) } +// createEECReg creates a new EEC registration. +// It validates the incoming request, verifies that the request is stored in Redis, +// validates AcId fields, and finally stores the new registration while returning the registration details. func createEECReg(w http.ResponseWriter, r *http.Request) { - log.Info("requestServProv") + log.Info("createEECReg: Request to create EEC Registration") w.Header().Set("Content-Type", "application/json; charset=UTF-8") mutex.Lock() defer mutex.Unlock() + // Check if the request body is provided if r.Body == nil { - err := errors.New("Request body is missing") + err := errors.New("createEECReg: request body is missing") + log.Error(err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) return } + + // Decode the request body into an EecRegistration structure var ecsRegReq EecRegistration decoder := json.NewDecoder(r.Body) - err := decoder.Decode(&ecsRegReq) - if err != nil { - log.Error("Failed to decode the request body:", err.Error()) + if err := decoder.Decode(&ecsRegReq); err != nil { + log.Error("createEECReg: failed to decode the request body", "error", err.Error()) errHandlerProblemDetails(w, "Invalid request format", http.StatusBadRequest) return } + + // Validate that a unique EecId is provided if ecsRegReq.EecId == "" || ecsRegReq.EecId == "string" { - log.Error("Invalid request: Please enter the unique EecId") + log.Error("createEECReg: invalid request - unique EecId missing or default value provided") errHandlerProblemDetails(w, "Please enter the unique EecId", http.StatusBadRequest) return } + + // Verify that the registration request is already stored in Redis key := baseKey + "app:" + ecsRegReq.EecId sInfoJson1, err := rc.JSONGetEntry(key, ".") if err != nil { - log.Error("Failed to get json entry from redis db") - errHandlerProblemDetails(w, "Failed to get json entry from redis db", http.StatusInternalServerError) + log.Error("createEECReg: failed to get JSON entry from Redis DB", "error", err.Error()) + errHandlerProblemDetails(w, "Failed to get JSON entry from Redis DB", http.StatusInternalServerError) return } if sInfoJson1 == "" { - errHandlerProblemDetails(w, "Request is not stored in redis db", http.StatusInternalServerError) + log.Error("createEECReg: registration request is not stored in Redis", "EecId", ecsRegReq.EecId) + errHandlerProblemDetails(w, "Request is not stored in Redis DB", http.StatusInternalServerError) return } - // Helper function to check if AcId is valid + + // Helper function to check if an AcId is valid isValidAcId := func(acId string) bool { return acId != "" && acId != "string" } - // Check if either valid AcId or LocationInfo is provided + // Ensure that at least one valid AcId is provided in the registration request hasAcId := false for _, acProf := range ecsRegReq.AcProfs { if isValidAcId(acProf.AcId) { @@ -872,20 +918,23 @@ func createEECReg(w http.ResponseWriter, r *http.Request) { break } } - // Process AcId if provided + + // Validate each valid AcId by retrieving and validating associated application info if hasAcId { for _, acProf := range ecsRegReq.AcProfs { if isValidAcId(acProf.AcId) { appId := acProf.AcId + log.Debug("createEECReg: processing AcId", "appId", appId) appInfo, err := getAppInfo(appId) if err != nil { + log.Error("createEECReg: getAppInfo failed", "appId", appId, "error", err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) return } code, problemDetails, err := validateAppInfo(appInfo) if err != nil { - log.Error(err.Error()) + log.Error("createEECReg: validateAppInfo error", "appId", appId, "error", err.Error()) if problemDetails != "" { w.WriteHeader(code) fmt.Fprint(w, problemDetails) @@ -898,116 +947,137 @@ func createEECReg(w http.ResponseWriter, r *http.Request) { } } - // Get platform details dynamically and prepare response + // Dynamically retrieve platform details to verify the registration endpoint and MEC platform ID ednConfig, err := getDynamicPlatformDetails(basePath) if err != nil { - errHandlerProblemDetails(w, "Failed to get the ednconfig information that contain MEC plateform information", http.StatusBadRequest) + log.Error("createEECReg: failed to get EDN config information", "error", err.Error()) + errHandlerProblemDetails(w, "Failed to get the EDN config information", http.StatusBadRequest) return } - // Compare EEC registration endpoint with EDN config endpoint + + // Validate that the registration endpoint and source EES ID match the expected EDN config values if ecsRegReq.EndPt == nil || ecsRegReq.EndPt.Uri != ednConfig.Eess[0].EndPt.Uri { - log.Error("Endpoint mismatch: EEC registration endpoint does not match EDN config endpoint") + log.Error("createEECReg: endpoint mismatch - EEC registration endpoint does not match EDN config endpoint", + "provided", ecsRegReq.EndPt, "expected", ednConfig.Eess[0].EndPt) errHandlerProblemDetails(w, "Endpoint mismatch: EEC registration endpoint does not match EDN config endpoint", http.StatusBadRequest) return } if ecsRegReq.EndPt == nil || ecsRegReq.SrcEesId != ednConfig.Eess[0].EesId { - log.Error("Endpoint mismatch: SrcEesId does not match EDN config MEC Plateform ID") - errHandlerProblemDetails(w, "Endpoint mismatch: SrcEesId does not match EDN config MEC Plateform ID", http.StatusBadRequest) + log.Error("createEECReg: endpoint mismatch - SrcEesId does not match EDN config MEC Platform ID", + "provided", ecsRegReq.SrcEesId, "expected", ednConfig.Eess[0].EesId) + errHandlerProblemDetails(w, "Endpoint mismatch: SrcEesId does not match EDN config MEC Platform ID", http.StatusBadRequest) return } + + // Generate a new unique registrationId for the new registration registrationId := uuid.New().String() sInfoJson := convertEecRegReqInfoToJson(&ecsRegReq) key = baseKey + "app:" + registrationId err = rc.JSONSetEntry(key, ".", sInfoJson) if err != nil { - log.Error("Failed to set json entry in redis db") - errHandlerProblemDetails(w, "Failed to set json entry in redis db", http.StatusInternalServerError) + log.Error("createEECReg: failed to set JSON entry in Redis DB", "registrationId", registrationId, "error", err.Error()) + errHandlerProblemDetails(w, "Failed to set JSON entry in Redis DB", http.StatusInternalServerError) return } - // if ecsRegReq.EndPt == nil || ecsRegReq.SrcEesId != + // Prepare the response with registration details response := InlineResponse201{ RegistrationID: registrationId, ExpirationTime: time.Now(), - EECContextID: "example-context-id", + EECContextID: "example-context-id", // Replace with actual context ID if available EECContextRelocationStatus: true, - DiscoveredEASList: []EasProfile{}, // Add appropriate EasProfile values if needed + DiscoveredEASList: []EasProfile{}, // Populate with actual EasProfile values if needed } jsonResponse, err := json.Marshal(response) if err != nil { - log.Error("Failed to marshal the response:", err.Error()) + log.Error("createEECReg: failed to marshal the response", "error", err.Error()) errHandlerProblemDetails(w, "Internal server error", http.StatusInternalServerError) return } - // Send the response + // Send the successful creation response w.WriteHeader(http.StatusOK) w.Write(jsonResponse) + log.Info("createEECReg: registration created successfully", "registrationId", registrationId) } +// deleteIndEECReg deletes an individual EEC registration identified by RegistrationId. +// It removes the corresponding entry from Redis and returns a 204 No Content response on success. func deleteIndEECReg(w http.ResponseWriter, r *http.Request) { - log.Info("Delete EEC Registration by RegistrationId") - + log.Info("deleteIndEECReg: Delete EEC Registration by RegistrationId") w.Header().Set("Content-Type", "application/json; charset=UTF-8") + + // Extract registrationId from URL vars := mux.Vars(r) registrationId := vars["registrationId"] - keyName := baseKey + "app:" + registrationId - // Find appInfo entry in redis DB + // Check if the registration exists in Redis _, err := rc.JSONGetEntry(keyName, ".") if err != nil { - err = errors.New("eecRegistration not found against the provided RegistrationId") - log.Error(err.Error()) - errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) + errMsg := "deleteIndEECReg: eecRegistration not found for the provided RegistrationId" + log.Error(errMsg, "registrationId", registrationId, "error", err.Error()) + errHandlerProblemDetails(w, errMsg, http.StatusNotFound) return } - // Delete appInfo entry from redis DB + // Delete the registration entry from Redis err = rc.JSONDelEntry(keyName, ".") if err != nil { - log.Error(err.Error()) + log.Error("deleteIndEECReg: failed to delete JSON entry from Redis DB", "registrationId", registrationId, "error", err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) return } - // Send response on successful deletion of registration + log.Info("deleteIndEECReg: registration deleted successfully", "registrationId", registrationId) + // Send a 204 No Content response on successful deletion w.WriteHeader(http.StatusNoContent) } +// requestServProv handles requests for service provisioning. It validates the request payload, +// ensures proper synchronization, checks required fields, and finally stores the request in Redis +// before sending the platform configuration as a response. func requestServProv(w http.ResponseWriter, r *http.Request) { - log.Info("requestServProv") + log.Info("requestServProv: Received request to provide service") + + // Set response header to JSON w.Header().Set("Content-Type", "application/json; charset=UTF-8") + + // Ensure thread safety when accessing shared resources mutex.Lock() defer mutex.Unlock() + // Check if the request body is present if r.Body == nil { - err := errors.New("Request body is missing") + err := errors.New("requestServProv: request body is missing") + log.Error(err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) return } + // Decode the incoming JSON request into an EcsServProvReq structure var ecsServReq EcsServProvReq decoder := json.NewDecoder(r.Body) - err := decoder.Decode(&ecsServReq) - if err != nil { - log.Error("Failed to decode the request body:", err.Error()) + if err := decoder.Decode(&ecsServReq); err != nil { + log.Error("requestServProv: failed to decode request body", "error", err.Error()) errHandlerProblemDetails(w, "Invalid request format", http.StatusBadRequest) return } + + // Validate that the EecId is provided and is not a placeholder value if ecsServReq.EecId == "" || ecsServReq.EecId == "string" { - log.Error("Invalid request: Please enter the unique EecId") + log.Error("requestServProv: invalid request - unique EecId missing or default value provided") errHandlerProblemDetails(w, "Please enter the unique EecId", http.StatusBadRequest) return } - // Helper function to check if AcId is valid + // Helper function to check if a provided AcId is valid (i.e. non-empty and not the default "string") isValidAcId := func(acId string) bool { return acId != "" && acId != "string" } - // Check if either valid AcId or LocationInfo is provided + // Verify that either a valid AcId is provided within AcProfs or LocationInfo is present. hasAcId := false for _, acProf := range ecsServReq.AcProfs { if isValidAcId(acProf.AcId) { @@ -1015,27 +1085,30 @@ func requestServProv(w http.ResponseWriter, r *http.Request) { break } } - if !hasAcId && ecsServReq.LocInf == nil { - log.Error("Invalid request: Both AcId and LocationInfo are missing") + log.Error("requestServProv: invalid request - both AcId and LocationInfo are missing") errHandlerProblemDetails(w, "Either a valid AcId or LocationInfo must be provided", http.StatusBadRequest) return } - // Process AcId if provided + // Process each valid AcId in the AcProfs slice if hasAcId { for _, acProf := range ecsServReq.AcProfs { if isValidAcId(acProf.AcId) { appId := acProf.AcId + log.Debug("requestServProv: processing AcId", "appId", appId) + // Retrieve application info for the given appId appInfo, err := getAppInfo(appId) if err != nil { + log.Error("requestServProv: getAppInfo failed", "appId", appId, "error", err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) return } + // Validate the retrieved appInfo; if invalid, send an appropriate response. code, problemDetails, err := validateAppInfo(appInfo) if err != nil { - log.Error(err.Error()) + log.Error("requestServProv: validateAppInfo error", "appId", appId, "error", err.Error()) if problemDetails != "" { w.WriteHeader(code) fmt.Fprint(w, problemDetails) @@ -1052,60 +1125,73 @@ func requestServProv(w http.ResponseWriter, r *http.Request) { if ecsServReq.LocInf != nil { lat := ecsServReq.LocInf.GeographicArea.Point.Point.Lat lon := ecsServReq.LocInf.GeographicArea.Point.Point.Lon - log.Info("Received coordinates: Lat:", lat, " Lon:", lon) + log.Info("requestServProv: received coordinates", "latitude", lat, "longitude", lon) + // Validate the geographic coordinates to ensure they are within acceptable boundaries. if !isValidCoordinates(lat, lon) { - log.Error("Invalid location: MEC platform not found for this location") + log.Error("requestServProv: invalid location - MEC platform not found for provided coordinates", + "latitude", lat, "longitude", lon) errHandlerProblemDetails(w, "MEC platform not found for this location", http.StatusNotFound) return } } + // Convert the ECS service provisioning request info into JSON format for storage. sInfoJson := convertEcsServProvReqInfoToJson(&ecsServReq) key := baseKey + "app:" + ecsServReq.EecId - err = rc.JSONSetEntry(key, ".", sInfoJson) - if err != nil { - log.Error("Failed to set json entry in redis db") - errHandlerProblemDetails(w, "Failed to set json entry in redis db", http.StatusInternalServerError) + + // Store the request in Redis DB. + if err := rc.JSONSetEntry(key, ".", sInfoJson); err != nil { + log.Error("requestServProv: failed to set JSON entry in Redis DB", "error", err.Error()) + errHandlerProblemDetails(w, "Failed to set JSON entry in Redis DB", http.StatusInternalServerError) return } + + // Verify that the entry was successfully stored by retrieving it. sInfoJson1, err := rc.JSONGetEntry(key, ".") if err != nil { - log.Error("Failed to get json entry from redis db") - errHandlerProblemDetails(w, "Failed to get json entry from redis db", http.StatusInternalServerError) + log.Error("requestServProv: failed to get JSON entry from Redis DB", "error", err.Error()) + errHandlerProblemDetails(w, "Failed to get JSON entry from Redis DB", http.StatusInternalServerError) return } if sInfoJson1 == "" { - errHandlerProblemDetails(w, "Request is not stored in redis db", http.StatusInternalServerError) + log.Error("requestServProv: request not stored in Redis DB", "key", key) + errHandlerProblemDetails(w, "Request is not stored in Redis DB", http.StatusInternalServerError) return } - // Get platform details dynamically and prepare response + // Dynamically retrieve platform configuration details using the basePath. ednConfig, err := getDynamicPlatformDetails(basePath) if err != nil { + log.Error("requestServProv: failed to get dynamic platform details", "error", err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) return } - // Send the response + // Marshal the platform configuration to JSON for the response. jsonResponse, err := json.Marshal(ednConfig) if err != nil { - log.Error("Failed to marshal the response:", err.Error()) + log.Error("requestServProv: failed to marshal response", "error", err.Error()) errHandlerProblemDetails(w, "Internal server error", http.StatusInternalServerError) return } + // Send the successful response with HTTP 200 OK. w.WriteHeader(http.StatusOK) w.Write(jsonResponse) + log.Info("requestServProv: successfully processed service provisioning request", "EecId", ecsServReq.EecId) } -// Function to get dynamic platform details +// getDynamicPlatformDetails extracts platform configuration details dynamically based on the provided basePath. +// It returns an EdnConfigInfo structure containing the list of EES (Edge Services) configurations or an error if any occurs. func getDynamicPlatformDetails(basePath string) (*EdnConfigInfo, error) { platformDetails, err := getPlatformDetailsFromBasePath(basePath) if err != nil { + log.Error("getDynamicPlatformDetails: error retrieving platform details from basePath", "error", err) return nil, err } + // Build EES information from the retrieved platform details. eesInfo := EesInfo{ EesId: platformDetails.EesId, EndPt: platformDetails.EndPt, @@ -1116,20 +1202,28 @@ func getDynamicPlatformDetails(basePath string) (*EdnConfigInfo, error) { }, nil } -// Function to validate coordinates +// isValidCoordinates checks whether the given latitude and longitude fall within the expected geographic boundaries, +// allowing a small tolerance for minor discrepancies. func isValidCoordinates(lat, lon float64) bool { const tolerance = 0.0001 - return lat >= (43.7244-tolerance) && lat <= (43.7515+tolerance) && lon >= (7.4090-tolerance) && lon <= (7.4390+tolerance) + return lat >= (43.7244-tolerance) && lat <= (43.7515+tolerance) && + lon >= (7.4090-tolerance) && lon <= (7.4390+tolerance) } -// Function to extract platform details dynamically from basePath +// getPlatformDetailsFromBasePath parses the basePath to extract the platform identifier and constructs the corresponding URL. +// It returns an EesInfo structure containing the platform's EES identifier and endpoint or an error if the parsing fails. func getPlatformDetailsFromBasePath(basePath string) (*EesInfo, error) { + // Locate the "/mep" segment in the path. This marks the beginning of the platform-specific portion. mepIndex := strings.Index(basePath, "/mep") if mepIndex == -1 { - return nil, errors.New("Invalid base path: /mep not found") + err := errors.New("getPlatformDetailsFromBasePath: invalid base path, '/mep' not found") + log.Error(err.Error()) + return nil, err } + // The namespace is the part of the basePath before "/mep". namespace := basePath[:mepIndex] + // Extract the platform-specific segment after "/mep". platformPart := basePath[mepIndex+1:] nextSlashIndex := strings.Index(platformPart, "/") var platformIdentifier string @@ -1139,7 +1233,11 @@ func getPlatformDetailsFromBasePath(basePath string) (*EesInfo, error) { platformIdentifier = platformPart } - mecPlatformUrl := strings.TrimSuffix(hostUrl.String(), "/") + "/" + strings.TrimPrefix(namespace, "/") + "/" + platformIdentifier + // Construct the full URL for the MEC platform by combining hostUrl with the namespace and platform identifier. + mecPlatformUrl := strings.TrimSuffix(hostUrl.String(), "/") + "/" + + strings.TrimPrefix(namespace, "/") + "/" + platformIdentifier + + log.Debug("getPlatformDetailsFromBasePath: constructed MEC platform URL", "url", mecPlatformUrl) return &EesInfo{ EesId: platformIdentifier, @@ -1149,28 +1247,31 @@ func getPlatformDetailsFromBasePath(basePath string) (*EesInfo, error) { }, nil } -///////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////// - +// getEASDiscInfo handles HTTP requests to retrieve EAS (Edge Application Service) discovery information. +// It validates the request, retrieves application configuration from the datastore, and returns a JSON response. func getEASDiscInfo(w http.ResponseWriter, r *http.Request) { - log.Debug(">>> getEASDiscInfo:", r) + log.Debug("getEASDiscInfo: request received", "method", r.Method, "url", r.URL) + + // Set the response content type to JSON. w.Header().Set("Content-Type", "application/json; charset=UTF-8") + + // Decode the incoming JSON request into an EasDiscoveryReq structure. var discReq EasDiscoveryReq if err := json.NewDecoder(r.Body).Decode(&discReq); err != nil { - log.Error("Error decoding request body:", err) + log.Error("getEASDiscInfo: error decoding request body", "error", err) errHandlerProblemDetails(w, "Invalid request body.", http.StatusBadRequest) return } - log.Info("getEASDiscInfo: Received reqInfo:", discReq) - // Immediately after decoding the request body + log.Info("getEASDiscInfo: decoded request info", "discReq", discReq) + + // Immediately reject requests that include an unsupported EasId. if discReq.RequestorId.EasId != "" { - log.Error("EasId support is currently disabled") + log.Error("getEASDiscInfo: unsupported EasId provided", "EasId", discReq.RequestorId.EasId) errHandlerProblemDetails(w, "EasId is not supported in this implementation", http.StatusBadRequest) return } - // Enforce oneOf between EecId and EesId + // Validate that exactly one of EecId or EesId is provided in the request. idCount := 0 if discReq.RequestorId.EecId != "" { idCount++ @@ -1178,84 +1279,86 @@ func getEASDiscInfo(w http.ResponseWriter, r *http.Request) { if discReq.RequestorId.EesId != "" { idCount++ } - if idCount != 1 { - log.Error("Request must contain exactly one identifier (EecId or EesId)") + log.Error("getEASDiscInfo: request validation failed - exactly one identifier required", "EecId", discReq.RequestorId.EecId, "EesId", discReq.RequestorId.EesId) errHandlerProblemDetails(w, "Exactly one of eecId or eesId must be provided", http.StatusBadRequest) return } - // Existing EecId validation + // Process EecId if provided. if discReq.RequestorId.EecId != "" { key := baseKey + "app:" + discReq.RequestorId.EecId sInfoJson1, err := rc.JSONGetEntry(key, ".") if err != nil { - log.Error("Invalid EecId: ", discReq.RequestorId.EecId) - errHandlerProblemDetails(w, "Invalid EecId - not generated through serviceProvisioning", http.StatusBadRequest) + log.Error("getEASDiscInfo: invalid EecId", "EecId", discReq.RequestorId.EecId, "error", err) + errHandlerProblemDetails(w, "Invalid EecId", http.StatusBadRequest) return } if sInfoJson1 == "" { + log.Error("getEASDiscInfo: no data found for provided EecId", "EecId", discReq.RequestorId.EecId) errHandlerProblemDetails(w, "No data found for EecId", http.StatusNotFound) return } } else { - // EesId validation + // Validate the provided EesId against the dynamically generated platform configuration. ednConfig, err := getDynamicPlatformDetails(basePath) if err != nil { + log.Error("getEASDiscInfo: error retrieving dynamic platform details", "error", err) errHandlerProblemDetails(w, "Platform configuration error: "+err.Error(), http.StatusInternalServerError) return } if len(ednConfig.Eess) == 0 || ednConfig.Eess[0].EesId == "" { - log.Error("Missing EES configuration") + log.Error("getEASDiscInfo: missing EES configuration in server settings") errHandlerProblemDetails(w, "Server configuration error", http.StatusInternalServerError) return } expectedEesId := ednConfig.Eess[0].EesId if discReq.RequestorId.EesId != expectedEesId { - log.Error("EesId mismatch") + log.Error("getEASDiscInfo: provided EesId does not match expected configuration", "providedEesId", discReq.RequestorId.EesId, "expectedEesId", expectedEesId) errHandlerProblemDetails(w, "Invalid EesId", http.StatusBadRequest) return } } - // Initialize appInstanceId - var appInstanceId string - // Check if EasDiscoveryFilter and AcChars exist and are not empty + // Retrieve the appInstanceId from the first AcCharacteristics entry if present. + var appInstanceId string if discReq.EasDiscoveryFilter != nil && len(discReq.EasDiscoveryFilter.AcChars) > 0 { - // Take the first AcCharacteristics entry acChar := discReq.EasDiscoveryFilter.AcChars[0] if acChar.AcProf != nil { appInstanceId = acChar.AcProf.AcId } } - // Check if appInstanceId was successfully retrieved + // Ensure that appInstanceId is present in the request. if appInstanceId == "" { - err := errors.New("acId not found in the request") + err := errors.New("getEASDiscInfo: acId not found in the request") log.Error(err.Error()) errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest) return } + + // Construct the datastore key for retrieving app information. keyName := baseKey + "appInfo:" + appInstanceId - log.Info("appRegistrationGET: keyName: ", keyName) + log.Info("getEASDiscInfo: retrieving app information", "keyName", keyName) jsonAppInfo, err := rc.JSONGetEntry(keyName, ".") if err != nil { - err = errors.New("appInfo not found against the provided appInstanceId") - log.Error(err.Error()) - errHandlerProblemDetails(w, err.Error(), http.StatusNotFound) + errMsg := "appInfo not found for the provided appInstanceId" + log.Error("getEASDiscInfo:", errMsg, "appInstanceId", appInstanceId, "error", err) + errHandlerProblemDetails(w, errMsg, http.StatusNotFound) return } - // Unmarshal retrieved JSON into AppInfo struct + + // Unmarshal the retrieved JSON data into the AppInfo structure. var appInfo AppInfo if err := json.Unmarshal([]byte(jsonAppInfo), &appInfo); err != nil { - log.Error("Error unmarshaling appInfo:", err) + log.Error("getEASDiscInfo: error unmarshaling appInfo", "error", err) errHandlerProblemDetails(w, "Internal server error.", http.StatusInternalServerError) return } - // Map AppInfo attributes to EASProfile response format - // Map to EasProfile + + // Map the AppInfo data to the EASProfile response format. easProfile := EasProfile{ EasId: appInfo.AppName, EndPt: appInfo.Endpoint, @@ -1267,7 +1370,8 @@ func getEASDiscInfo(w http.ResponseWriter, r *http.Request) { PermLvl: appInfo.PermLvl, AcIds: []string{appInfo.AppInstanceId}, } - // Construct response + + // Build the full discovery response with the mapped EAS profile. resp := EasDiscoveryResp{ DiscoveredEas: []DiscoveredEas{ { @@ -1275,9 +1379,11 @@ func getEASDiscInfo(w http.ResponseWriter, r *http.Request) { }, }, } - // Send JSON response with status 201 Created + + // Convert the response to JSON and send it with an HTTP 200 OK status. jsonResponse := convertEasDiscoveryRespToJson(&resp) w.WriteHeader(http.StatusOK) + log.Info("getEASDiscInfo: successfully processed request", "appInstanceId", appInstanceId) fmt.Fprint(w, jsonResponse) }