Commit f1a17e69 authored by Jorge Moratinos's avatar Jorge Moratinos
Browse files

Merge branch 'OCF175-how-to-add-new-api-to-helper-service-2' into 'staging'

Ocf175 how to add new api to helper service 2

See merge request !160
parents 266e8080 60a530da
Loading
Loading
Loading
Loading
Loading
+12 −1
Original line number Original line Diff line number Diff line
@@ -27,3 +27,14 @@ data:
    {{- if .Values.capifConfiguration }}
    {{- if .Values.capifConfiguration }}
    capif_configuration: {{ .Values.capifConfiguration | toYaml | nindent 6 }}
    capif_configuration: {{ .Values.capifConfiguration | toYaml | nindent 6 }}
    {{- end }}
    {{- 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
+1 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ services:
    restart: unless-stopped
    restart: unless-stopped
    volumes:
    volumes:
      - ${SERVICES_DIR}/helper/config.yaml:/usr/src/app/config.yaml
      - ${SERVICES_DIR}/helper/config.yaml:/usr/src/app/config.yaml
      - ${SERVICES_DIR}/helper/helper_service/certs:/usr/src/app/helper_service/certs
    extra_hosts:
    extra_hosts:
      - host.docker.internal:host-gateway
      - host.docker.internal:host-gateway
      - fluent-bit:host-gateway
      - fluent-bit:host-gateway
+28 −19
Original line number Original line Diff line number Diff line
mongo: {
mongo:
  'user': 'root',
  user: root
  'password': 'example',
  password: example
  'db': 'capif',
  db: capif
  'invoker_col': 'invokerdetails',
  invoker_col: invokerdetails
  'provider_col': 'providerenrolmentdetails',
  provider_col: providerenrolmentdetails
  'col_services': "serviceapidescriptions",
  col_services: serviceapidescriptions
  'col_security': "security",
  col_security: security
  'col_event': "eventsdetails",
  col_event: eventsdetails
  'col_capif_configuration': "capif_configuration",
  col_capif_configuration: capif_configuration
  'host': 'mongo',
  host: mongo
  'port': "27017"
  port: 27017
}


ca_factory: 
  url: vault
  port: 8200
  token: dev-only-token
  verify: False


ca_factory: {
  "url": "vault",
  "port": "8200",
  "token": "dev-only-token",
  "verify": False
}


capif_configuration: 
capif_configuration: 
  config_description: Default CAPIF Configuration
  config_description: Default CAPIF Configuration
@@ -36,3 +36,12 @@ capif_configuration:
      oauth: 1
      oauth: 1
      pki: 2
      pki: 2
      psk: 3
      psk: 3
  
package_paths:
  helper_api:
    path: /api
    openapi_file: api/openapi/openapi.yaml
  configuration_api:
    path: /configuration
    openapi_file: configuration/openapi/openapi.yaml
+50 −6
Original line number Original line Diff line number Diff line
@@ -3,17 +3,31 @@ import logging
import os
import os
import requests
import requests
from config import Config
from config import Config
from controllers.helper_controller import helper_routes
from pathlib import Path
from db.db import MongoDatabse
from db.db import get_mongo
import connexion
import sys
from flask import Flask
from flask import Flask
from OpenSSL.crypto import FILETYPE_PEM, TYPE_RSA, PKey, X509Req, dump_certificate_request, dump_privatekey
from OpenSSL.crypto import FILETYPE_PEM, TYPE_RSA, PKey, X509Req, dump_certificate_request, dump_privatekey
from asgiref.wsgi import WsgiToAsgi
from asgiref.wsgi import WsgiToAsgi


app = Flask(__name__)
# --- Paths setup: make 'services' discoverable so "import api..." works ---
BASE_DIR = Path(__file__).resolve().parent
SERVICES_DIR = BASE_DIR / "services"

# Insert services directory at front of sys.path
if SERVICES_DIR.is_dir():
    services_path_str = str(SERVICES_DIR)
    if services_path_str not in sys.path:
        sys.path.insert(0, services_path_str)
else:
    raise RuntimeError(f"Services directory not found at {SERVICES_DIR!s}")

app = connexion.App(__name__, specification_dir=str(SERVICES_DIR))
config = Config().get_config()
config = Config().get_config()


# Connect MongoDB and get TTL for superadmin certificate
# Connect MongoDB and get TTL for superadmin certificate
db = MongoDatabse()
db = get_mongo()
capif_config = db.get_col_by_name("capif_configuration").find_one({})
capif_config = db.get_col_by_name("capif_configuration").find_one({})
ttl_superadmin_cert = capif_config["settings"]["certificates_expiry"].get("ttl_superadmin_cert", "43000h")
ttl_superadmin_cert = capif_config["settings"]["certificates_expiry"].get("ttl_superadmin_cert", "43000h")


@@ -77,7 +91,37 @@ cert_file = open("certs/ca_root.crt", 'wb')
cert_file.write(bytes(ca_root, 'utf-8'))
cert_file.write(bytes(ca_root, 'utf-8'))
cert_file.close()
cert_file.close()


app.register_blueprint(helper_routes)

app.logger.setLevel(numeric_level)
package_paths = config.get("package_paths", {})

if not package_paths:
    logger.error("No package paths defined in configuration.")
    raise Exception("No package paths defined in configuration.")

# Dynamically add all APIs defined in package_paths
for name, pkg in package_paths.items():
    openapi_file = pkg.get("openapi_file")
    base_path = pkg.get("path")

    if not openapi_file or not base_path:
        logger.warning(f"Skipping package_path '{name}' because 'openapi_file' or 'path' is missing.")
        continue

    # Build a readable title from the key, e.g. "helper_api" -> "Helper Api"
    title = name.replace("_", " ").title()

    logger.info(
        f"Adding API '{name}': openapi_file='{openapi_file}', base_path='{base_path}', title='{title}'"
    )

    app.add_api(
        openapi_file,             # relative to specification_dir (SERVICES_DIR)
        arguments={"title": title},
        pythonic_params=True,
        base_path=base_path
    )


app.app.logger.setLevel(numeric_level)


asgi_app = WsgiToAsgi(app)
asgi_app = WsgiToAsgi(app)
 No newline at end of file
+0 −199
Original line number Original line 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()
Loading