Commit 3b84d8af authored by Mudassar Khan's avatar Mudassar Khan
Browse files

Merge branch 'STF_685_Sandbox_Enhancements' into 'TemporaryMergeToMaster'

STF_685_Sandbox_Enhancements into TemporaryMergeToMaster

See merge request !13
parents 4b42e5de 4f041623
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
package mec

import (
	"fmt"
	"net/http"
	model "estimed_demo/Model"
	utils "estimed_demo/utils"
	"io"
	"encoding/json"
)

// Get MEC APP instances
func GetAppInstances(url string) ([]model.AppInstanceID, error) {
	resp, err := utils.SendMECRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get MEC app instances: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to get MEC App Instance ID: received status code %d", resp.StatusCode)
	}

	// From the response body, extract all App Instance IDs
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %v", err)
	}
	//log.Println("Response body:", string(body))

	var allAppInstances []model.AppInstanceID
	if err := json.Unmarshal(body, &allAppInstances); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response body: %v", err)
	}
	return allAppInstances, nil
}
 No newline at end of file
+337 −0
Original line number Diff line number Diff line
package mec

import (
	"encoding/json"
	model "estimed_demo/Model"
	utils "estimed_demo/utils"
	"fmt"
	"log"
	"net/http"
	"regexp"
)

// Create MEC AMS service registration
func CreateAMSRegistration(url string, app_instance_id string, device_info *model.RegistrationInfoDeviceInformation) (*model.RegistrationInfo, error) {
	payload := &model.RegistrationInfo{}
	if device_info != nil {
		payload = &model.RegistrationInfo{
			ServiceConsumerId: &model.RegistrationInfoServiceConsumerId{
				AppInstanceId: app_instance_id,
			},
			DeviceInformation: []model.RegistrationInfoDeviceInformation{*device_info},
		}
	} else {
		payload = &model.RegistrationInfo{
			ServiceConsumerId: &model.RegistrationInfoServiceConsumerId{
				AppInstanceId: app_instance_id,
			},
		}
	}
	resp, err := utils.SendMECRequest("POST", url, payload)
	if err != nil {
		return nil, fmt.Errorf("failed to create AMS registration: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusCreated {
		return nil, fmt.Errorf("failed to create AMS registration: received status code %d", resp.StatusCode)
	}

	var registrationInfo model.RegistrationInfo
	if err := json.NewDecoder(resp.Body).Decode(&registrationInfo); err != nil {
		return nil, fmt.Errorf("failed to decode AMS registration response: %v", err)
	}

	return &registrationInfo, nil
}

// Create MEC AMS subscription

func CreateAMSSubscription(url string, app_instance_id string, callbackURL string, associateId *model.AssociateId) (*model.InlineSubscription, error) {
	payload := &model.InlineSubscription{}
	if associateId != nil {
		payload = &model.InlineSubscription{
			SubscriptionType: "MobilityProcedureSubscription",
			FilterCriteria: &model.MobilityProcedureSubscriptionFilterCriteria{
				AppInstanceId: app_instance_id,
				AssociateId:   []model.AssociateId{*associateId},
			},
			CallbackReference: callbackURL,
		}
	} else {
		payload = &model.InlineSubscription{
			SubscriptionType: "MobilityProcedureSubscription",
			FilterCriteria: &model.MobilityProcedureSubscriptionFilterCriteria{
				AppInstanceId: app_instance_id,
			},
			// TODO: Update callback reference URL as needed
			CallbackReference: callbackURL,
		}
	}

	resp, err := utils.SendMECRequest("POST", url, payload)
	if err != nil {
		return nil, fmt.Errorf("failed to create AMS subscription: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusCreated {
		return nil, fmt.Errorf("failed to create AMS subscription: received status code %d", resp.StatusCode)
	}

	var subscriptionInfo model.InlineSubscription
	if err := json.NewDecoder(resp.Body).Decode(&subscriptionInfo); err != nil {
		return nil, fmt.Errorf("failed to decode AMS subscription response: %v", err)
	}

	return &subscriptionInfo, nil
}

// AMSNotificationHandler handles incoming AMS notifications
func AMSNotificationHandler(w http.ResponseWriter, r *http.Request) (string, error) {
	if r.Method != http.MethodPost {
		http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
		return "", fmt.Errorf("invalid request method")
	}

	var notification map[string]interface{}
	err := json.NewDecoder(r.Body).Decode(&notification)
	if err != nil {
		http.Error(w, "Failed to decode request body", http.StatusBadRequest)
		return "", fmt.Errorf("failed to decode request body: %w", err)
	}
	log.Printf("Received AMS notification: %+v", notification)
	w.WriteHeader(http.StatusNoContent)
	// Extract target app instance ID from the notification
	var targetAppInstanceID string
	if targetAppInfo, ok := notification["targetAppInfo"].(map[string]interface{}); ok {
		if appInstanceID, ok := targetAppInfo["appInstanceId"].(string); ok {
			targetAppInstanceID = appInstanceID
			log.Printf("Target App Instance ID: %s", targetAppInstanceID)
		}
	}
	if targetAppInstanceID == "" {
		log.Println("Target App Instance ID not found in notification")
	}
	return targetAppInstanceID, nil
}

func RegistrationAndSubscriptionHandler(matchingAppInstances []model.AppInstanceID, platformURL string,
	sandbox_name string, callbackURL string, currentAppInstanceId *string,
	deviceInfo *model.RegistrationInfoDeviceInformation, associateId *model.AssociateId) ([]map[string]model.PlatformInfo, error) {
	var platformDetails []map[string]model.PlatformInfo

	for i, appInstance := range matchingAppInstances {
		log.Printf("Creating AMS registration for App Instance ID: %s, Name: %s", appInstance.ID, appInstance.Name)
		amsURL := fmt.Sprintf("%s/%s/mep1/amsi/v1/app_mobility_services", platformURL, sandbox_name)
		var resp *model.RegistrationInfo
		var err error
		if i == 0 {
			*currentAppInstanceId = appInstance.ID
			resp, err = CreateAMSRegistration(amsURL, appInstance.ID, deviceInfo)
			if err != nil {
				return nil, fmt.Errorf("failed to create AMS registration: %v", err)
			} else {
				log.Printf("AMS registration created successfully: %v", resp)
			}
		} else {
			resp, err = CreateAMSRegistration(amsURL, appInstance.ID, nil)
			if err != nil {
				return nil, fmt.Errorf("failed to create AMS registration: %v", err)
			} else {
				log.Printf("AMS registration created successfully: %v", resp)
			}
		}
		amsSubURL := fmt.Sprintf("%s/%s/mep1/amsi/v1/subscriptions", platformURL, sandbox_name)
		sub_resp := &model.InlineSubscription{}
		if i == 0 {
			sub_resp, err = CreateAMSSubscription(amsSubURL, appInstance.ID, callbackURL, associateId)
			if err != nil {
				return nil, fmt.Errorf("failed to create AMS subscription: %v", err)
			} else {
				log.Printf("AMS subscription created successfully: %v", sub_resp)
			}
		} else {
			sub_resp, err = CreateAMSSubscription(amsSubURL, appInstance.ID, callbackURL, nil)
			if err != nil {
				return nil, fmt.Errorf("failed to create AMS subscription: %v", err)
			} else {
				log.Printf("AMS subscription created successfully: %v", sub_resp)
			}
		}
		// Extract subscription ID from href using regex
		subID := ""
		if sub_resp.Links.Self.Href != "" {
			re := regexp.MustCompile(`subscriptions/(.*)`)
			matches := re.FindStringSubmatch(sub_resp.Links.Self.Href)
			if len(matches) > 1 {
				subID = matches[1]
			}
		}
		platformInfo := model.PlatformInfo{
			AmsServiceID:      resp.AppMobilityServiceId,
			AppInstanceID:     appInstance.ID,
			AmsSubscriptionID: subID,
			NodeName:          appInstance.NodeName,
		}
		platformDetails = append(platformDetails, map[string]model.PlatformInfo{
			appInstance.ID: platformInfo,
		})
	}

	return platformDetails, nil
}

// TODO: Complete the function to update AMS registration info
func PutAMSRegistrationInfo(url string, targetappinstance string, platformDetails []map[string]model.PlatformInfo,
	current_app_instance *string, associateID *model.AssociateId) error {
	if len(platformDetails) == 0 {
		return fmt.Errorf("platformDetails is empty")
	}
	if targetappinstance == "" {
		log.Println("Initializing AMS registration info...")
	}

	// Find the PlatformInfo for the current app instance by iterating through the slice
	var currentPlatformInfo *model.PlatformInfo
	for _, detail := range platformDetails {
		if info, exists := detail[*current_app_instance]; exists {
			currentPlatformInfo = &info
			break // Found it, no need to continue
		}
	}
	payload := model.RegistrationInfo{
		AppMobilityServiceId: currentPlatformInfo.AmsServiceID,
		DeviceInformation: []model.RegistrationInfoDeviceInformation{
			{
				AssociateId:             associateID,
				AppMobilityServiceLevel: &[]model.AppMobilityServiceLevel{"APP_MOBILITY_WITHOUT_CONFIRMATION"}[0],
				ContextTransferState:    &[]model.ContextTransferState{"USER_CONTEXT_TRANSFER_COMPLETED"}[0],
			},
		},
		ServiceConsumerId: &model.RegistrationInfoServiceConsumerId{
			AppInstanceId: *current_app_instance,
		},
	}
	req_url := fmt.Sprintf("%s/%s", url, currentPlatformInfo.AmsServiceID)
	resp, err := utils.SendMECRequest("PUT", req_url, payload)
	if err != nil {
		return fmt.Errorf("failed to update AMS registration info: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to update AMS registration info: received status code %d", resp.StatusCode)
	}
	log.Println("AMS registration info updated successfully")

	// find the PlatformInfo for the target app instance
	var targetPlatformInfo *model.PlatformInfo
	for _, detail := range platformDetails {
		if info, exists := detail[targetappinstance]; exists {
			targetPlatformInfo = &info
			break // Found it, no need to continue
		}
	}
	payload = model.RegistrationInfo{
		AppMobilityServiceId: targetPlatformInfo.AmsServiceID,
		DeviceInformation: []model.RegistrationInfoDeviceInformation{
			{
				AssociateId:             associateID,
				AppMobilityServiceLevel: &[]model.AppMobilityServiceLevel{"APP_MOBILITY_WITHOUT_CONFIRMATION"}[0],
				ContextTransferState:    &[]model.ContextTransferState{"NOT_TRANSFERRED"}[0],
			},
		},
		ServiceConsumerId: &model.RegistrationInfoServiceConsumerId{
			AppInstanceId: targetappinstance,
		},
	}
	req_url = fmt.Sprintf("%s/%s", url, targetPlatformInfo.AmsServiceID)
	resp, err = utils.SendMECRequest("PUT", req_url, payload)
	if err != nil {
		return fmt.Errorf("failed to update AMS registration info for target app instance: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to update AMS registration info for target app instance: received status code %d", resp.StatusCode)
	}

	log.Println("AMS registration info for target app instance updated successfully")

	return nil
}

func PUTAMSSubscriptionInfo(url string, targetappinstance string, platformDetails []map[string]model.PlatformInfo,
	current_app_instance *string, call_back_url string, associateid *model.AssociateId) error {
	if len(platformDetails) == 0 {
		return fmt.Errorf("platformDetails is empty")
	}
	if targetappinstance == "" {
		log.Println("Initializing AMS subscription info...")
	}

	// Find the PlatformInfo for the current app instance by iterating through the slice
	var currentPlatformInfo *model.PlatformInfo
	for _, detail := range platformDetails {
		if info, exists := detail[*current_app_instance]; exists {
			currentPlatformInfo = &info
			break // Found it, no need to continue
		}
	}

	payload := model.InlineSubscription{
		SubscriptionType: "MobilityProcedureSubscription",
		Links: &model.MobilityProcedureSubscriptionLinks{
			Self: &model.LinkType{
				Href: fmt.Sprintf("%s/%s", url, currentPlatformInfo.AmsSubscriptionID),
			},
		},
		CallbackReference: call_back_url,
		FilterCriteria: &model.MobilityProcedureSubscriptionFilterCriteria{
			AppInstanceId:  *current_app_instance,
			AssociateId:    []model.AssociateId{*associateid},
			MobilityStatus: []model.MobilityStatus{"INTERHOST_MOVEOUT_COMPLETED"},
		},
	}
	resp, err := utils.SendMECRequest("PUT", fmt.Sprintf("%s/%s", url, currentPlatformInfo.AmsSubscriptionID), payload)
	if err != nil {
		return fmt.Errorf("failed to update AMS subscription info: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to update AMS subscription info: received status code %d", resp.StatusCode)
	}
	log.Println("AMS subscription info updated successfully")

	// find the PlatformInfo for the target app instance
	var targetPlatformInfo *model.PlatformInfo
	for _, detail := range platformDetails {
		if info, exists := detail[targetappinstance]; exists {
			targetPlatformInfo = &info
			break // Found it, no need to continue
		}
	}

	payload = model.InlineSubscription{
		SubscriptionType: "MobilityProcedureSubscription",
		Links: &model.MobilityProcedureSubscriptionLinks{
			Self: &model.LinkType{
				Href: fmt.Sprintf("%s/%s", url, targetPlatformInfo.AmsSubscriptionID),
			},
		},
		CallbackReference: call_back_url,
		FilterCriteria: &model.MobilityProcedureSubscriptionFilterCriteria{
			AppInstanceId:  targetappinstance,
			AssociateId:    []model.AssociateId{*associateid},
			MobilityStatus: []model.MobilityStatus{"INTERHOST_MOVEOUT_TRIGGERED"},
		},
	}
	resp, err = utils.SendMECRequest("PUT", fmt.Sprintf("%s/%s", url, targetPlatformInfo.AmsSubscriptionID), payload)
	if err != nil {
		return fmt.Errorf("failed to update AMS subscription info for target app instance: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to update AMS subscription info for target app instance: received status code %d", resp.StatusCode)
	}

	log.Println("AMS subscription info for target app instance updated successfully")
	return nil
}
+26 −0
Original line number Diff line number Diff line
package mec

import (
	"encoding/json"
	utils "estimed_demo/utils"
	"fmt"
	"net/http"
)

// Fetch for platform info
func GETIotPlatformInfo(url string) ([]map[string]interface{}, error) {
	resp, err := utils.SendMECRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get IoT platform info: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to get IoT platform info: received status code %d", resp.StatusCode)
	}
	
	var platformInfo []map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&platformInfo); err != nil {
		return nil, fmt.Errorf("failed to decode IoT platform info: %v", err)
	}
	return platformInfo, nil
}	
 No newline at end of file
+33 −0
Original line number Diff line number Diff line
package mec

import (
	model "estimed_demo/Model"
	"fmt"
	"regexp"
)

// Match for target App Instance ID
func MatchForTargetAppInstance(allAppInstances []model.AppInstanceID, cse_name string) ([]model.AppInstanceID, error) {
	// Compile regex for cse_name once
	reCSE, err := regexp.Compile(cse_name)
	if err != nil {
		return nil, fmt.Errorf("invalid regex for cse_name: %v", err)
	}
	var matchingAppInstances []model.AppInstanceID
	for _, appInstance := range allAppInstances {
		// If name matches cse_name using compiled regex, store it in our array
		if reCSE.MatchString(appInstance.Name) {
			matchingAppInstance := model.AppInstanceID{
				ID:       appInstance.ID,
				Name:     appInstance.Name,
				NodeName: appInstance.NodeName,
			}
			matchingAppInstances = append(matchingAppInstances, matchingAppInstance)
		}
	}

	if len(matchingAppInstances) == 0 {
		return nil, fmt.Errorf("MEC App Instance with name %s not found", cse_name)
	}
	return matchingAppInstances, nil
}
+7 −0
Original line number Diff line number Diff line
package model

type AppInstanceID struct {
	ID string `json:"id"`
	Name string `json:"name"`
	NodeName string `json:"nodeName"`
}
 No newline at end of file
Loading