diff --git a/opencapif_sdk/__init__.py b/opencapif_sdk/__init__.py index b977cb1451bf04b91f3b305bfd736519ca01c76a..46df6bfbc0707d97ffbb437a5de547f6dd615205 100644 --- a/opencapif_sdk/__init__.py +++ b/opencapif_sdk/__init__.py @@ -3,5 +3,6 @@ from opencapif_sdk.capif_provider_connector import capif_provider_connector from opencapif_sdk.service_discoverer import service_discoverer from opencapif_sdk.api_schema_translator import api_schema_translator from opencapif_sdk.capif_logging_feature import capif_logging_feature +from opencapif_sdk.capif_event_feature import capif_invoker_event_feature -__all__ = ["capif_invoker_connector", "service_discoverer", "capif_provider_connector", "api_schema_translator", "capif_logging_feature"] \ No newline at end of file +__all__ = ["capif_invoker_connector", "service_discoverer", "capif_provider_connector", "api_schema_translator", "capif_logging_feature","capif_invoker_event_feature"] \ No newline at end of file diff --git a/opencapif_sdk/capif_event_feature.py b/opencapif_sdk/capif_event_feature.py index 3300d63b318c2b420888ab1763169b206e96ad7b..14263803a53905616010e238895998755bbdee1b 100644 --- a/opencapif_sdk/capif_event_feature.py +++ b/opencapif_sdk/capif_event_feature.py @@ -1,5 +1,4 @@ -from capif_invoker_connector import capif_invoker_connector -from capif_provider_connector import capif_provider_connector +from opencapif_sdk import capif_invoker_connector import os import logging import shutil @@ -39,42 +38,80 @@ logging.basicConfig( ] ) + class capif_invoker_event_feature(capif_invoker_connector): - def create_subscription: + def create_subscription(self): - invoker_capif_details = self.__load_invoker_api_details() + invoker_capif_details = self.invoker_capif_details subscriberId = invoker_capif_details["api_invoker_id"] - path = self.capif_https_url + f"/{subscriberId}/subscriptions" - - payload = self.events_config - - + path = self.capif_https_url + f"capif-events/v1/{subscriberId}/subscriptions" + + payload = { + "events": self.events_description, + "eventFilters": self.events_filter, + "eventReq": {}, # TO IMPROVE !!! + "notificationDestination": f"{self.capif_callback_url}", + "requestTestNotification": True, + "websockNotifConfig": { + "websocketUri": f"{self.capif_callback_url}", + "requestWebsocketUri": True + }, + "supportedFeatures": f"{self.supported_features}" + } try: response = requests.post( url=path, json=payload, headers={"Content-Type": "application/json"}, - cert=cert, + cert=(self.signed_key_crt_path, self.private_key_path), verify=os.path.join(self.invoker_folder, "ca.crt") ) - response.raise_for_status() - - return response.status_code, response.json() + location_header = response.headers.get("Location") + + if location_header: + # Extrae el identificador de la URL en el encabezado 'Location' + identifier = location_header.rstrip('/').split('/')[-1] + self.logger.info(f"Subscriptionid obtained: {identifier}") + else: + self.logger.error("The Location header is not available in the response") + + path = os.path.join(self.invoker_folder, "capif_subscriptions_id.json") + + # Load or initialize the subscription list + if os.path.exists(path): + subscription = self._load_config_file(path) + # Ensure subscription is a list + if not isinstance(subscription, list): + raise TypeError(f"Expected 'subscription' to be a list, but got {type(subscription).__name__}") + else: + subscription = [] + + # Find if the subscriberId already exists in the list + subscriber_entry = next((item for item in subscription if item.get("subscriberId") == subscriberId), None) + + if subscriber_entry is None: + # If subscriberId is not found, create a new entry + subscriber_entry = {"subscriberId": subscriberId, "events": []} + subscription.append(subscriber_entry) + + # Add the event to the subscriber's events list + subscriber_entry["events"].append({self.events_description: identifier}) + + # Save the updated list back to the file + self._create_or_update_file("capif_subscriptions_id", "json", subscription, "w") except Exception as e: self.logger.error("Unexpected error: %s", e) return None, {"error": f"Unexpected error: {e}"} + # def delete_subcription(self): - def delete_subcription: - - def modify_subcription: + # def modify_subcription(self): - def patch_subcription: + # def patch_subcription(self): - \ No newline at end of file diff --git a/opencapif_sdk/capif_invoker_connector.py b/opencapif_sdk/capif_invoker_connector.py index 678afbe1ff4a759b9f05292b0b1c7089011d554a..e04ccb1ac4f9b75ba09c3e7b8bfcac0b13ea3638 100644 --- a/opencapif_sdk/capif_invoker_connector.py +++ b/opencapif_sdk/capif_invoker_connector.py @@ -46,7 +46,7 @@ class capif_invoker_connector: config_file = os.path.abspath(config_file) # Load configuration from file if necessary - config = self.__load_config_file(config_file) + config = self._load_config_file(config_file) debug_mode = os.getenv('DEBUG_MODE', config.get('debug_mode', 'False')).strip().lower() if debug_mode == "false": @@ -101,7 +101,8 @@ class capif_invoker_connector: # Events configuration events_config = invoker_config.get('events', {}) - self.events_config = events_config + self.events_description = os.getenv('INVOKER_EVENTS_DESCRIPTION', events_config.get('description', '')) + self.events_filter = os.getenv('INVOKER_EVENTS_FILTERS', events_config.get('eventFilters', '')) # Define the invoker folder path and create it if it doesn't exist self.invoker_folder = os.path.join(invoker_general_folder, capif_username) @@ -141,10 +142,22 @@ class capif_invoker_connector: ) if os.path.exists(path): self.invoker_capif_details = self.__load_invoker_api_details() + + self.signed_key_crt_path = os.path.join( + self.invoker_folder, + self.capif_username + ".crt" + ) + + self.private_key_path = os.path.join( + self.invoker_folder, + "private.key" + ) + + self.pathca = os.path.join(self.invoker_folder, "ca.crt") self.logger.info("capif_invoker_connector initialized with the JSON parameters") - def __load_config_file(self, config_file: str): + def _load_config_file(self, config_file: str): """Loads the configuration file.""" try: with open(config_file, 'r') as file: @@ -198,25 +211,11 @@ class capif_invoker_connector: + invoker_capif_details["api_invoker_id"] ) - signed_key_crt_path = os.path.join( - self.invoker_folder, - invoker_capif_details["user_name"] + ".crt" - ) - - private_key_path = os.path.join( - self.invoker_folder, - "private.key" - ) - - path = os.path.join( - self.invoker_folder, - "ca.crt" - ) response = requests.request( "DELETE", url, - cert=(signed_key_crt_path, private_key_path), - verify=path, + cert=(self.signed_key_crt_path, self.private_key_path), + verify=self.pathca, ) response.raise_for_status() self.logger.info("Invoker offboarded successfully") @@ -241,7 +240,6 @@ class capif_invoker_connector: self.logger.info( "Creating private and public keys for the Invoker cert") try: - private_key_path = os.path.join(self.invoker_folder, "private.key") csr_file_path = os.path.join(self.invoker_folder, "cert_req.csr") @@ -262,7 +260,7 @@ class capif_invoker_connector: with open(csr_file_path, "wb+") as f: f.write(dump_certificate_request(FILETYPE_PEM, req)) public_key = dump_certificate_request(FILETYPE_PEM, req) - with open(private_key_path, "wb+") as f: + with open(self.private_key_path, "wb+") as f: f.write(dump_privatekey(FILETYPE_PEM, key)) self.logger.info("Keys created successfully") @@ -309,7 +307,7 @@ class capif_invoker_connector: response.raise_for_status() response_payload = json.loads(response.text) - ca_root_file_path = os.path.join(self.invoker_folder, "ca.crt") + ca_root_file_path = self.pathca ca_root_file = open(ca_root_file_path, "wb+") ca_root_file.write(bytes(response_payload["ca_root"], "utf-8")) self.logger.info( @@ -343,13 +341,12 @@ class capif_invoker_connector: "Authorization": "Bearer {}".format(capif_access_token), "Content-Type": "application/json", } - pathca = os.path.join(self.invoker_folder, "ca.crt") response = requests.request( "POST", url, headers=headers, data=payload, - verify=pathca, + verify=self.pathca, ) response.raise_for_status() response_payload = json.loads(response.text) @@ -447,23 +444,14 @@ class capif_invoker_connector: "Authorization": "Bearer {}".format(capif_access_token), "Content-Type": "application/json", } - signed_key_crt_path = os.path.join( - self.invoker_folder, - self.capif_username + ".crt" - ) - private_key_path = os.path.join( - self.invoker_folder, - "private.key" - ) - pathca = os.path.join(self.invoker_folder, "ca.crt") response = requests.request( "PUT", url, headers=headers, data=payload, - cert=(signed_key_crt_path, private_key_path), - verify=pathca, + cert=(self.signed_key_crt_path, self.private_key_path), + verify=self.pathca, ) response.raise_for_status() @@ -475,6 +463,50 @@ class capif_invoker_connector: self.logger.error( f"Error during updating Invoker to CAPIF: {e} - Response: {response.text}") raise + + def _create_or_update_file(self, file_name, file_type, content, mode="w"): + """ + Create or update a file with the specified content. + + :param file_name: Name of the file (without extension). + :param file_type: File type or extension (e.g., "txt", "json", "html"). + :param content: Content to write into the file. Can be a string, dictionary, or list. + :param mode: Write mode ('w' to overwrite, 'a' to append). Default is 'w'. + """ + # Validate the mode + if mode not in ["w", "a"]: + raise ValueError("Mode must be 'w' (overwrite) or 'a' (append).") + + # Construct the full file name + full_file_name = f"{file_name}.{file_type}" + full_path = os.path.join(self.invoker_folder, full_file_name) + + # Ensure the content is properly formatted + if isinstance(content, (dict, list)): + if file_type == "json": + try: + # Serialize content to JSON + content = json.dumps(content, indent=4) + except TypeError as e: + raise ValueError(f"Failed to serialize content to JSON: {e}") + else: + raise TypeError("Content must be a string when the file type is not JSON.") + elif not isinstance(content, str): + raise TypeError("Content must be a string, dictionary, or list.") + + try: + # Open the file in the specified mode + with open(full_path, mode, encoding="utf-8") as file: + file.write(content) + + # Log success based on the mode + if mode == "w": + self.logger.info(f"File '{full_file_name}' created or overwritten successfully.") + elif mode == "a": + self.logger.info(f"Content appended to file '{full_file_name}' successfully.") + except Exception as e: + self.logger.error(f"Error handling the file '{full_file_name}': {e}") + raise diff --git a/scripts/invoker_capif_event_subcription.py b/scripts/invoker_capif_event_subcription.py new file mode 100644 index 0000000000000000000000000000000000000000..d3ffec82038f8c4b12dec0e5e100d4d88afa25c0 --- /dev/null +++ b/scripts/invoker_capif_event_subcription.py @@ -0,0 +1,21 @@ +import utilities +from opencapif_sdk import capif_invoker_connector, capif_invoker_event_feature + +def showcase_capif_connector(): + """ + This method showcases how one can use the CAPIFConnector class. + """ + + capif_connector = capif_invoker_connector(config_file=utilities.get_config_file()) + + capif_connector.onboard_invoker() + + events = capif_invoker_event_feature(config_file=utilities.get_config_file()) + + events.create_subscription() + print("COMPLETED") + + +if __name__ == "__main__": + # Register invoker to CAPIF. This should happen exactly once + showcase_capif_connector() diff --git a/scripts/utilities.py b/scripts/utilities.py index 238f969ea14f4fefe6ce480a856f4c94046c5c1d..0b4855f7c45b122119cfaceba01094038ff2e33f 100755 --- a/scripts/utilities.py +++ b/scripts/utilities.py @@ -1,7 +1,7 @@ def get_config_file() -> str: - return "../config/capif_sdk_config.json" + return "../test/capif_sdk_config_sample_test.json" def get_register_file() -> str: