Ocf175 how to add new api to helper service 2

Proposers

  • Pelayo Torres (Telefonica)

Description

New improvements in the helper service

  • Db access with singleton
  • Script to genarate from an OpenAPI file, new service for the helper

Acknowledgements

This work is funded by the European Commission through the project with Grant Agreement number . example HORIZON-JU-SNS-2022 FLEX-SCALE project with Grant Agreement number 101096909.

Merge request reports

Loading
+11 −0
Changes for helm/capif/charts/ocf-helper/templates/ocf-helper-configmap.yaml: 11 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -27,3 +27,14 @@ data:
    {{- if .Values.capifConfiguration }}
    capif_configuration: {{ .Values.capifConfiguration | toYaml | nindent 6 }}
    {{- end }}

    package_paths: {
      "helper_api": {
        "path": "/api",
        "openapi_file": "api/openapi/openapi.yaml"
      },
      "configuration_api": {
        "path": "/configuration",
        "openapi_file": "configuration/openapi/openapi.yaml"
      }
    }
 No newline at end of file
+0 −199
Changes for services/helper/helper_service/controllers/helper_controller.py: 0 added lines, 199 removed lines.
Original line number Diff line number Diff line
#!/usr/bin/env python3

from config import Config
from core.helper_operations import HelperOperations
from flask import Blueprint, current_app, jsonify, request

config = Config().get_config()

helper_routes = Blueprint("helper_routes", __name__)
helper_operation = HelperOperations()

@helper_routes.route("/helper/getInvokers", methods=["GET"])
def getInvokers():
    uuid = request.args.get('uuid')
    invoker_id = request.args.get('api_invoker_id')
    page_size = request.args.get('page_size')
    page = request.args.get('page')
    sort_order = request.args.get('sort_order')
    if page_size:
        page_size = int(page_size)
        if page_size < 0:
            return jsonify(message="The value of the page_size parameter must be greater than 0"), 400
    if page:
        page = int(page)
        if page < 0:
            return jsonify(message="The value of the page parameter must be greater than 0"), 400
    
    current_app.logger.debug(f"uuid: {uuid}, invoker_id: {invoker_id}, page: {page}, page_size: {page_size}, sort_order: {sort_order}")

    return helper_operation.get_invokers(uuid, invoker_id, page, page_size, sort_order)

@helper_routes.route("/helper/getProviders", methods=["GET"])
def getProviders():
    uuid = request.args.get('uuid')
    provider_id = request.args.get('api_prov_dom_id')
    page_size = request.args.get('page_size')
    page = request.args.get('page')
    sort_order = request.args.get('sort_order')

    if page_size:
        page_size = int(page_size)
        if page_size < 0:
            return jsonify(message="The value of the page_size parameter must be greater than 0"), 400
    if page:
        page = int(page)
        if page < 0:
            return jsonify(message="The value of the page parameter must be greater than 0"), 400
    
    current_app.logger.debug(f"uuid: {uuid}, provider_id: {provider_id}, page: {page}, page_size: {page_size}, sort_order: {sort_order}")

    return helper_operation.get_providers(uuid, provider_id, page, page_size, sort_order)


@helper_routes.route("/helper/getServices", methods=["GET"])
def getServices():
    service_id = request.args.get('service_id')
    apf_id = request.args.get('apf_id')
    api_name = request.args.get('api_name')
    page_size = request.args.get('page_size')
    page = request.args.get('page')
    sort_order = request.args.get('sort_order')
    if page_size:
        page_size = int(page_size)
        if page_size < 0:
            return jsonify(message="The value of the page_size parameter must be greater than 0"), 400
    if page:
        page = int(page)
        if page < 0:
            return jsonify(message="The value of the page parameter must be greater than 0"), 400
    
    current_app.logger.debug(f"service_id: {service_id}, apf_id: {apf_id}, api_name: {api_name}, page: {page}, page_size: {page_size}, sort_order: {sort_order}")

    return helper_operation.get_services(service_id, apf_id, api_name, page, page_size, sort_order)

@helper_routes.route("/helper/getSecurity", methods=["GET"])
def getSecurity():
    invoker_id = request.args.get('invoker_id')
    page_size = request.args.get('page_size')
    page = request.args.get('page')
    if page_size:
        page_size = int(page_size)
        if page_size < 0:
            return jsonify(message="The value of the page_size parameter must be greater than 0"), 400
    if page:
        page = int(page)
        if page < 0:
            return jsonify(message="The value of the page parameter must be greater than 0"), 400
    
    current_app.logger.debug(f"invoker_id: {invoker_id}, page: {page}, page_size: {page_size} ")

    return helper_operation.get_security(invoker_id, page, page_size)

