Commit 7f03c695 authored by Yann Garcia's avatar Yann Garcia
Browse files

Merge branch 'STF678_Task1_2_3_openCAPIF' into 'STF678_Task1_2_3_4'

Merge CAPIF into MEC Federation branch for ETSI SNS4SNS demo

See merge request !1
parents 558b6c02 fb58c301
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -46,9 +46,15 @@ ingress:
        {{- if .IsMepService }}
        - /{{.SandboxName}}/{{.MepName}}/mec_app_support
        - /{{.SandboxName}}/{{.MepName}}/mec_service_mgmt
        - /{{.SandboxName}}/{{.MepName}}/service-apis
        - /{{.SandboxName}}/{{.MepName}}/published-apis
        - /{{.SandboxName}}/{{.MepName}}/capif-events
        {{- else }}
        - /{{.SandboxName}}/mec_app_support
        - /{{.SandboxName}}/mec_service_mgmt
        - /{{.SandboxName}}/service-apis
        - /{{.SandboxName}}/published-apis
        - /{{.SandboxName}}/capif-events
        {{- end }}
  annotations:
    kubernetes.io/ingress.class: nginx
@@ -57,10 +63,16 @@ ingress:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      rewrite ^/{{ .SandboxName }}/{{.MepName}}/mec_app_support(/|$)(.*)$ /mec_app_support/$2 break;
      rewrite ^/{{ .SandboxName }}/{{.MepName}}/mec_service_mgmt(/|$)(.*)$ /mec_service_mgmt/$2 break;
      rewrite ^/{{ .SandboxName }}/{{.MepName}}/service-apis(/|$)(.*)$ /service-apis/$2 break;
      rewrite ^/{{ .SandboxName }}/{{.MepName}}/published-apis(/|$)(.*)$ /published-apis/$2 break;
      rewrite ^/{{ .SandboxName }}/{{.MepName}}/capif-events(/|$)(.*)$ /capif-events/$2 break;
    {{- else }}
    nginx.ingress.kubernetes.io/configuration-snippet: |
      rewrite ^/{{ .SandboxName }}/mec_app_support(/|$)(.*)$ /mec_app_support/$2 break;
      rewrite ^/{{ .SandboxName }}/mec_service_mgmt(/|$)(.*)$ /mec_service_mgmt/$2 break;
      rewrite ^/{{ .SandboxName }}/service-apis(/|$)(.*)$ /service-apis/$2 break;
      rewrite ^/{{ .SandboxName }}/published-apis(/|$)(.*)$ /published-apis/$2 break;
      rewrite ^/{{ .SandboxName }}/capif-events(/|$)(.*)$ /capif-events/$2 break;
    {{- end }}
    {{- if .AuthEnabled }}
    nginx.ingress.kubernetes.io/auth-url: https://$http_host/auth/v1/authenticate?svc=meep-app-enablement&sbox={{.SandboxName}}&mep={{.MepName}}
+1584 −0

