From ad9eb0668aa14ca5d5f183574eed0d29995bd4ee Mon Sep 17 00:00:00 2001 From: Stavros-Anastasios Charismiadis Date: Fri, 6 Jun 2025 15:28:49 +0300 Subject: [PATCH 1/4] Working version. Needs some fixes. One test fails --- .../controllers/default_controller.py | 3 +- .../core/serviceapidescriptions.py | 33 ++++++++++++++++--- .../models/service_api_description.py | 19 ----------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py index 27c4b642..29839084 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py @@ -7,6 +7,7 @@ from published_apis.vendor_specific import find_attribute_in_body, vendor_specif from ..core.responses import bad_request_error from ..core.serviceapidescriptions import PublishServiceOperations +from ..core.serviceapidescriptions import return_negotiated_supp_feat_dict from ..core.validate_user import ControlAccess from ..models.service_api_description import ServiceAPIDescription # noqa: E501 @@ -88,7 +89,7 @@ def apf_id_service_apis_post(apf_id, body): # noqa: E501 invalid_params=[{"param": "supportedFeatures", "reason": "not defined"}] ) - supp_feat_dict = ServiceAPIDescription.return_supp_feat_dict( + supp_feat_dict = return_negotiated_supp_feat_dict( body['supportedFeatures']) vendor_specific = [] diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py index d5b45175..15f04886 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py @@ -21,12 +21,33 @@ from .responses import ( unauthorized_error ) + +TOTAL_FEATURES = 10 +SUPPORTED_FEATURES_HEX = "120" + publisher_ops = Publisher() service_api_not_found_message = "Service API not found" +def return_negotiated_supp_feat_dict(supp_feat): + final_supp_feat = bin(int(supp_feat, 16) & int(SUPPORTED_FEATURES_HEX, 16))[2:].zfill(TOTAL_FEATURES)[::-1] + + return { + "ApiSupportedFeaturePublishing": True if final_supp_feat[0] == "1" else False, + "PatchUpdate": True if final_supp_feat[1] == "1" else False, + "ExtendedIntfDesc": True if final_supp_feat[2] == "1" else False, + "MultipleCustomOperations": True if final_supp_feat[3] == "1" else False, + "ProtocDataFormats_Ext1": True if final_supp_feat[4] == "1" else False, + "ApiStatusMonitoring": True if final_supp_feat[5] == "1" else False, + "EdgeApp_2": True if final_supp_feat[6] == "1" else False, + "RNAA": True if final_supp_feat[7] == "1" else False, + "VendorExt": True if final_supp_feat[8] == "1" else False, + "SliceBasedAPIExposure": True if final_supp_feat[9] == "1" else False, + "Final": hex(int(final_supp_feat[::-1], 2))[2:] + } + class PublishServiceOperations(Resource): def check_apf(self, apf_id): @@ -126,7 +147,7 @@ class PublishServiceOperations(Resource): vendor_specific, serviceapidescription_dict) rec.update(serviceapidescription_dict) - if not ServiceAPIDescription.return_supp_feat_dict(rec.get("supported_features"))["ApiStatusMonitoring"] and rec.get("api_status", None) is not None: + if not return_negotiated_supp_feat_dict(rec.get("supported_features"))["ApiStatusMonitoring"] and rec.get("api_status", None) is not None: return bad_request_error( detail="Set apiStatus with apiStatusMonitoring feature inactive at supportedFeatures if not allowed", cause="apiStatus can't be set if apiStatusMonitoring is inactive", @@ -296,8 +317,9 @@ class PublishServiceOperations(Resource): service_api_description["apf_id"] = serviceapidescription_old["apf_id"] service_api_description["onboarding_date"] = serviceapidescription_old["onboarding_date"] service_api_description["api_id"] = serviceapidescription_old["api_id"] + service_api_description["supported_features"] = return_negotiated_supp_feat_dict(service_api_description["supported_features"])["Final"] - if not ServiceAPIDescription.return_supp_feat_dict(service_api_description.get("supported_features"))["ApiStatusMonitoring"] and service_api_description.get("api_status", None) is not None: + if not return_negotiated_supp_feat_dict(service_api_description.get("supported_features"))["ApiStatusMonitoring"] and service_api_description.get("api_status", None) is not None: return bad_request_error( detail="Set apiStatus with apiStatusMonitoring feature inactive at supportedFeatures if not allowed", cause="apiStatus can't be set if apiStatusMonitoring is inactive", @@ -382,11 +404,14 @@ class PublishServiceOperations(Resource): patch_service_api_description = patch_service_api_description.to_dict() api_status = patch_service_api_description.get("api_status", None) + supported_features = patch_service_api_description.get("supported_features", None) patch_service_api_description = clean_empty(patch_service_api_description) if api_status: patch_service_api_description["api_status"]=api_status + if supported_features: + patch_service_api_description["supported_features"] = return_negotiated_supp_feat_dict(patch_service_api_description["supported_features"])["Final"] - if not ServiceAPIDescription.return_supp_feat_dict(serviceapidescription_old.get("supported_features"))["ApiStatusMonitoring"] and patch_service_api_description.get("api_status", None) is not None: + if not return_negotiated_supp_feat_dict(serviceapidescription_old.get("supported_features"))["ApiStatusMonitoring"] and patch_service_api_description.get("api_status", None) is not None: return bad_request_error( detail="Set apiStatus with apiStatusMonitoring feature inactive at supportedFeatures if not allowed", cause="apiStatus can't be set if apiStatusMonitoring is inactive", @@ -471,7 +496,7 @@ class PublishServiceOperations(Resource): def service_api_availability_event(self, service_api_description): service_api_status = "" - if ServiceAPIDescription.return_supp_feat_dict(service_api_description.get("supportedFeatures"))["ApiStatusMonitoring"]: + if return_negotiated_supp_feat_dict(service_api_description.get("supportedFeatures"))["ApiStatusMonitoring"]: current_app.logger.info( "ApiStatusMonitoring active") if service_api_description.get("apiStatus") is None or len(service_api_description.get("apiStatus").get("aefIds")) > 0: diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/models/service_api_description.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/models/service_api_description.py index 0111b908..3f97250d 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/models/service_api_description.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/models/service_api_description.py @@ -87,25 +87,6 @@ class ServiceAPIDescription(Model): self._ccf_id = ccf_id self._api_prov_name = api_prov_name - @classmethod - def return_supp_feat_dict(cls, supp_feat): - TOTAL_FEATURES = 9 - supp_feat_in_hex = int(supp_feat, 16) - supp_feat_in_bin = bin(supp_feat_in_hex)[2:].zfill(TOTAL_FEATURES)[::-1] - - - return { - "ApiSupportedFeaturePublishing": True if supp_feat_in_bin[0] == "1" else False, - "PatchUpdate": True if supp_feat_in_bin[1] == "1" else False, - "ExtendedIntfDesc": True if supp_feat_in_bin[2] == "1" else False, - "MultipleCustomOperations": True if supp_feat_in_bin[3] == "1" else False, - "ProtocDataFormats_Ext1": True if supp_feat_in_bin[4] == "1" else False, - "ApiStatusMonitoring": True if supp_feat_in_bin[5] == "1" else False, - "EdgeApp_2": True if supp_feat_in_bin[6] == "1" else False, - "RNAA": True if supp_feat_in_bin[7] == "1" else False, - "VendorExt": True if supp_feat_in_bin[8] == "1" else False - } - @classmethod def from_dict(cls, dikt) -> 'ServiceAPIDescription': -- GitLab From 8bec2f1d93d2ffe39ec30a5302f0db9352638866 Mon Sep 17 00:00:00 2001 From: Stavros-Anastasios Charismiadis Date: Thu, 12 Jun 2025 12:13:05 +0300 Subject: [PATCH 2/4] Add zfill to negotiation function and edit supportedFeatures in tests to contain 3 characters --- .../published_apis/core/serviceapidescriptions.py | 8 +++++++- tests/features/Api Status/api_status.robot | 6 +++--- tests/features/Event Filter/event_filter.robot | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py index 15f04886..cd1ab91b 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py @@ -45,7 +45,7 @@ def return_negotiated_supp_feat_dict(supp_feat): "RNAA": True if final_supp_feat[7] == "1" else False, "VendorExt": True if final_supp_feat[8] == "1" else False, "SliceBasedAPIExposure": True if final_supp_feat[9] == "1" else False, - "Final": hex(int(final_supp_feat[::-1], 2))[2:] + "Final": hex(int(final_supp_feat[::-1], 2))[2:].zfill(3) } class PublishServiceOperations(Resource): @@ -317,7 +317,13 @@ class PublishServiceOperations(Resource): service_api_description["apf_id"] = serviceapidescription_old["apf_id"] service_api_description["onboarding_date"] = serviceapidescription_old["onboarding_date"] service_api_description["api_id"] = serviceapidescription_old["api_id"] + current_app.logger.debug("************** Diff of suppfeat **************") + current_app.logger.debug(service_api_description["supported_features"]) service_api_description["supported_features"] = return_negotiated_supp_feat_dict(service_api_description["supported_features"])["Final"] + current_app.logger.debug(service_api_description["supported_features"]) + current_app.logger.debug(service_api_description["supported_features"]) + current_app.logger.debug(service_api_description.get("supported_features")) + current_app.logger.debug("************** Diff of suppfeat **************") if not return_negotiated_supp_feat_dict(service_api_description.get("supported_features"))["ApiStatusMonitoring"] and service_api_description.get("api_status", None) is not None: return bad_request_error( diff --git a/tests/features/Api Status/api_status.robot b/tests/features/Api Status/api_status.robot index 9e9f92f9..d4798e76 100644 --- a/tests/features/Api Status/api_status.robot +++ b/tests/features/Api Status/api_status.robot @@ -1155,7 +1155,7 @@ Update published API with apiStatus empty and apiStatusMonitoring active ${service_api_description_modified}= Create Service Api Description ... service_1 ... aef_id=${aef_ids} - ... supported_features=20 + ... supported_features=020 ... api_status=${aef_empty_list} ${resp}= Put Request Capif ... ${resource_url.path} @@ -1260,7 +1260,7 @@ Update published API with apiStatus only aef2 and apiStatusMonitoring active ${service_api_description_modified}= Create Service Api Description ... service_1 ... aef_id=${aef_ids} - ... supported_features=20 + ... supported_features=020 ... api_status=${aef_id_2} ${resp}= Put Request Capif ... ${resource_url.path} @@ -1357,7 +1357,7 @@ Published API without aefs available updated to one aef available ${service_api_description_modified}= Create Service Api Description ... service_1 ... aef_id=${aef_ids} - ... supported_features=20 + ... supported_features=020 ... api_status=${aef_id_2} ${resp}= Put Request Capif ... ${resource_url.path} diff --git a/tests/features/Event Filter/event_filter.robot b/tests/features/Event Filter/event_filter.robot index 95f94310..9853646b 100644 --- a/tests/features/Event Filter/event_filter.robot +++ b/tests/features/Event Filter/event_filter.robot @@ -84,7 +84,7 @@ Invoker subscribed to SERVICE_API_AVAILABLE, SERVICE_API_UNAVAILABLE and SERVICE ${service_api_description_modified}= Create Service Api Description ... service_1 ... aef_id=${aef_ids} - ... supported_features=20 + ... supported_features=020 ... api_status=${aef_ids} ${resp}= Put Request Capif ... ${resource_url.path} -- GitLab From 12ee9e1d8bf9a5836794732f37d553fff705621d Mon Sep 17 00:00:00 2001 From: Stavros-Anastasios Charismiadis Date: Fri, 13 Jun 2025 11:25:19 +0300 Subject: [PATCH 3/4] Fix robot file of capif event to take the response from the publish api when creating an event --- tests/features/CAPIF Api Events/capif_events_api.robot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/features/CAPIF Api Events/capif_events_api.robot b/tests/features/CAPIF Api Events/capif_events_api.robot index 4b37d0a6..941cb6d0 100644 --- a/tests/features/CAPIF Api Events/capif_events_api.robot +++ b/tests/features/CAPIF Api Events/capif_events_api.robot @@ -344,7 +344,7 @@ Invoker subscribe to Service API Update ${events_expected}= Create Expected Service Update Event ... ${subscription_id} ... ${resource_url} - ... ${service_api_description_modified} + ... ${resp.json()} ## Check Events Expected towards received notifications at mock server Wait Until Keyword Succeeds 5x 5s Check Mock Server Notification Events ${events_expected} -- GitLab From 6214d3cd4bdd37366b080f12af8d13a0ea71e6bd Mon Sep 17 00:00:00 2001 From: Stavros-Anastasios Charismiadis Date: Mon, 23 Jun 2025 09:48:21 +0300 Subject: [PATCH 4/4] Set supported_features equal to 000 for the Publish API test running --- .../published_apis/core/serviceapidescriptions.py | 6 ------ tests/features/CAPIF Api Events/capif_events_api.robot | 2 +- tests/libraries/api_publish_service/bodyRequests.py | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py index cd1ab91b..658957ef 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/serviceapidescriptions.py @@ -317,13 +317,7 @@ class PublishServiceOperations(Resource): service_api_description["apf_id"] = serviceapidescription_old["apf_id"] service_api_description["onboarding_date"] = serviceapidescription_old["onboarding_date"] service_api_description["api_id"] = serviceapidescription_old["api_id"] - current_app.logger.debug("************** Diff of suppfeat **************") - current_app.logger.debug(service_api_description["supported_features"]) service_api_description["supported_features"] = return_negotiated_supp_feat_dict(service_api_description["supported_features"])["Final"] - current_app.logger.debug(service_api_description["supported_features"]) - current_app.logger.debug(service_api_description["supported_features"]) - current_app.logger.debug(service_api_description.get("supported_features")) - current_app.logger.debug("************** Diff of suppfeat **************") if not return_negotiated_supp_feat_dict(service_api_description.get("supported_features"))["ApiStatusMonitoring"] and service_api_description.get("api_status", None) is not None: return bad_request_error( diff --git a/tests/features/CAPIF Api Events/capif_events_api.robot b/tests/features/CAPIF Api Events/capif_events_api.robot index 941cb6d0..4b37d0a6 100644 --- a/tests/features/CAPIF Api Events/capif_events_api.robot +++ b/tests/features/CAPIF Api Events/capif_events_api.robot @@ -344,7 +344,7 @@ Invoker subscribe to Service API Update ${events_expected}= Create Expected Service Update Event ... ${subscription_id} ... ${resource_url} - ... ${resp.json()} + ... ${service_api_description_modified} ## Check Events Expected towards received notifications at mock server Wait Until Keyword Succeeds 5x 5s Check Mock Server Notification Events ${events_expected} diff --git a/tests/libraries/api_publish_service/bodyRequests.py b/tests/libraries/api_publish_service/bodyRequests.py index 046e8e18..5f969a54 100644 --- a/tests/libraries/api_publish_service/bodyRequests.py +++ b/tests/libraries/api_publish_service/bodyRequests.py @@ -1,6 +1,6 @@ def create_service_api_description(api_name="service_1", aef_id="aef_id", - supported_features="0", + supported_features="000", vendor_specific_service_api_description=None, vendor_specific_aef_profile=None, api_status=None, -- GitLab