@helper_routes.route("/helper/getEvents", methods=["GET"])
def getEvents():
    subscriber_id = request.args.get('subscriber_id')
    subscription_id = request.args.get('subscription_id')
    page_size = request.args.get('page_size')
    page = request.args.get('page')
    if page_size:
        page_size = int(page_size)
        if page_size < 0:
            return jsonify(message="The value of the page_size parameter must be greater than 0"), 400
    if page:
        page = int(page)
        if page < 0:
            return jsonify(message="The value of the page parameter must be greater than 0"), 400
    
    current_app.logger.debug(f"subscriber_id: {subscriber_id}, subscription_id: {subscription_id}, page: {page}, page_size: {page_size} ")

    return helper_operation.get_events(subscriber_id, subscription_id, page, page_size)


@helper_routes.route("/helper/deleteEntities/<uuid>", methods=["DELETE"])
def deleteUserEntities(uuid):
    return helper_operation.remove_entities(uuid)


@helper_routes.route("/helper/getConfiguration", methods=["GET"])
def getConfiguration():
    """Returns the current configuration"""
    return helper_operation.get_configuration()


@helper_routes.route("/helper/updateConfigParam", methods=["PATCH"])
def updateConfigParam():
    """Updates a single configuration parameter"""
    data = request.json
    param_path = data.get("param_path")
    new_value = data.get("new_value")

    if not param_path or new_value is None:
        return jsonify(message="Missing 'param_path' or 'new_value' in request body"), 400

    return helper_operation.update_config_param(param_path, new_value)


@helper_routes.route("/helper/replaceConfiguration", methods=["PUT"])
def replaceConfiguration():
    """Replaces the entire configuration with a new one"""
    new_config = request.json
    if not new_config:
        return jsonify(message="Missing new configuration in request body"), 400

    return helper_operation.replace_configuration(new_config)


@helper_routes.route("/helper/addNewConfiguration", methods=["POST"])
def add_new_configuration():
    """Adds a new category inside 'settings'."""
    data = request.json
    category_name = data.get("category_name")
    category_values = data.get("category_values")

    if not category_name or not category_values:
        return jsonify(message="Missing 'category_name' or 'category_values' in request body"), 400

    return helper_operation.add_new_configuration(category_name, category_values)

@helper_routes.route("/helper/addNewConfigSetting", methods=["PATCH"])
def add_new_config_setting():
    """Adds a new configuration inside 'settings'."""
    data = request.json
    param_path = data.get("param_path")
    new_value = data.get("new_value")
    
    if not param_path or new_value is None:
        return jsonify(message="Missing 'param_path' or 'new_value' in request body"), 400
    
    return helper_operation.add_new_config_setting(param_path, new_value)


@helper_routes.route("/helper/removeConfigParam", methods=["DELETE"])
def remove_config_param():
    """Deletes a specific parameter inside 'settings'."""
    data = request.json
    param_path = data.get("param_path")

    if not param_path:
        return jsonify(message="Missing 'param_path' in request body"), 400

    return helper_operation.remove_config_param(param_path)


@helper_routes.route("/helper/removeConfigCategory", methods=["DELETE"])
def remove_config_category():
    """Deletes an entire category inside 'settings'."""
    data = request.json
    category_name = data.get("category_name")

    if not category_name:
        return jsonify(message="Missing 'category_name' in request body"), 400

    return helper_operation.remove_config_category(category_name)


@helper_routes.route("/helper/getCcfId", methods=["GET"])
def get_ccf_id():
    """Returns the current CAPIF ccfId"""
    return helper_operation.get_ccf_id()
+27 −1
Changes for services/helper/helper_service/db/db.py: 27 added lines, 1 removed line.
Original line number Diff line number Diff line
import time
import secrets
from threading import Lock

from bson.codec_options import CodecOptions
from config import Config
@@ -9,7 +10,26 @@ from pymongo.errors import AutoReconnect

class MongoDatabse():

    def __init__(self):
    _instance = None
    _lock = Lock()

    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:  # double-checked
                    cls._instance = super().__new__(cls)
                    cls._instance._init_once()
        return cls._instance

    def close(self):
        if getattr(self, "_client", None):
            self._client.close()
            self._client = None

    def get_col_by_name(self, name):
        return self.db[name].with_options(codec_options=CodecOptions(tz_aware=True))

    def _init_once(self):
        self.config = Config().get_config()
        self.db = self.__connect()
        self.invoker_col = self.config['mongo']['invoker_col']
@@ -78,3 +98,9 @@ class MongoDatabse():
                print("Capif_configuration already contains data with a unique ccf_id. No default values inserted.")


_singleton = None
def get_mongo():
    global _singleton
    if _singleton is None:
        _singleton = MongoDatabse()
    return _singleton
 No newline at end of file
+0 −0

Empty file added.

+141 −0
Changes for services/helper/helper_service/services/api/controllers/default_controller.py: 141 added lines, 0 removed lines.
Original line number Diff line number Diff line
import connexion
from typing import Dict
from typing import Tuple
from typing import Union

from api.models.error_response import ErrorResponse  # noqa: E501
from api.models.paginated_response_event import PaginatedResponseEvent  # noqa: E501
from api.models.paginated_response_invoker import PaginatedResponseInvoker  # noqa: E501
from api.models.paginated_response_provider import PaginatedResponseProvider  # noqa: E501
from api.models.paginated_response_security import PaginatedResponseSecurity  # noqa: E501
from api.models.paginated_response_service import PaginatedResponseService  # noqa: E501
from api import util