File added.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ func main() {

		// Start Edge Platform App Enablement Service REST API Server
		router := server.NewRouter()
		methods := handlers.AllowedMethods([]string{"OPTIONS", "DELETE", "GET", "HEAD", "POST", "PUT"})
		methods := handlers.AllowedMethods([]string{"OPTIONS", "DELETE", "GET", "HEAD", "POST", "PUT", "PATCH"})
		header := handlers.AllowedHeaders([]string{"content-type"})
		log.Fatal(http.ListenAndServe(":80", handlers.CORS(methods, header)(router)))
		run = false
+13 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import (
	"sync"

	as "github.com/InterDigitalInc/AdvantEDGE/go-apps/meep-app-enablement/server/app-support"
	cm "github.com/InterDigitalInc/AdvantEDGE/go-apps/meep-app-enablement/server/capif-mgmt"
	sm "github.com/InterDigitalInc/AdvantEDGE/go-apps/meep-app-enablement/server/service-mgmt"
	httpLog "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-http-logger"
	log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger"
@@ -125,6 +126,12 @@ func Init() (err error) {
	}
	log.Info("Service Management created")

	// Initialize Capif Management
	err = cm.Init(sandboxName, mepName, hostUrl, mqLocal, redisAddr, &mutex)
	if err != nil {
		return err
	}
	log.Info("Service Management created")
	// Initialize App Support
	err = as.Init(sandboxName, mepName, hostUrl, mqLocal, redisAddr, &mutex)
	if err != nil {
@@ -144,6 +151,11 @@ func Run() (err error) {
		return err
	}

	err = cm.Run()
	if err != nil {
		return err
	}

	err = as.Run()
	if err != nil {
		return err
@@ -189,6 +201,7 @@ func Stop() {
	}

	_ = sm.Stop()
	_ = cm.Stop()
	_ = as.Stop()

	// Remove APIs
+133 −58
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import (
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"sync"
	"time"
@@ -658,79 +659,124 @@ func appRegistrationPOST(w http.ResponseWriter, r *http.Request) {

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	// Decode the request body into AppInfo struct
	// Parse request body into AppInfo struct
	var appInfo AppInfo
	err := json.NewDecoder(r.Body).Decode(&appInfo)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
	if err := json.NewDecoder(r.Body).Decode(&appInfo); err != nil {
		log.Error("Error decoding request body:", err)
		errHandlerProblemDetails(w, "Invalid request body.", http.StatusBadRequest)
		return
	}
	log.Info("appRegistrationPOST: appInfo: ", appInfo)
	log.Info("appRegistrationPOST: Received appInfo:", appInfo)

	// Validation of mandatory parameters
	// Validate required fields
	if appInfo.AppName == "" {
		log.Error("Mandatory AppName parameter not present")
		errHandlerProblemDetails(w, "Mandatory AppName parameter not present", http.StatusBadRequest)
		log.Error("Missing mandatory parameter: AppName")
		errHandlerProblemDetails(w, "Mandatory attribute AppName is missing.", http.StatusBadRequest)
		return
	}

	if appInfo.AppInstanceId == "" {
		log.Error("Mandatory appInstanceId parameter should be present")
		errHandlerProblemDetails(w, "Mandatory attribute appInstanceId is missing in the request body.", http.StatusBadRequest)
		log.Error("Missing mandatory parameter: AppInstanceId")
		errHandlerProblemDetails(w, "Mandatory attribute AppInstanceId is missing.", http.StatusBadRequest)
		return
	}

	if !appInfo.IsInsByMec && appInfo.Endpoint == nil {
		log.Error("Shall be present when IsInsByMec is FALSE")
		errHandlerProblemDetails(w, "Endpoint shall be present when IsInsByMec is FALSE.", http.StatusBadRequest)
		log.Error("Endpoint is required when IsInsByMec is FALSE")
		errHandlerProblemDetails(w, "Endpoint is required when IsInsByMec is FALSE.", http.StatusBadRequest)
		return
	}

	// Get App instance
	log.Info("appRegistrationPOST: appInfo.AppInstanceId: ", appInfo.AppInstanceId)
	// Process appProfile if provided
	if appInfo.AppProfile != nil {
		// Validate appProvider and other fields mapped to EASProfile
		if appInfo.AppProvider != appInfo.AppProfile.ProvId {
			log.Error("Mismatch between appProvider in AppInfo and provId in appProfile")
			errHandlerProblemDetails(w, "appProvider and provId must match.", http.StatusBadRequest)
			return
		}

		if !reflect.DeepEqual(getEndpointUris(appInfo.Endpoint), getProfileEndpointUris(appInfo.AppProfile.EndPt)) {
			log.Error("Mismatch between endpoint in AppInfo and endPt in appProfile")
			errHandlerProblemDetails(w, "Endpoint and endPt must match.", http.StatusBadRequest)
			return
		}

		if appInfo.AppProfile.EasId == "" {
			log.Error("Missing mandatory parameter: easId")
			errHandlerProblemDetails(w, "Mandatory attribute easId is missing.", http.StatusBadRequest)
			return
		}

		if appInfo.AppName != appInfo.AppProfile.EasId {
			log.Error("Mismatch between AppName in AppInfo and EasId in appProfile")
			errHandlerProblemDetails(w, "AppName and EasId must match.", http.StatusBadRequest)
			return
		}
		// Additional checks for attributes such as scheds, svcArea, etc., as required.
	}

	// Retrieve App instance information
	log.Info("appRegistrationPOST: Processing AppInstanceId:", appInfo.AppInstanceId)
	appId, err := getAppInfo(appInfo.AppInstanceId)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusNotFound)
		log.Error("Error retrieving app instance:", err)
		errHandlerProblemDetails(w, "App instance not found.", http.StatusNotFound)
		return
	}
	log.Info("appRegistrationPOST: appId: ", appId)
	log.Info("appRegistrationPOST: Retrieved appId:", appId)

	// Validate App info
	// Validate the retrieved AppInfo
	code, problemDetails, err := validateAppInfo(appId)
	if err != nil {
		log.Error(err.Error())
		log.Error("Error validating app info:", err)
		if problemDetails != "" {
			w.WriteHeader(code)
			fmt.Fprint(w, problemDetails)
		} else {
			errHandlerProblemDetails(w, err.Error(), code)
			errHandlerProblemDetails(w, "Validation error.", code)
		}
		return
	}

	// Set the application info in Redis
	// Store AppInfo in Redis
	keyName := baseKey + "appInfo:" + appInfo.AppInstanceId
	log.Info("appRegistrationPOST: keyName: ", keyName)
	log.Info("appRegistrationPOST: Storing data with key:", keyName)
	if err := rc.JSONSetEntry(keyName, ".", convertAppInfoToJson(&appInfo)); err != nil {
		log.Error("Unable to store new Registration in redis")
		errHandlerProblemDetails(w, "Unable to store new Registration in redis", http.StatusInternalServerError)
		log.Error("Failed to store registration in Redis:", err)
		errHandlerProblemDetails(w, "Server error. Could not store registration.", http.StatusInternalServerError)
		return
	}

	// Create a unique link for every subscription and concatenate subscription to it
	// Generate resource URI for the created app registration
	resourceURI := hostUrl.String() + basePath + "registrations/" + appInfo.AppInstanceId
	log.Info("appRegistrationPOST: resourceUDI: ", resourceURI)
	log.Info("appRegistrationPOST: Generated resource URI:", resourceURI)
	w.Header().Set("Location", resourceURI)

	// Prepare & send response
	// Send JSON response with status 201 Created
	jsonResponse := convertAppInfoToJson(&appInfo)
	// On successful registration, return 201 Created with the response body
	w.WriteHeader(http.StatusCreated)
	fmt.Fprint(w, jsonResponse)
}

func getEndpointUris(endpoint *OneOfAppInfoEndpoint) []string {
	if endpoint == nil {
		return nil
	}

	// Access the `Uris` field directly from `EndPointInfoUris`
	return endpoint.EndPointInfoUris.Uris
}

func getProfileEndpointUris(endPt *OneOfAppProfileEndPt) []string {
	if endPt == nil {
		return nil
	}

	// Access the `Uris` field directly from `EndPointInfoUris`
	return endPt.EndPointInfoUris.Uris
}

/*
* appRegistrationGET handles retrieving the registration information of an application.
* It fetches the appInstanceId from the URL variables, retrieves the associated application info from Redis, and sends it back in the response. If the appInstanceId is not found or an error occurs, it responds with the appropriate status and error message.
@@ -790,57 +836,86 @@ func appRegistrationPUT(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	vars := mux.Vars(r)
	appInstanceId := vars["appInstanceId"]
	log.Info("appRegistrationPUT: appInstanceId: ", appInstanceId)
	log.Info("appRegistrationPUT: Received appInstanceId:", appInstanceId)

	// Read JSON input stream provided in the Request, and stores it in the buffer of a Decoder object
	decoder := json.NewDecoder(r.Body)
	// Decode function return strings containing the text provided in the request body
	// Decode JSON input from request body into AppInfo structure
	var appInfoPut AppInfo
	err := decoder.Decode(&appInfoPut)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
	if err := json.NewDecoder(r.Body).Decode(&appInfoPut); err != nil {
		log.Error("Error decoding request body:", err)
		errHandlerProblemDetails(w, "Invalid request body.", http.StatusBadRequest)
		return
	}
	log.Info("appRegistrationPUT: appInfoPut: ", appInfoPut)
	log.Info("appRegistrationPUT: Parsed appInfoPut:", appInfoPut)

	// Check if the appInfo exists in Redis for the given appInstanceId
	keyName := baseKey + "appInfo:" + appInstanceId
	log.Info("appRegistrationPUT: keyName: ", keyName)
	log.Info("appRegistrationPUT: Checking existence of key:", keyName)
	jsonAppInfo, _ := rc.JSONGetEntry(keyName, ".")
	if jsonAppInfo == "" {
		log.Error("appInfo not found against the provided appInstanceId")
		errHandlerProblemDetails(w, "appInfo not found against the provided appInstanceId", http.StatusNotFound)
		log.Error("No appInfo found for provided appInstanceId")
		errHandlerProblemDetails(w, "appInfo not found for the provided appInstanceId", http.StatusNotFound)
		return
	}

	if appInfoPut.AppInstanceId != "" {
		if appInstanceId != appInfoPut.AppInstanceId {
			log.Error("appInstnaceId provided in endpoint and in request body not matching")
			errHandlerProblemDetails(w, "appInstanceId provided in endpoint and in request body not matching", http.StatusNotFound)
	// Validate appInstanceId consistency between URL and request body
	if appInfoPut.AppInstanceId != "" && appInstanceId != appInfoPut.AppInstanceId {
		log.Error("appInstanceId mismatch between endpoint and request body")
		errHandlerProblemDetails(w, "appInstanceId in endpoint and request body do not match", http.StatusBadRequest)
		return
	}
	}

	// Validate required parameters
	if appInfoPut.AppName == "" {
		log.Error("Mandatory AppName parameter not present")
		errHandlerProblemDetails(w, "Mandatory AppName parameter not present", http.StatusBadRequest)
		log.Error("Missing mandatory parameter: AppName")
		errHandlerProblemDetails(w, "Mandatory attribute AppName is missing.", http.StatusBadRequest)
		return
	}

	if !appInfoPut.IsInsByMec && appInfoPut.Endpoint == nil {
		log.Error("Shall be present when IsInsByMec is FALSE")
		errHandlerProblemDetails(w, "Shall be present when IsInsByMec is FALSE.", http.StatusBadRequest)
		log.Error("Endpoint is required when IsInsByMec is FALSE")
		errHandlerProblemDetails(w, "Endpoint is required when IsInsByMec is FALSE.", http.StatusBadRequest)
		return
	}

	appInfoPut.AppInstanceId = appInstanceId
	// Process appProfile if provided
	if appInfoPut.AppProfile != nil {
		// Validate appProvider and associated fields as per EASProfile mapping
		if appInfoPut.AppProvider != appInfoPut.AppProfile.ProvId {
			log.Error("Mismatch between appProvider in AppInfo and provId in appProfile")
			errHandlerProblemDetails(w, "appProvider and provId must match.", http.StatusBadRequest)
			return
		}

	// Store appInfo key in redis
	err = rc.JSONSetEntry(baseKey+"appInfo:"+appInstanceId, ".", convertAppInfoToJson(&appInfoPut))
	if err != nil {
		log.Error("Failed to store AppInfo in the redis DB: ", err)
		if !reflect.DeepEqual(getEndpointUris(appInfoPut.Endpoint), getProfileEndpointUris(appInfoPut.AppProfile.EndPt)) {
			log.Error("Mismatch between endpoint in AppInfo and endPt in appProfile")
			errHandlerProblemDetails(w, "Endpoint and endPt must match.", http.StatusBadRequest)
			return
		}

		if appInfoPut.AppProfile.EasId == "" {
			log.Error("Missing mandatory parameter: easId")
			errHandlerProblemDetails(w, "Mandatory attribute easId is missing.", http.StatusBadRequest)
			return
		}

		if appInfoPut.AppName != appInfoPut.AppProfile.EasId {
			log.Error("Mismatch between AppName in AppInfo and EasId in appProfile")
			errHandlerProblemDetails(w, "AppName and EasId must match.", http.StatusBadRequest)
			return
		}
		// Additional consistency checks for fields such as scheds, svcArea, etc., as required.
	}

	// Set appInstanceId in appInfoPut and store in Redis
	appInfoPut.AppInstanceId = appInstanceId
	log.Info("appRegistrationPUT: Storing updated appInfo for appInstanceId:", appInstanceId)
	if err := rc.JSONSetEntry(keyName, ".", convertAppInfoToJson(&appInfoPut)); err != nil {
		log.Error("Failed to store updated AppInfo in Redis:", err)
		errHandlerProblemDetails(w, "Server error. Could not store updated appInfo.", http.StatusInternalServerError)
		return
	}

	// Respond with status 204 No Content to indicate successful update
	w.WriteHeader(http.StatusNoContent)
}

Loading