Commit ee7b9d8e authored by Ikram Haq's avatar Ikram Haq
Browse files

Merge branch 'STF678_TASK1_2_3_4_MEC021' into 'STF678_Task1_2_3_4'

merge STF678_TASK1_2_3_4_MEC021 branch into STF678_Task1_2_3_4

See merge request !4
parents b5b0636e 7fbc85c5
Loading
Loading
Loading
Loading
+18 −18
Original line number Diff line number Diff line
openapi: 3.0.0
info:
  title: AdvantEDGE Application Mobility API
  version: '2.2.1'
  version: '3.1.1'
  description: Application Mobility Service is AdvantEDGE's implementation of [ETSI
    MEC ISG MEC021 Application Mobility API](http://www.etsi.org/deliver/etsi_gs/MEC/001_099/021/02.02.01_60/gs_MEC021v020201p.pdf)
    MEC ISG MEC021 Application Mobility API](https://www.etsi.org/deliver/etsi_gs/MEC/001_099/021/03.01.01_60/gs_mec021v030101p.pdf)
    <p>[Copyright (c) ETSI 2017](https://forge.etsi.org/etsi-forge-copyright-notice.txt)
    <p>**Micro-service**<br>[meep-ams](https://github.com/InterDigitalInc/AdvantEDGE/tree/master/go-apps/meep-ams)
    <p>**Type & Usage**<br>Edge Service used by edge applications that want to get
@@ -16,8 +16,8 @@ info:
    name: InterDigital AdvantEDGE Support
    email: AdvantEDGE@InterDigital.com
externalDocs:
  description: ETSI GS MEC 021 Application Mobility Service API, v2.2.1
  url: https://www.etsi.org/deliver/etsi_gs/MEC/001_099/021/02.02.01_60/gs_mec021v020201p.pdf
  description: ETSI GS MEC 021 Application Mobility Service API, v3.1.1
  url: https://www.etsi.org/deliver/etsi_gs/MEC/001_099/021/03.01.01_60/gs_mec021v030101p.pdf
servers:
- url: https://localhost/sandboxname/amsi/v1
  variables: {}
@@ -40,30 +40,30 @@ paths:
        explode: true
        schema:
          type: string
      - name: all_fields
      - name: All_fields
        in: query
        description: Include all complex attributes in the response.
        style: form
        explode: true
        schema:
          type: string
      - name: fields
      - name: Fields
        in: query
        description: Complex attributes to be included into the response. See clause 6.18 in ETSI GS MEC 009
        style: form
        explode: true
        schema:
          type: string
      - name: exclude_fields
      - name: Exclude_fields
        in: query
        description: Complex attributes to be excluded from the response.See clause 6.18 in ETSI GS MEC 009
        style: form
        explode: true
        schema:
          type: string
      - name: exclude_default
      - name: Exclude_default
        in: query
        description: Indicates to exclude the following complex attributes from the response  See clause 6.18 in ETSI GS MEC 011 for details.
        description: Indicates to exclude the following complex attributes from the response  See clause 6.18 in ETSI GS MEC 009 for details.
        style: form
        explode: true
        schema:
@@ -133,35 +133,35 @@ paths:
      parameters:
      - name: filter
        in: query
        description: Attribute-based filtering parameters according to ETSI GS MEC 011
        description: Attribute-based filtering parameters, according to ETSI GS MEC 009, use the format (op,attr,value)
        style: form
        explode: true
        schema:
          type: string
      - name: all_fields
      - name: All_fields
        in: query
        description: Include all complex attributes in the response.
        description: Include all complex attributes in the response. e.g., All_Fields.
        style: form
        explode: true
        schema:
          type: string
      - name: fields
      - name: Fields
        in: query
        description: Complex attributes to be included into the response. See clause 6.18 in ETSI GS MEC 011
        description: Complex attributes to be included in the response (see Clause 6.18 in ETSI GS MEC 009), e.g., att or att/subatt.
        style: form
        explode: true
        schema:
          type: string
      - name: exclude_fields
      - name: Exclude_fields
        in: query
        description: Complex attributes to be excluded from the response.See clause 6.18 in ETSI GS MEC 011
        description: Complex attributes to be excluded in the response (see Clause 6.18 in ETSI GS MEC 009), e.g., att or att/subatt.
        style: form
        explode: true
        schema:
          type: string
      - name: exclude_default
      - name: Exclude_default
        in: query
        description: Indicates to exclude the following complex attributes from the response  See clause 6.18 in ETSI GS MEC 011 for details.
        description: Indicates to exclude the following complex attributes from the response  See clause 6.18 in ETSI GS MEC 009 for details.
        style: form
        explode: true
        schema:
+347 −15
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import (
	"net/http"
	"net/url"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
@@ -148,6 +149,19 @@ func notImplemented(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusNotImplemented)
}

type RegisterationInfoList struct {
	Registrations []RegistrationInfo
	Filters       *FilterParameters
}

type FilterParameters struct {
	filter          string
	all_fields      string
	fields          string
	exclude_fields  string
	exclude_default string
}

// Init - App Mobility Service initialization
func Init() (err error) {

@@ -1205,8 +1219,10 @@ func subscriptionLinkListSubscriptionsGet(w http.ResponseWriter, r *http.Request
	u, _ := url.Parse(r.URL.String())
	q := u.Query()
	validQueryParams := []string{"subscriptionType"}
	if !validateQueryParams(q, validQueryParams) {
		w.WriteHeader(http.StatusBadRequest)
	err := validateQueryParams(q, validQueryParams)
	if err != nil {

		errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
		return
	}

@@ -1473,20 +1489,51 @@ func appMobilityServiceByIdDELETE(w http.ResponseWriter, r *http.Request) {
}

func appMobilityServiceGET(w http.ResponseWriter, r *http.Request) {

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

	// Validate query parameters
	u, _ := url.Parse(r.URL.String())
	q := u.Query()
	validParams := []string{"filter", "All_fields", "Fields", "Exclude_fields", "Exclude_default"}
	err := validateQueryParams(q, validParams)
	if err != nil {
		print("Query Parameter error")
		errHandlerProblemDetails(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Parse query parameters
	urlFilter := q.Get("filter")
	urlAllFields := q.Get("All_fields")
	urlfields := q.Get("Fields")
	urlExcludeFields := q.Get("Exclude_fields")
	urlExcludeDefault := q.Get("Exclude_default")
	regInfoList := &RegisterationInfoList{
		Filters: &FilterParameters{
			filter:          urlFilter,
			all_fields:      urlAllFields,
			fields:          urlfields,
			exclude_fields:  urlExcludeFields,
			exclude_default: urlExcludeDefault,
		},
		Registrations: make([]RegistrationInfo, 0),
	}

	// Get all AMS Registration Info
	regInfoList := make([]RegistrationInfo, 0)
	//regInfoList := make([]RegistrationInfo, 0)
	key := baseKey + "svc:*:info"
	err := rc.ForEachJSONEntry(key, populateRegInfoList, &regInfoList)

	err = rc.ForEachJSONEntry(key, populateRegInfoList, regInfoList)

	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Send response
	jsonResponse, err := json.Marshal(regInfoList)
	// Prepare & send response
	jsonResponse, err := json.Marshal(regInfoList.Registrations)
	if err != nil {
		log.Error(err.Error())
		errHandlerProblemDetails(w, err.Error(), http.StatusInternalServerError)
@@ -1498,9 +1545,9 @@ func appMobilityServiceGET(w http.ResponseWriter, r *http.Request) {
}

func populateRegInfoList(key string, jsonEntry string, response interface{}) error {
	regInfoList := response.(*[]RegistrationInfo)
	if regInfoList == nil {
		return errors.New("Response not defined")
	data := response.(*RegisterationInfoList)
	if data == nil {
		return errors.New("response not defined")
	}

	// Retrieve registration info from DB
@@ -1509,7 +1556,202 @@ func populateRegInfoList(key string, jsonEntry string, response interface{}) err
	if err != nil {
		return err
	}
	*regInfoList = append(*regInfoList, regInfo)

	// Filter services
	if data.Filters != nil {

		// Filter Paramter
		if data.Filters.filter != "" {
			filterField := data.Filters.filter
			// Split filterField into operator, attribute, and value
			operator, attribute, value, _ := parseFilter(filterField)

			// Apply filters based on attribute
			switch attribute {
			case "appMobilityServiceId":
				if !applyStringFilter(operator, regInfo.AppMobilityServiceId, value) {
					return nil
				}

			case "serviceConsumerId/appInstanceId":
				if !applyStringFilter(operator, regInfo.ServiceConsumerId.AppInstanceId, value) {
					return nil
				}

			case "serviceConsumerId/mepId":
				if !applyStringFilter(operator, regInfo.ServiceConsumerId.MepId, value) {
					return nil
				}

			case "deviceInformation/associateId":
				matched := false
				for _, deviceInfo := range regInfo.DeviceInformation {
					if applyStringFilter(operator, deviceInfo.AssociateId.Value, value) {
						matched = true
						break
					}
				}
				if !matched {
					return nil
				}

			case "deviceInformation/appMobilityServiceLevel":
				matched := false
				for _, deviceInfo := range regInfo.DeviceInformation {
					if applyStringFilter(operator, string(*deviceInfo.AppMobilityServiceLevel), value) {
						matched = true
						break
					}
				}
				if !matched {
					return nil
				}

			case "deviceInformation/contextTransferState":
				matched := false
				for _, deviceInfo := range regInfo.DeviceInformation {
					if applyEnumFilter(operator, string(*deviceInfo.ContextTransferState), value) {
						matched = true
						break
					}
				}
				if !matched {
					return nil
				}

			case "expiryTime":
				expiryTime, err := strconv.ParseUint(value, 10, 32)
				if err != nil {
					return nil
				}
				if !applyNumericFilter(operator, uint32(regInfo.ExpiryTime), uint32(expiryTime)) {
					return nil
				}

			default:
				return nil
			}

		}

		// Handle Fields Parameter (Include ALL fields)
		if data.Filters.all_fields != "" && data.Filters.all_fields != "All_fields" {
			return nil
		}

		// Handle Fields Parameter
		if data.Filters.fields != "" {
			fields := strings.Split(data.Filters.fields, ",")
			filteredRegInfo := RegistrationInfo{}

			for _, field := range fields {
				switch field {
				case "appMobilityServiceId":
					filteredRegInfo.AppMobilityServiceId = regInfo.AppMobilityServiceId

				case "serviceConsumerId/appInstanceId":
					// if filteredRegInfo.ServiceConsumerId == nil {
					// 	filteredRegInfo.ServiceConsumerId = &RegistrationInfoServiceConsumerId{}
					// }
					if regInfo.ServiceConsumerId.AppInstanceId != "" {
						filteredRegInfo.ServiceConsumerId.AppInstanceId = regInfo.ServiceConsumerId.AppInstanceId
					}

				case "serviceConsumerId/mepId":
					// if filteredRegInfo.ServiceConsumerId == nil {
					// 	filteredRegInfo.ServiceConsumerId = &RegistrationInfoServiceConsumerId{}
					// }
					if regInfo.ServiceConsumerId.MepId != "" {
						filteredRegInfo.ServiceConsumerId.MepId = regInfo.ServiceConsumerId.MepId
					}

				case "deviceInformation/associateId":
					for _, deviceInfo := range regInfo.DeviceInformation {
						if deviceInfo.AssociateId.Value != "" {
							filteredDeviceInfo := RegistrationInfoDeviceInformation{
								AssociateId: deviceInfo.AssociateId,
							}
							filteredRegInfo.DeviceInformation = append(filteredRegInfo.DeviceInformation, filteredDeviceInfo)
						}
					}

				case "deviceInformation/appMobilityServiceLevel":
					for _, deviceInfo := range regInfo.DeviceInformation {
						if *deviceInfo.AppMobilityServiceLevel != "" {
							filteredDeviceInfo := RegistrationInfoDeviceInformation{
								AppMobilityServiceLevel: deviceInfo.AppMobilityServiceLevel,
							}
							filteredRegInfo.DeviceInformation = append(filteredRegInfo.DeviceInformation, filteredDeviceInfo)
						}
					}

				case "deviceInformation/contextTransferState":
					for _, deviceInfo := range regInfo.DeviceInformation {
						if *deviceInfo.ContextTransferState != "" {
							filteredDeviceInfo := RegistrationInfoDeviceInformation{
								ContextTransferState: deviceInfo.ContextTransferState,
							}
							filteredRegInfo.DeviceInformation = append(filteredRegInfo.DeviceInformation, filteredDeviceInfo)
						}
					}

				case "expiryTime":
					//Logic
					if string(regInfo.ExpiryTime) != "" {
						filteredRegInfo.ExpiryTime = regInfo.ExpiryTime
					}

				}
				// Replace regInfo with the filtered version

			}
			regInfo = filteredRegInfo
		}

		// Handle Exclude Fields Parameter (Exclude specified fields)
		if data.Filters.exclude_fields != "" {
			excludeFields := strings.Split(data.Filters.exclude_fields, ",")
			filteredRegInfo := regInfo

			// Exclude the listed fields
			for _, field := range excludeFields {
				switch field {
				case "appMobilityServiceId":
					filteredRegInfo.AppMobilityServiceId = "" // Exclude this field
				case "serviceConsumerId/appInstanceId":
					if filteredRegInfo.ServiceConsumerId != nil {
						filteredRegInfo.ServiceConsumerId.AppInstanceId = "" // Exclude this field
					}
				case "serviceConsumerId/mepId":
					if filteredRegInfo.ServiceConsumerId != nil {
						filteredRegInfo.ServiceConsumerId.MepId = "" // Exclude this field
					}
				case "deviceInformation/associateId":
					for i := range filteredRegInfo.DeviceInformation {
						filteredRegInfo.DeviceInformation[i].AssociateId = nil // Exclude this field
					}
				case "deviceInformation/appMobilityServiceLevel":
					for i := range filteredRegInfo.DeviceInformation {
						filteredRegInfo.DeviceInformation[i].AppMobilityServiceLevel = nil // Exclude this field
					}
				case "deviceInformation/contextTransferState":
					for i := range filteredRegInfo.DeviceInformation {
						filteredRegInfo.DeviceInformation[i].ContextTransferState = nil // Exclude this field
					}
				case "expiryTime":
					filteredRegInfo.ExpiryTime = 0 // Exclude this field
				}
			}

			// Replace regInfo with the filtered version based on exclude_fields parameter
			regInfo = filteredRegInfo
		}

		// Handle Exclude Fields default Parameter (Exclude specified fields)

	}
	// Returning Data
	data.Registrations = append(data.Registrations, regInfo)
	return nil
}

@@ -2324,21 +2566,22 @@ func delTrackedDevInfo(svcId string, address string) error {
	return nil
}

func validateQueryParams(params url.Values, validParamList []string) bool {
func validateQueryParams(params url.Values, validParams []string) error {
	for param := range params {
		found := false
		for _, validParam := range validParamList {
		for _, validParam := range validParams {
			if param == validParam {
				found = true
				break
			}
		}
		if !found {
			log.Error("Invalid query param: ", param)
			return false
			err := errors.New("Invalid query param: " + param)
			log.Error(err.Error())
			return err
		}
	}
	return true
	return nil
}

func validateQueryParamValue(val string, validValues []string) bool {
@@ -2370,3 +2613,92 @@ func errHandlerProblemDetails(w http.ResponseWriter, error string, code int) {
	w.WriteHeader(code)
	fmt.Fprint(w, jsonResponse)
}

func parseFilter(filterField string) (string, string, string, error) {
	// Regular expression to match the filter format
	re := regexp.MustCompile(`^(eq|neq|gt|lt|gte|lte|in|nin|cont|ncont),([a-zA-Z0-9/]+),([^,]+)(?:,([^,]+))?$`)

	// Trim any surrounding parentheses
	filterField = strings.Trim(filterField, "()")

	// Match the filterField against the regular expression
	matches := re.FindStringSubmatch(filterField)
	if len(matches) < 3 {
		return "", "", "", nil
	}

	// Extract the operator, attribute, and value(s)
	operator := matches[1]
	attribute := matches[2]
	value := matches[3]

	// If there's a second value (for operators like "in" or "nin"), handle it
	if len(matches) > 4 && matches[4] != "" {
		value += "," + matches[4]
	}

	return operator, attribute, value, nil
}

// Helper functions for applying filters
func applyStringFilter(operator, fieldValue, filterValue string) bool {
	switch operator {
	case "eq":
		return fieldValue == filterValue
	case "neq":
		return fieldValue != filterValue
	case "cont":
		return strings.Contains(fieldValue, filterValue)
	case "ncont":
		return !strings.Contains(fieldValue, filterValue)
	case "in":
		values := strings.Split(filterValue, ",")
		for _, v := range values {
			if fieldValue == v {
				return true
			}
		}
		return false
	case "nin":
		values := strings.Split(filterValue, ",")
		for _, v := range values {
			if fieldValue == v {
				return false
			}
		}
		return true
	case "gt":
		return fieldValue > filterValue
	case "gte":
		return fieldValue >= filterValue
	case "lt":
		return fieldValue < filterValue
	case "lte":
		return fieldValue <= filterValue
	default:
		return false
	}
}

func applyEnumFilter(operator, fieldValue, filterValue string) bool {
	return applyStringFilter(operator, fieldValue, filterValue)
}

func applyNumericFilter(operator string, fieldValue, filterValue uint32) bool {
	switch operator {
	// case "eq":
	// 	return fieldValue == filterValue
	// case "neq":
	// 	return fieldValue != filterValue
	case "gt":
		return fieldValue > filterValue
	case "gte":
		return fieldValue >= filterValue
	case "lt":
		return fieldValue < filterValue
	case "lte":
		return fieldValue <= filterValue
	default:
		return false
	}
}
+1 −1
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
 *
 * Application Mobility Service is AdvantEDGE's implementation of [ETSI MEC ISG MEC021 Application Mobility API](http://www.etsi.org/deliver/etsi_gs/MEC/001_099/021/02.02.01_60/gs_MEC021v020201p.pdf) <p>[Copyright (c) ETSI 2017](https://forge.etsi.org/etsi-forge-copyright-notice.txt) <p>**Micro-service**<br>[meep-ams](https://github.com/InterDigitalInc/AdvantEDGE/tree/master/go-apps/meep-ams) <p>**Type & Usage**<br>Edge Service used by edge applications that want to get information about application mobility in the network <p>**Note**<br>AdvantEDGE supports a selected subset of Application Mobility API endpoints (see below).
 *
 * API version: 2.2.1
 * API version: 3.1.1
 * Contact: AdvantEDGE@InterDigital.com
 * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
 */
+1 −1
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
 *
 * Application Mobility Service is AdvantEDGE's implementation of [ETSI MEC ISG MEC021 Application Mobility API](http://www.etsi.org/deliver/etsi_gs/MEC/001_099/021/02.02.01_60/gs_MEC021v020201p.pdf) <p>[Copyright (c) ETSI 2017](https://forge.etsi.org/etsi-forge-copyright-notice.txt) <p>**Micro-service**<br>[meep-ams](https://github.com/InterDigitalInc/AdvantEDGE/tree/master/go-apps/meep-ams) <p>**Type & Usage**<br>Edge Service used by edge applications that want to get information about application mobility in the network <p>**Note**<br>AdvantEDGE supports a selected subset of Application Mobility API endpoints (see below).
 *
 * API version: 2.2.1
 * API version: 3.1.1
 * Contact: AdvantEDGE@InterDigital.com
 * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
 */
+1 −0
Original line number Diff line number Diff line
package server
Loading