from ..core.helper_operations import HelperOperations


helper_operations = HelperOperations()

def helper_controller_delete_entities(uuid):  # noqa: E501
    """Delete entities by UUID

    Deletes all CAPIF entities (invokers, providers, services, security contexts, events) associated with the given UUID. # noqa: E501

    :param uuid: UUID of the user whose entities are to be deleted.
    :type uuid: str

    :rtype: Union[None, Tuple[None, int], Tuple[None, int, Dict[str, str]]
    """
    return helper_operations.remove_entities(uuid)


def helper_controller_get_ccf_id():  # noqa: E501
    """Get CCF ID

    Retrieves the CCF ID of the CAPIF Core Function. # noqa: E501


    :rtype: Union[None, Tuple[None, int], Tuple[None, int, Dict[str, str]]
    """
    return helper_operations.get_ccf_id()


def helper_controller_get_events(subscriber_id=None, subscription_id=None, page_size=None, page=None):  # noqa: E501
    """Retrieve CAPIF events

    Returns CAPIF event subscriptions or delivered events. # noqa: E501

    :param subscriber_id: Filter by subscriber identifier.
    :type subscriber_id: str
    :param subscription_id: Filter by subscription identifier.
    :type subscription_id: str
    :param page_size: Page size.
    :type page_size: int
    :param page: Page index (0-based).
    :type page: int

    :rtype: Union[PaginatedResponseEvent, Tuple[PaginatedResponseEvent, int], Tuple[PaginatedResponseEvent, int, Dict[str, str]]
    """
    return helper_operations.get_events(subscriber_id, subscription_id, page_size, page)


def helper_controller_get_invokers(uuid=None, api_invoker_id=None, page_size=None, page=None, sort_order=None):  # noqa: E501
    """Retrieve API invokers

    Returns invoker entries with pagination and optional filters. # noqa: E501

    :param uuid: Filter by invoker UUID.
    :type uuid: str
    :param api_invoker_id: Filter by CAPIF &#x60;apiInvokerId&#x60;.
    :type api_invoker_id: str
    :param page_size: Page size.
    :type page_size: int
    :param page: Page index (0-based).
    :type page: int
    :param sort_order: Sort direction.
    :type sort_order: str

    :rtype: Union[PaginatedResponseInvoker, Tuple[PaginatedResponseInvoker, int], Tuple[PaginatedResponseInvoker, int, Dict[str, str]]
    """
    return helper_operations.get_invokers(uuid, api_invoker_id, page_size, page, sort_order)


def helper_controller_get_providers(uuid=None, api_prov_dom_id=None, page_size=None, page=None, sort_order=None):  # noqa: E501
    """Retrieve providers

    Returns provider domains (CAPIF provider domains / AEF providers) with pagination. # noqa: E501

    :param uuid: Filter by provider UUID.
    :type uuid: str
    :param api_prov_dom_id: Filter by provider domain ID.
    :type api_prov_dom_id: str
    :param page_size: Page size.
    :type page_size: int
    :param page: Page index (0-based).
    :type page: int
    :param sort_order: Sort direction.
    :type sort_order: str

    :rtype: Union[PaginatedResponseProvider, Tuple[PaginatedResponseProvider, int], Tuple[PaginatedResponseProvider, int, Dict[str, str]]
    """
    return helper_operations.get_providers(uuid, api_prov_dom_id, page_size, page, sort_order)


def helper_controller_get_security(invoker_id=None, page_size=None, page=None):  # noqa: E501
    """Retrieve security associations

    Returns security credentials/bindings for a given invoker. # noqa: E501

    :param invoker_id: Filter by invoker identifier.
    :type invoker_id: str
    :param page_size: Page size.
    :type page_size: int
    :param page: Page index (0-based).
    :type page: int

    :rtype: Union[PaginatedResponseSecurity, Tuple[PaginatedResponseSecurity, int], Tuple[PaginatedResponseSecurity, int, Dict[str, str]]
    """
    return helper_operations.get_security(invoker_id, page_size, page)


def helper_controller_get_services(service_id=None, apf_id=None, api_name=None, page_size=None, page=None, sort_order=None):  # noqa: E501
    """Retrieve services

    Returns published APIs/services exposed by providers. # noqa: E501

    :param service_id: Filter by service identifier.
    :type service_id: str
    :param apf_id: Filter by APF identifier.
    :type apf_id: str
    :param api_name: Filter by API name.
    :type api_name: str
    :param page_size: Page size.
    :type page_size: int
    :param page: Page index (0-based).
    :type page: int
    :param sort_order: Sort direction.
    :type sort_order: str

    :rtype: Union[PaginatedResponseService, Tuple[PaginatedResponseService, int], Tuple[PaginatedResponseService, int, Dict[str, str]]
    """
    return helper_operations.get_services(service_id, apf_id, api_name, page_size, page, sort_order)
Loading
Loading