From 6d21330694fb2d451172153b240eb36e783a732f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 06:23:44 +0100 Subject: [PATCH 01/91] Add missing authentication topic --- doc/source/examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 58aca2b..1ada9a0 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -21,7 +21,7 @@ The company's customer data, including a) Internet Domains and b) DNS Hostnames, .. note:: - In example number 4, we will explore these aspects further, including *Service Scaling* and *Load Balancing*. + In example number 4, we will explore these aspects further, including *Service Scaling*, *Load Balancing* and *Service Authentication*. 1.1. Basic OOP Relations ************************* From be03e7dff2622c203a0883788827e1489ef901b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 06:25:49 +0100 Subject: [PATCH 02/91] Fix correct exception type --- src/microesb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/microesb.py b/src/microesb.py index b8e929d..1072772 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -539,7 +539,7 @@ def _map( getattr(ci, ci.SYSServiceMethod)() except (TypeError, AttributeError) as e: self.logger.debug('SYSServiceMethod call exception:{}'.format(e)) - except Exception as e: + except KeyError as e: self.logger.debug('Class reference in service call metadata not set:{}'.format(e)) From 22615a0a2afb2635e82af47e0e654a550bab097b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 06:37:36 +0100 Subject: [PATCH 03/91] Add user simple (direct, unencapsulated) routing integration --- src/router.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/router.py diff --git a/src/router.py b/src/router.py new file mode 100644 index 0000000..e04fe53 --- /dev/null +++ b/src/router.py @@ -0,0 +1,24 @@ +# ]*[ --------------------------------------------------------------------- ]*[ +# . Micro ESB Router Python Module . +# ]*[ --------------------------------------------------------------------- ]*[ +# . . +# . Copyright Claus Prüfer 2016-2026 . +# . . +# . . +# ]*[ --------------------------------------------------------------------- ]*[ + +import logging +import importlib + +logger = logging.getLogger(__name__) + +mod_ref = importlib.import_module('user_routing') + + +class ServiceRouter(): + + def send(self, send_id, metadata): + logger.info('ServiceRouter send_id:{} metadata:{}'.format(send_id, metadata)) + func_ref = getattr(mod_ref, send_id) + logger.debug('FuncRef:{}'.format(func_ref)) + return func_ref(metadata) From b8eb778fa44d50edf0465cad6c5c53862121cb86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 06:38:23 +0100 Subject: [PATCH 04/91] Delete --- src/test.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/test.py diff --git a/src/test.py b/src/test.py deleted file mode 100644 index e69de29..0000000 From 6bfca1fff8248f5ed539a26d573ee6f913c48a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 06:39:20 +0100 Subject: [PATCH 05/91] Working user based simple routing, first routines for getting complete recursive hierarichal JSON data --- src/microesb.py | 94 ++++++++++++++++++++++++++++++++++++++++++---- src/transformer.py | 22 ++--------- 2 files changed, 90 insertions(+), 26 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 1072772..6b12132 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -2,7 +2,7 @@ # . Micro ESB Python Module . # ]*[ --------------------------------------------------------------------- ]*[ # . . -# . Copyright Claus Prüfer (2016 - 2025) . +# . Copyright Claus Prüfer (2016 - 2026) . # . . # . . # ]*[ --------------------------------------------------------------------- ]*[ @@ -10,11 +10,14 @@ import os import abc import sys +import copy import logging import importlib +from microesb.router import ServiceRouter from microesb.transformer import JSONTransformer + try: esb_python_path = os.environ['esbpythonpath'] os.environ['PYTHONPATH'] = esb_python_path @@ -53,6 +56,7 @@ def __init__(self): self.logger = logging.getLogger(__name__) self._SYSProperties = None + self._SYSPropertiesRegister = {} self._SYSParentObject = None self._SYSClassNames = [] @@ -86,9 +90,13 @@ def add_properties(self, properties, parent_instance): add_properties() method on initialization for each existing class instance. """ properties = self._add_sys_default_properties(properties) + properties.update(self._SYSPropertiesRegister) + self.logger.debug('add properties:{}'.format(properties)) + self._SYSParentObject = parent_instance setattr(self, '_SYSProperties', properties) + for p_key, p_value in properties.items(): setattr(self, p_key, p_value['default']) @@ -113,6 +121,19 @@ def _add_sys_default_properties(self, properties): } return properties + def _register_property(self, property_id, property_item): + """ _register_property() method. + + :param str property_id: property id (internal class attribute name) + :param dict property_item: property item to be registered for internal processing only + + Modifying data internally (inside a Service-Implementation) requires setting additional properties + not defined in Service-Properties, e.g. generated data, time-stamps or similar. + + Use this private method for this purpose. + """ + self._SYSPropertiesRegister[property_id] = property_item + def _set_property(self, key, value): """ _set_property() method. @@ -169,7 +190,7 @@ def class_name(self): def get_value_by_property_id(self, property_id): """ get_value_by_property_id() method.""" - raise NotImplementedError + return getattr(self, property_id) class ClassHandler(BaseHandler): @@ -182,6 +203,7 @@ def __init__(self): """ super().__init__() self._SYSType = 'class_instance' + self._ServiceRouter = ServiceRouter() def __add__(self, args): """ overloaded internal __add__() method (+ operator). @@ -194,7 +216,7 @@ def __add__(self, args): >>> 'class_name': class_name, >>> 'class_ref': class_ref >>> } - >>> parent_instance + args + >>> class_instance + args """ self._add_class(**args) @@ -393,6 +415,16 @@ def get_references(self): """ return self._class_references + def get_class_hierarchy(self): + """ get_class_hierarchy() method. + + :return: self._class_hierarchy + :rtype: dict + + Get class hierarchy dictionary. + """ + return self._class_hierarchy + def _map( self, *, @@ -406,7 +438,7 @@ def _map( :param dict *: used for passing params as **args dictionary :param str class_name: (root) class name :param dict property_ref: property reference dictionary - :param classref parent_instance: property reference dictionary + :param classref parent_instance: object reference :param dict children: children definition dictionary Recursive map class hierarchy / class instances. @@ -550,7 +582,7 @@ class ServiceExecuter(): def __init__(self): pass - def execute(self, class_mapper, service_data): + def execute_result(self, class_mapper, service_data): """ :param classref class_mapper: class mapper instance reference :param list service_data: list of service call metadata dictionary items @@ -558,13 +590,59 @@ def execute(self, class_mapper, service_data): rlist = [] for item in service_data['data']: - res = ServiceMapper( - class_mapper=class_mapper, + class_mapper_copy = copy.deepcopy(class_mapper) + sm_ref = ServiceMapper( + class_mapper=class_mapper_copy, service_call_data=item ) - rlist.append(res) + rlist.append( + self._connect_hierarchy( + class_mapper_copy.get_references() + ) + ) return rlist + def execute(self, class_mapper, service_data): + """ + :param classref class_mapper: class mapper instance reference + :param list service_data: list of service call metadata dictionary items + """ + + rlist = [] + for item in service_data['data']: + class_mapper_copy = copy.deepcopy(class_mapper) + ServiceMapper( + class_mapper=class_mapper_copy, + service_call_data=item + ) + + def _connect_hierarchy(self, reference_dict): + """ _connect_hierarchy() method. + + Init method for connecting all generated json_dicts. + """ + self._con_ref_dict = reference_dict + while self._get_sum_child_count(self._con_ref_dict) > 0: + self._connect_hierarchy_recursive(self._con_ref_dict) + + def _connect_hierarchy_rescursive(self, reference_dict): + """ _connect_hierarchy_recursive() method. + + Recursive connect all generated json_dicts. + """ + for key, value in reference_dict: + pass + + def _get_sum_child_count(self, reference_dict): + """ _get_sum_child_count() method. + + Count children nodes recursive and return sum. + """ + self.child_count = 0 + for key, value in reference_dict: + if key == 'children': + self.child_count += 1 + return self.child_count # import classes into current namespace current_mod = sys.modules[__name__] diff --git a/src/transformer.py b/src/transformer.py index d9ea923..63c88c0 100644 --- a/src/transformer.py +++ b/src/transformer.py @@ -2,13 +2,12 @@ # . Micro ESB transformer Python Module . # ]*[ --------------------------------------------------------------------- ]*[ # . . -# . Copyright Claus Prüfer 2016-2025 . +# . Copyright Claus Prüfer 2016-2026 . # . . # . . # ]*[ --------------------------------------------------------------------- ]*[ import json -import copy class JSONTransformer(): @@ -27,28 +26,15 @@ def json_transform(self): Recursive generate _json_dict for complete object hierarchy. """ - root_instance = copy.copy(self) - - for element in root_instance.iterate(): + for element in self.iterate(): element.set_json_dict() - self.logger.debug('JSON:{} properties:{}'.format( + self.logger.debug( + 'JSON:{} properties:{}'.format( element.json_dict, element._SYSProperties ) ) - while root_instance.class_count > 0: - for element in root_instance.iterate(): - if element.class_count == 0 and element._SYSType != 'multiclass_instance': - cname = element.class_name - parent_element = element.parent_object - parent_element._json_dict[cname] = element.json_dict[cname] - class_names_list = parent_element._SYSClassNames - del class_names_list[class_names_list.index(cname)] - - self._json_dict = root_instance.json_dict - del root_instance - @property def json(self): """ json() method. From 355c5decec4a4055621d9c91deff3811118a4c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 06:48:10 +0100 Subject: [PATCH 06/91] Modify example to NoSQL (mongodb) abstraction --- example/02-pki-management/main-ca.py | 13 +- example/02-pki-management/main-client.py | 13 +- example/02-pki-management/main-server.py | 13 +- .../service_implementation.py | 121 +++++++++++++++--- example/02-pki-management/user_routing.py | 16 +++ 5 files changed, 152 insertions(+), 24 deletions(-) create mode 100644 example/02-pki-management/user_routing.py diff --git a/example/02-pki-management/main-ca.py b/example/02-pki-management/main-ca.py index 4e0a3d8..858f4ea 100644 --- a/example/02-pki-management/main-ca.py +++ b/example/02-pki-management/main-ca.py @@ -1,3 +1,6 @@ +import sys +import logging + from microesb import microesb from class_reference import class_reference_ca as class_reference @@ -6,6 +9,14 @@ from service_call_metadata import service_metadata_ca as service_metadata +logging.getLogger().addHandler( + logging.StreamHandler(sys.stdout) +) + +logging.getLogger().setLevel( + logging.INFO +) + class_mapper = microesb.ClassMapper( class_references=class_reference, class_mappings=class_mapping, @@ -13,7 +24,7 @@ ) try: - res = microesb.ServiceExecuter().execute( + microesb.ServiceExecuter().execute( class_mapper=class_mapper, service_data=service_metadata ) diff --git a/example/02-pki-management/main-client.py b/example/02-pki-management/main-client.py index ce7f666..1031f0c 100644 --- a/example/02-pki-management/main-client.py +++ b/example/02-pki-management/main-client.py @@ -1,3 +1,6 @@ +import sys +import logging + from microesb import microesb from class_reference import class_reference_client as class_reference @@ -6,6 +9,14 @@ from service_call_metadata import service_metadata_client as service_metadata +logging.getLogger().addHandler( + logging.StreamHandler(sys.stdout) +) + +logging.getLogger().setLevel( + logging.INFO +) + class_mapper = microesb.ClassMapper( class_references=class_reference, class_mappings=class_mapping, @@ -13,7 +24,7 @@ ) try: - res = microesb.ServiceExecuter().execute( + microesb.ServiceExecuter().execute( class_mapper=class_mapper, service_data=service_metadata ) diff --git a/example/02-pki-management/main-server.py b/example/02-pki-management/main-server.py index 1697c0f..f576ff8 100644 --- a/example/02-pki-management/main-server.py +++ b/example/02-pki-management/main-server.py @@ -1,3 +1,6 @@ +import sys +import logging + from microesb import microesb from class_reference import class_reference_server as class_reference @@ -6,6 +9,14 @@ from service_call_metadata import service_metadata_server as service_metadata +logging.getLogger().addHandler( + logging.StreamHandler(sys.stdout) +) + +logging.getLogger().setLevel( + logging.INFO +) + class_mapper = microesb.ClassMapper( class_references=class_reference, class_mappings=class_mapping, @@ -13,7 +24,7 @@ ) try: - res = microesb.ServiceExecuter().execute( + microesb.ServiceExecuter().execute( class_mapper=class_mapper, service_data=service_metadata ) diff --git a/example/02-pki-management/service_implementation.py b/example/02-pki-management/service_implementation.py index 5f63ea4..c270d55 100644 --- a/example/02-pki-management/service_implementation.py +++ b/example/02-pki-management/service_implementation.py @@ -1,13 +1,37 @@ import abc +import logging +import datetime from microesb import microesb +logger = logging.getLogger(__name__) + class Cert(microesb.ClassHandler, metaclass=abc.ABCMeta): def __init__(self): super().__init__() + self._register_property( + 'generation_timestamp', + { + 'type': 'str', + 'default': None, + 'required': False, + 'description': 'SysInternal Certificate Generation Date' + } + ) + + self._register_property( + 'cert_data', + { + 'type': 'str', + 'default': None, + 'required': False, + 'description': 'SysInternal Generated Certificate Base64 encoded' + } + ) + @abc.abstractmethod def _load_ref_cert_data(self): """ Abstract _load_ref_cert_data() method. @@ -19,8 +43,8 @@ def _gen_openssl_cert(self): """ @abc.abstractmethod - def _insert_cert_db_data(self): - """ Abstract _insert_cert_db_data() method. + def _store_cert_data(self): + """ Abstract _store_cert_data() method. """ def gen_cert(self): @@ -28,27 +52,39 @@ def gen_cert(self): self._load_ref_cert_data() if getattr(self, 'Smartcard') is not None: + logger.info('Gen HSM Keypair') self._hsm_gen_keypair() - else: + if getattr(self, 'Smartcard') is None: + logger.info('Gen OpenSSL PrivKey') self._gen_openssl_privkey() self._gen_openssl_cert() - self._insert_cert_db_data() + self.generation_timestamp = datetime.datetime.now().isoformat('T') + self._store_cert_data() def _gen_openssl_privkey(self): - print('Gen openssl private key.') + logger.info('Gen openssl private key.') - def _get_cert_dbdata_by_id(self): - print('Get cert data from db. Type: {}.'.format(self.type)) + def _get_cert_data_by_id(self): + logger.info('Get cert data from ESB. Type:{}.'.format(self.type)) + self.set_properties( + self._ServiceRouter.send('CertGetById', metadata=self.id) + ) def _hsm_gen_keypair(self): - print('Smartcard container label:{}'.format( + logger.info('Smartcard container label:{}'.format( self.Smartcard.SmartcardContainer.label) ) self.Smartcard.gen_keypair() + def _store_cert_data(self): + logger.info('Store {} cert metadata.'.format(self.type)) + self.json_transform() + self._ServiceRouter.send('CertStore', metadata=self.json_dict) + class CertCA(Cert): + def __init__(self): self.type = 'ca' super().__init__() @@ -57,54 +93,97 @@ def _load_ref_cert_data(self): pass def _gen_openssl_cert(self): - print('Gen openssl cert type:{}.'.format(self.type)) + logger.info('Generating {} cert.'.format(self.type)) + + self.json_transform() - def _insert_cert_db_data(self): - print('Insert cert data type:{} into db.'.format(self.type)) + srv_metadata = { + "CertCA": self.json_dict + } + + self.cert_data = 'dummy_cacert_data' + logger.info('Generating cert with metadata:{}'.format(self.json)) class CertServer(Cert): + def __init__(self): self.type = 'server' super().__init__() def _load_ref_cert_data(self): - self.CertCA._get_cert_dbdata_by_id() + self.CertCA._get_cert_data_by_id() def _gen_openssl_cert(self): - print('Gen openssl cert type:{}, rel to CA.'.format(self.type)) + logger.info('Generating {} cert.'.format(self.type)) + + self.json_transform() - def _insert_cert_db_data(self): - print('Insert cert data type:{} into db.'.format(self.type)) + srv_metadata = { + "CertServer": self.json_dict, + "CertCA": self.CertCA.json_dict + } + + logger.info('Generating cert with metadata:{}'.format(srv_metadata)) + + self.cert_data = 'dummy_servercert_data' class CertClient(Cert): + def __init__(self): self.type = 'client' super().__init__() def _load_ref_cert_data(self): - self.CertCA._get_cert_dbdata_by_id() - self.CertServer._get_cert_dbdata_by_id() + self.CertCA._get_cert_data_by_id() + self.CertServer._get_cert_data_by_id() def _gen_openssl_cert(self): - print('Gen openssl cert type:{}, rel to cCA and cServer.'.format(self.type)) + logger.info('Route {} cert gen request (rel to CA and Server) to ESB external service.'.format(self.type)) - def _insert_cert_db_data(self): - print('Insert cert data type:{} into db.'.format(self.type)) + self.json_transform() + + srv_metadata = { + "CertClient": self.json, + "CertServer": self.CertServer.json, + "CertCA": self.CertCA.json + } + + logger.info('Generating cert with metadata:{}'.format(srv_metadata)) + self.cert_data = 'dummy_clientcert_data' class Smartcard(microesb.ClassHandler): + def __init__(self): super().__init__() + self._register_property( + 'gen_status', + { + 'type': bool, + 'default': False, + 'required': False, + 'description': 'SysInternal Generated Smartcard Keypair Status' + } + ) + def gen_keypair(self): - print('Gen keypair on smartcard:{} with keypair label:{}'.format( + logger.info('Gen keypair on smartcard:{} with keypair label:{}'.format( self.label, self.SmartcardContainer.label )) + srv_metadata = { + "SmartcardID": self.label, + "SmartcardContainerLabel": self.SmartcardContainer.label + } + + self.gen_status = self._ServiceRouter.send('KeypairGenerate', metadata=srv_metadata) + class SmartcardContainer(microesb.ClassHandler): + def __init__(self): super().__init__() diff --git a/example/02-pki-management/user_routing.py b/example/02-pki-management/user_routing.py new file mode 100644 index 0000000..dcf677b --- /dev/null +++ b/example/02-pki-management/user_routing.py @@ -0,0 +1,16 @@ +from pymongo import MongoClient + +client = MongoClient('mongodb://127.0.0.1/') +mongodb = client.get_database('microesb') + + +def CertGetById(metadata): + return mongodb.cert.find_one( + {"id": metadata} + ) + +def CertStore(metadata): + mongodb.cert.insert_one(metadata) + +def KeypairGenerate(metadata): + return True From c60cfef4f147a97aef8474487fc38d74298a343e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 06:56:02 +0100 Subject: [PATCH 07/91] Fix member declaration --- src/microesb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/microesb.py b/src/microesb.py index 6b12132..bdde5fd 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -580,7 +580,7 @@ class ServiceExecuter(): """ def __init__(self): - pass + self._con_ref_dict = None def execute_result(self, class_mapper, service_data): """ From 5414731c61a26510467d0a35d850196ac6fb91fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 06:58:49 +0100 Subject: [PATCH 08/91] Correct line length --- src/microesb.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index bdde5fd..9a933a8 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -127,8 +127,9 @@ def _register_property(self, property_id, property_item): :param str property_id: property id (internal class attribute name) :param dict property_item: property item to be registered for internal processing only - Modifying data internally (inside a Service-Implementation) requires setting additional properties - not defined in Service-Properties, e.g. generated data, time-stamps or similar. + Modifying data internally (inside a Service-Implementation) requires setting + additional properties not defined in Service-Properties, e.g. generated data, + time-stamps or similar. Use this private method for this purpose. """ From 3f405c20ceaa118ce4b8ba22a2b7519fc94ac4bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:04:16 +0100 Subject: [PATCH 09/91] Fix looping over dict --- src/microesb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 9a933a8..3aa7e5f 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -631,7 +631,7 @@ def _connect_hierarchy_rescursive(self, reference_dict): Recursive connect all generated json_dicts. """ - for key, value in reference_dict: + for key, value in reference_dict.items(): pass def _get_sum_child_count(self, reference_dict): @@ -640,7 +640,7 @@ def _get_sum_child_count(self, reference_dict): Count children nodes recursive and return sum. """ self.child_count = 0 - for key, value in reference_dict: + for key, value in reference_dict.items(): if key == 'children': self.child_count += 1 return self.child_count From 8791706a6dc43dceafba2b85edd1ec56940febaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:05:38 +0100 Subject: [PATCH 10/91] Remove --- src/microesb.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/microesb.py b/src/microesb.py index 3aa7e5f..accd6eb 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -609,7 +609,6 @@ def execute(self, class_mapper, service_data): :param list service_data: list of service call metadata dictionary items """ - rlist = [] for item in service_data['data']: class_mapper_copy = copy.deepcopy(class_mapper) ServiceMapper( From 13ab41387c66dd71ceaa01c37cf98cf305386bf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:06:38 +0100 Subject: [PATCH 11/91] Update src/microesb.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/microesb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/microesb.py b/src/microesb.py index accd6eb..9d43bc5 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -572,7 +572,7 @@ def _map( getattr(ci, ci.SYSServiceMethod)() except (TypeError, AttributeError) as e: self.logger.debug('SYSServiceMethod call exception:{}'.format(e)) - except KeyError as e: + except (KeyError, TypeError, AttributeError) as e: self.logger.debug('Class reference in service call metadata not set:{}'.format(e)) From eb904d879057597324684ab1beee5492e8b25e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:09:59 +0100 Subject: [PATCH 12/91] Correct type to json_dict --- example/02-pki-management/service_implementation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/02-pki-management/service_implementation.py b/example/02-pki-management/service_implementation.py index c270d55..77f009d 100644 --- a/example/02-pki-management/service_implementation.py +++ b/example/02-pki-management/service_implementation.py @@ -145,9 +145,9 @@ def _gen_openssl_cert(self): self.json_transform() srv_metadata = { - "CertClient": self.json, - "CertServer": self.CertServer.json, - "CertCA": self.CertCA.json + "CertClient": self.json_dict, + "CertServer": self.CertServer.json_dict, + "CertCA": self.CertCA.json_dict } logger.info('Generating cert with metadata:{}'.format(srv_metadata)) From 5d688f8281f5f2b2f87d3dd34f5b3eb9837e1fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:11:21 +0100 Subject: [PATCH 13/91] Correct typo --- src/microesb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/microesb.py b/src/microesb.py index 9d43bc5..398dc06 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -625,7 +625,7 @@ def _connect_hierarchy(self, reference_dict): while self._get_sum_child_count(self._con_ref_dict) > 0: self._connect_hierarchy_recursive(self._con_ref_dict) - def _connect_hierarchy_rescursive(self, reference_dict): + def _connect_hierarchy_recursive(self, reference_dict): """ _connect_hierarchy_recursive() method. Recursive connect all generated json_dicts. From 5208a25a6725b57e62d27e7c3ead328f5a8356dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:23:00 +0100 Subject: [PATCH 14/91] Correct member declaration --- src/microesb.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/microesb.py b/src/microesb.py index 398dc06..63ec2f0 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -582,6 +582,7 @@ class ServiceExecuter(): def __init__(self): self._con_ref_dict = None + self._child_count = 0 def execute_result(self, class_mapper, service_data): """ From 3c540c1b27f738588811f885f9f687dc4a1b2817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:23:12 +0100 Subject: [PATCH 15/91] Add docstrings --- src/router.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/router.py b/src/router.py index e04fe53..fb160ed 100644 --- a/src/router.py +++ b/src/router.py @@ -16,8 +16,19 @@ class ServiceRouter(): + """ ServiceRouter class. + """ def send(self, send_id, metadata): + """ send() method. + + :param str send_id: service method id + :param dynamic metadata: first argument passed to service method function + :rtype: dict | None + + Execute method with given id in `send_id` from imported user_routing.py module + and return result dict or None. + """ logger.info('ServiceRouter send_id:{} metadata:{}'.format(send_id, metadata)) func_ref = getattr(mod_ref, send_id) logger.debug('FuncRef:{}'.format(func_ref)) From 88e743375a1c85b085ecb972d6a93ee719829f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:25:44 +0100 Subject: [PATCH 16/91] Correct private member name --- src/microesb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/microesb.py b/src/microesb.py index 63ec2f0..d90f130 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -639,7 +639,7 @@ def _get_sum_child_count(self, reference_dict): Count children nodes recursive and return sum. """ - self.child_count = 0 + self._child_count = 0 for key, value in reference_dict.items(): if key == 'children': self.child_count += 1 From 305324b4523f26b6221670c28d3162c7a9271cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:29:56 +0100 Subject: [PATCH 17/91] Add exception handling for non-existing user_routing.py cases --- src/router.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/router.py b/src/router.py index fb160ed..69e4484 100644 --- a/src/router.py +++ b/src/router.py @@ -12,7 +12,10 @@ logger = logging.getLogger(__name__) -mod_ref = importlib.import_module('user_routing') +try: + mod_ref = importlib.import_module('user_routing') +except ImportError as e: + pass class ServiceRouter(): From 8de916d06513dc9c3b0903f66a795f0230b60a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:35:01 +0100 Subject: [PATCH 18/91] Correct for tests --- src/microesb.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/microesb.py b/src/microesb.py index d90f130..24b2921 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -610,12 +610,14 @@ def execute(self, class_mapper, service_data): :param list service_data: list of service call metadata dictionary items """ + rlist = [] for item in service_data['data']: class_mapper_copy = copy.deepcopy(class_mapper) - ServiceMapper( + sm_ref = ServiceMapper( class_mapper=class_mapper_copy, service_call_data=item ) + rlist.append(sm_ref) def _connect_hierarchy(self, reference_dict): """ _connect_hierarchy() method. From e49a82d201f5928152683b5ea87123fd2905bc71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:37:25 +0100 Subject: [PATCH 19/91] Add forgotten return statement --- src/microesb.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/microesb.py b/src/microesb.py index 24b2921..46e4c65 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -618,6 +618,7 @@ def execute(self, class_mapper, service_data): service_call_data=item ) rlist.append(sm_ref) + return rlist def _connect_hierarchy(self, reference_dict): """ _connect_hierarchy() method. From 050a57369dc9755fe76bd34bba22eec87ccda0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 5 Jan 2026 07:58:08 +0100 Subject: [PATCH 20/91] Remove json_transform check (add new test after new deserialization method has been applied) --- test/integration/test_base.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/test/integration/test_base.py b/test/integration/test_base.py index 02559bd..8cbbab6 100644 --- a/test/integration/test_base.py +++ b/test/integration/test_base.py @@ -273,6 +273,8 @@ def test_multi_item_object( shipment = getattr(s[0]._class_mapper, 'Shipment') palette = getattr(shipment, 'Palette') + assert getattr(shipment, 'id') == 'testshipment1' + p1 = palette._object_container[0] p2 = palette._object_container[1] @@ -280,13 +282,3 @@ def test_multi_item_object( assert getattr(p1, 'label') == 'label1' assert getattr(p2, 'id') == 2 assert getattr(p2, 'label') == 'label2' - - shipment.json_transform() - assert shipment.json_dict == { - 'id': 'testshipment1', - 'SYSServiceMethod': None, - 'Palette': [ - {'id': 1, 'label': 'label1'}, - {'id': 2, 'label': 'label2'} - ] - } From c64ea5dbe2f75f979b6bf7cddefdef9dee0c3648 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 05:25:17 +0000 Subject: [PATCH 21/91] Initial plan From bdbe4e54822044075df847ba236cc42dc59ee4ed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 05:27:19 +0000 Subject: [PATCH 22/91] Add Example 04 NLAP integration template with README.md Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- example/04-nlap-integration/README.md | 50 +++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 example/04-nlap-integration/README.md diff --git a/example/04-nlap-integration/README.md b/example/04-nlap-integration/README.md new file mode 100644 index 0000000..ffaaaa0 --- /dev/null +++ b/example/04-nlap-integration/README.md @@ -0,0 +1,50 @@ +# Example 4: NLAP Proxy Integration + +> **Note:** This example is a **template** for future implementation of NLAP Proxy Authentication integration with Python MicroESB. + +## Overview + +This example will demonstrate how to integrate Python MicroESB with the **NLAP (Network Layer Authentication Protocol) Proxy** for secure, authenticated service communication. + +## NLAP Proxy + +The NLAP Proxy is an advanced authentication and security layer for HTTP communication. For more information, see the NLAP Proxy project: + +**External Link:** [https://github.com/WEBcodeX1/http-1.2](https://github.com/WEBcodeX1/http-1.2) + +### Key Security Features + +The NLAP Proxy implementation enforces the following security requirements: + +1. **Encrypted Communication Only** + - The proxy will **not** communicate unencrypted + - All traffic must use encrypted channels + +2. **Restricted Communication Scope** + - The proxy will **not** allow global communication + - Communication is restricted to authorized endpoints only + +3. **X.509 Certificate-Based Authentication** + - The proxy will **only** allow communication by X.509 client certificates + - Client authentication is mandatory for all connections + +## Future Implementation + +This example will cover: + +- Setting up NLAP Proxy for MicroESB services +- Configuring X.509 client certificate authentication +- Integrating encrypted communication channels +- Service routing through the NLAP Proxy +- Load balancing and service scaling with NLAP authentication + +## Prerequisites + +- Python 3.8 or later +- NLAP Proxy (see external link above) +- X.509 client and server certificates +- Python MicroESB package + +## Additional Information + +For more detailed documentation on Python MicroESB, see: [https://pythondocs.webcodex.de/micro-esb/examples.html](https://pythondocs.webcodex.de/micro-esb/examples.html) From de7e14a5f7bbdfa7d85e8a1065890e9975d51304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 6 Jan 2026 06:32:52 +0100 Subject: [PATCH 23/91] Update README to clarify NLAP Proxy description --- example/04-nlap-integration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/04-nlap-integration/README.md b/example/04-nlap-integration/README.md index ffaaaa0..a1a0e2b 100644 --- a/example/04-nlap-integration/README.md +++ b/example/04-nlap-integration/README.md @@ -8,7 +8,7 @@ This example will demonstrate how to integrate Python MicroESB with the **NLAP ( ## NLAP Proxy -The NLAP Proxy is an advanced authentication and security layer for HTTP communication. For more information, see the NLAP Proxy project: +The NLAP Proxy is an advanced authentication and security layer for NLAP protocol communication. For more information, see the NLAP (Next Level Application Protocol) project: **External Link:** [https://github.com/WEBcodeX1/http-1.2](https://github.com/WEBcodeX1/http-1.2) From 349e30d59382a03ae9bf4ce088104da9cd04cbe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 6 Jan 2026 07:41:52 +0100 Subject: [PATCH 24/91] Add version 1.1 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7bdd4f..ba6ba6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog + +## Version 1.1 + ## Version 1.0 - Working pylint checks From 5c076d13a5aad1c261500205d4e288e880e20870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 14:50:34 +0100 Subject: [PATCH 25/91] - add recursive auto mapping of class instances' json_dict (deserialization) after a json_transform() - add multiple improvements for service implementation processing (see example02) - add a @property decorated "property_dict" member to microesb.BaseHandler() - add global "simple" user service routing --- src/microesb.py | 224 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 194 insertions(+), 30 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 46e4c65..86cbe04 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -135,15 +135,15 @@ def _register_property(self, property_id, property_item): """ self._SYSPropertiesRegister[property_id] = property_item - def _set_property(self, key, value): + def _set_property(self, property_id, value): """ _set_property() method. - :param str key: property key name + :param str property_id: property dict key :param str value: property value """ - if key in self._SYSProperties: - setattr(self, key, value) + if property_id in self._SYSProperties: + setattr(self, property_id, value) @property def parent_object(self): @@ -167,6 +167,19 @@ def properties(self): """ return self._SYSProperties + @property + def property_dict(self): + """ property_dict() method. + + Return all classes self._SYSProperties property_id, value dictionary. + """ + + return_dict = {} + for property_id in self._SYSProperties: + if property_id != 'SYSServiceMethod': + return_dict[property_id] = self.get_value_by_property_id(property_id) + return return_dict + @property def class_count(self): """ class_count() method. @@ -256,19 +269,33 @@ def set_properties(self, item_dict): Iterates over item_dict and calls self._set_property(property_id, value) foreach item. """ + for property_id, value in item_dict.items(): self._set_property(property_id, value) def set_json_dict(self): """ set_json_dict() method. - Preprare self.json_dict from self._SYSProperties (used by JSONTransformer). + Propagate self.json_dict with current class instance attribute values (self._SYSProperties) + and with empty (None) class instance references (processed from JSONTransformer). """ - self.logger.debug('self._SYSProperties:{}'.format(self._SYSProperties)) + for property_id in self._SYSProperties: self.logger.debug('processing property:{}'.format(property_id)) self.json_dict[property_id] = getattr(self, property_id) + try: + del self.json_dict['SYSServiceMethod'] + except KeyError as e: + pass + + self.logger.debug('self._SYSProperties:{}'.format(self._SYSProperties)) + + for class_ref in self._SYSClassNames: + self.json_dict[class_name] = None + + self.logger.debug('JSONDict:{}'.format(self.json_dict)) + class MultiClassHandler(BaseHandler): """ MultiObject handler class. @@ -581,10 +608,19 @@ class ServiceExecuter(): """ def __init__(self): + + self.logger = logging.getLogger(__name__) + self._con_ref_dict = None - self._child_count = 0 + self._class_hierarchy = None + self._class_hierarchy_list = None + self._class_hierarchy_list_plain = None + self._hierarchy_level = None + self._map_hierarchy_level = None + self._class_hierarchy_comp = None + self._hierarchy_level_comp = None - def execute_result(self, class_mapper, service_data): + def execute(self, class_mapper, service_data): """ :param classref class_mapper: class mapper instance reference :param list service_data: list of service call metadata dictionary items @@ -597,14 +633,10 @@ def execute_result(self, class_mapper, service_data): class_mapper=class_mapper_copy, service_call_data=item ) - rlist.append( - self._connect_hierarchy( - class_mapper_copy.get_references() - ) - ) + rlist.append(sm_ref) return rlist - def execute(self, class_mapper, service_data): + def execute_get_hierarchy(self, class_mapper, service_data): """ :param classref class_mapper: class mapper instance reference :param list service_data: list of service call metadata dictionary items @@ -617,36 +649,168 @@ def execute(self, class_mapper, service_data): class_mapper=class_mapper_copy, service_call_data=item ) - rlist.append(sm_ref) + + rlist.append( + self._connect_hierarchy( + class_mapper_copy.get_references() + ) + ) return rlist - def _connect_hierarchy(self, reference_dict): + def _connect_hierarchy(self, cm_ref_dict): """ _connect_hierarchy() method. Init method for connecting all generated json_dicts. """ - self._con_ref_dict = reference_dict - while self._get_sum_child_count(self._con_ref_dict) > 0: - self._connect_hierarchy_recursive(self._con_ref_dict) - def _connect_hierarchy_recursive(self, reference_dict): + self.logger.debug('Processed class_mapper references dict (containing service hierarchy data):{}'.format(cm_ref_dict)) + + self.logger.debug('Mapping parent_object instances to child instances') + + self._map_hierarchy_level = -1 + self._map_object_instances(cm_ref_dict) + + sum_children = ChildCounter().get_sum_child_count(cm_ref_dict) + self.logger.debug('Sum children:{}'.format(sum_children)) + + while sum_children > 0: + + self._hierarchy_level = -1 + self._class_hierarchy = {} + self._class_hierarchy_list = [] + self._class_hierarchy_list_plain = [] + + self._connect_hierarchy_recursive(cm_ref_dict) + + self.logger.debug('Class hierarchy list:{} plain:{}'.format(self._class_hierarchy_list, self._class_hierarchy_list_plain)) + + for class_hierarchy_item in self._class_hierarchy_list: + + self._class_hierarchy_comp = {} + self._hierarchy_level_comp = -1 + + self._class_hierarchy = class_hierarchy_item + + self._rename_dict_key(cm_ref_dict) + self.logger.debug('Renamed children dict:{}'.format(cm_ref_dict)) + + sum_children = ChildCounter().get_sum_child_count(cm_ref_dict) + self.logger.debug('Sum children:{}'.format(sum_children)) + + return cm_ref_dict + + def _map_object_instances(self, reference_dict): + + for class_name, class_properties in reference_dict.items(): + + if 'children' in class_properties: + + children_dict = class_properties['children'] + first_child_key = next(iter(children_dict)) + reference_dict[class_name]['object_instance'] = children_dict[first_child_key]['parent_instance'] + + self._map_hierarchy_level += 1 + self._map_object_instances(class_properties['children']) + self._map_hierarchy_level -= 1 + + if self._map_hierarchy_level == -1: + self.logger.debug('Root object JSON transform:{}'.format(class_name)) + class_properties['object_instance'].json_transform() + + def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent_dict=None): """ _connect_hierarchy_recursive() method. - Recursive connect all generated json_dicts. + Recursive connect all generated json_dicts to its parents. """ - for key, value in reference_dict.items(): - pass - def _get_sum_child_count(self, reference_dict): - """ _get_sum_child_count() method. + self.logger.info('Parent dict:{}'.format(parent_dict)) + + if parent_class is not None: + self._class_hierarchy[self._hierarchy_level] = parent_class + + for class_name, class_properties in reference_dict.items(): + + if 'children' in class_properties: + self._hierarchy_level += 1 + self._connect_hierarchy_recursive(class_properties['children'], class_name, reference_dict) + del self._class_hierarchy[self._hierarchy_level] + self._hierarchy_level -= 1 + else: + parent_instance = class_properties['parent_instance'] + parent_class_name = parent_instance.class_name + src_instance = getattr(parent_instance, class_name) + parent_instance.json_dict[class_name] = src_instance.json_dict + + self.logger.debug('Mapping class_name:{} parent_class_name:{}'.format(class_name, parent_class_name)) + + tmp_class_hierarchy = copy.deepcopy(self._class_hierarchy) + new_class_hierarchy = {} + new_class_hierarchy[0] = tmp_class_hierarchy[0] + + insert_index = 1 + for i in range(1, len(tmp_class_hierarchy)+1): + self.logger.debug('Reorder hierarchy tmp:{} new:{}'.format(tmp_class_hierarchy, new_class_hierarchy)) + reorder_index = insert_index+1 + try: + new_class_hierarchy[reorder_index] = tmp_class_hierarchy[i] + except KeyError as e: + pass + new_class_hierarchy[insert_index] = 'children' + insert_index +=2 + + self.logger.debug('Append hierarchy:{}'.format(new_class_hierarchy)) + self._class_hierarchy_list.append(new_class_hierarchy) + self._class_hierarchy_list_plain.append(tmp_class_hierarchy) + + def _rename_dict_key(self, rename_dict, parent_dict=None, parent_class=None): + + self.logger.debug('Parent class:{}'.format(parent_class)) + self.logger.debug('RenameDict:{}'.format(rename_dict)) + + if parent_class is not None: + self._class_hierarchy_comp[self._hierarchy_level_comp] = parent_class + + if self._class_hierarchy == self._class_hierarchy_comp: + self.logger.info('Match rename_dict:{}'.format(rename_dict)) + + # only remove when all children have been altered to children_processed + if ChildCounter().get_sum_child_count(dict(rename_dict)) == 0: + parent_dict['children_processed'] = parent_dict.pop('children') + + self.logger.info('Hierarchy comp:{} orig:{}'.format(self._class_hierarchy, self._class_hierarchy_comp)) + + for key in list(rename_dict.keys()): + self.logger.info('Processing dict key:{} value:{}'.format(key, rename_dict[key])) + if isinstance(rename_dict[key], dict) and key != 'hierarchy': + self._hierarchy_level_comp += 1 + self._rename_dict_key(rename_dict[key], rename_dict, key) + del self._class_hierarchy_comp[self._hierarchy_level_comp] + self._hierarchy_level_comp -= 1 + + +class ChildCounter(): + """ Child node counter class. + """ + + def __init__(self): + self._children_occurences = 0 + self.logger = logging.getLogger(__name__) + + def get_sum_child_count(self, reference_dict): + """ get_sum_child_count() method. Count children nodes recursive and return sum. """ - self._child_count = 0 - for key, value in reference_dict.items(): - if key == 'children': - self.child_count += 1 - return self.child_count + + self.logger.info('Sum count ref-dict:{}'.format(reference_dict)) + + for class_name, class_properties in reference_dict.items(): + if 'children' in class_properties: + self._children_occurences += 1 + self.get_sum_child_count(class_properties['children']) + + return self._children_occurences + # import classes into current namespace current_mod = sys.modules[__name__] From 3b1c1c2d4a34b977d9f7609d7d15ff5fdcf06aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 15:01:29 +0100 Subject: [PATCH 26/91] Modify example to use MongoDB backend --- .../{main-ca.py => 0-main-ca.py} | 11 +++++++- .../{main-server.py => 1-main-server.py} | 11 +++++++- .../{main-client.py => 2-main-client.py} | 11 +++++++- .../service_implementation.py | 25 +++++++------------ example/02-pki-management/user_routing.py | 2 +- 5 files changed, 40 insertions(+), 20 deletions(-) rename example/02-pki-management/{main-ca.py => 0-main-ca.py} (71%) rename example/02-pki-management/{main-server.py => 1-main-server.py} (70%) rename example/02-pki-management/{main-client.py => 2-main-client.py} (70%) diff --git a/example/02-pki-management/main-ca.py b/example/02-pki-management/0-main-ca.py similarity index 71% rename from example/02-pki-management/main-ca.py rename to example/02-pki-management/0-main-ca.py index 858f4ea..22463fe 100644 --- a/example/02-pki-management/main-ca.py +++ b/example/02-pki-management/0-main-ca.py @@ -8,6 +8,11 @@ from class_mapping import class_mapping from service_call_metadata import service_metadata_ca as service_metadata +from pymongo import MongoClient + +client = MongoClient('mongodb://192.168.61.248/') +mongodb = client.get_database('microesb') + logging.getLogger().addHandler( logging.StreamHandler(sys.stdout) @@ -24,9 +29,13 @@ ) try: - microesb.ServiceExecuter().execute( + res = microesb.ServiceExecuter().execute_get_hierarchy( class_mapper=class_mapper, service_data=service_metadata ) except Exception as e: print('Service execution error: {}'.format(e)) + +root_object = res[0]['CertCA']['object_instance'] + +mongodb.cert_hierarchy.insert_one(root_object.json_dict) diff --git a/example/02-pki-management/main-server.py b/example/02-pki-management/1-main-server.py similarity index 70% rename from example/02-pki-management/main-server.py rename to example/02-pki-management/1-main-server.py index f576ff8..839edd7 100644 --- a/example/02-pki-management/main-server.py +++ b/example/02-pki-management/1-main-server.py @@ -8,6 +8,11 @@ from class_mapping import class_mapping from service_call_metadata import service_metadata_server as service_metadata +from pymongo import MongoClient + +client = MongoClient('mongodb://192.168.61.248/') +mongodb = client.get_database('microesb') + logging.getLogger().addHandler( logging.StreamHandler(sys.stdout) @@ -24,9 +29,13 @@ ) try: - microesb.ServiceExecuter().execute( + res = microesb.ServiceExecuter().execute_get_hierarchy( class_mapper=class_mapper, service_data=service_metadata ) except Exception as e: print('Service execution error: {}'.format(e)) + +root_object = res[0]['CertServer']['object_instance'] + +mongodb.cert_hierarchy.insert_one(root_object.json_dict) diff --git a/example/02-pki-management/main-client.py b/example/02-pki-management/2-main-client.py similarity index 70% rename from example/02-pki-management/main-client.py rename to example/02-pki-management/2-main-client.py index 1031f0c..b8fc100 100644 --- a/example/02-pki-management/main-client.py +++ b/example/02-pki-management/2-main-client.py @@ -8,6 +8,11 @@ from class_mapping import class_mapping from service_call_metadata import service_metadata_client as service_metadata +from pymongo import MongoClient + +client = MongoClient('mongodb://192.168.61.248/') +mongodb = client.get_database('microesb') + logging.getLogger().addHandler( logging.StreamHandler(sys.stdout) @@ -24,9 +29,13 @@ ) try: - microesb.ServiceExecuter().execute( + res = microesb.ServiceExecuter().execute_get_hierarchy( class_mapper=class_mapper, service_data=service_metadata ) except Exception as e: print('Service execution error: {}'.format(e)) + +root_object = res[0]['CertClient']['object_instance'] + +mongodb.cert_hierarchy.insert_one(root_object.json_dict) diff --git a/example/02-pki-management/service_implementation.py b/example/02-pki-management/service_implementation.py index 77f009d..e2da39b 100644 --- a/example/02-pki-management/service_implementation.py +++ b/example/02-pki-management/service_implementation.py @@ -79,8 +79,7 @@ def _hsm_gen_keypair(self): def _store_cert_data(self): logger.info('Store {} cert metadata.'.format(self.type)) - self.json_transform() - self._ServiceRouter.send('CertStore', metadata=self.json_dict) + self._ServiceRouter.send('CertStore', metadata=self.property_dict) class CertCA(Cert): @@ -95,14 +94,12 @@ def _load_ref_cert_data(self): def _gen_openssl_cert(self): logger.info('Generating {} cert.'.format(self.type)) - self.json_transform() - srv_metadata = { - "CertCA": self.json_dict + "CertCA": self.property_dict } self.cert_data = 'dummy_cacert_data' - logger.info('Generating cert with metadata:{}'.format(self.json)) + logger.info('Generating cert with metadata:{}'.format(srv_metadata)) class CertServer(Cert): @@ -117,11 +114,9 @@ def _load_ref_cert_data(self): def _gen_openssl_cert(self): logger.info('Generating {} cert.'.format(self.type)) - self.json_transform() - srv_metadata = { - "CertServer": self.json_dict, - "CertCA": self.CertCA.json_dict + "CertServer": self.property_dict, + "CertCA": self.CertCA.property_dict } logger.info('Generating cert with metadata:{}'.format(srv_metadata)) @@ -140,14 +135,12 @@ def _load_ref_cert_data(self): self.CertServer._get_cert_data_by_id() def _gen_openssl_cert(self): - logger.info('Route {} cert gen request (rel to CA and Server) to ESB external service.'.format(self.type)) - - self.json_transform() + logger.info('Generating {} cert.'.format(self.type)) srv_metadata = { - "CertClient": self.json_dict, - "CertServer": self.CertServer.json_dict, - "CertCA": self.CertCA.json_dict + "CertClient": self.property_dict, + "CertServer": self.CertServer.property_dict, + "CertCA": self.CertCA.property_dict } logger.info('Generating cert with metadata:{}'.format(srv_metadata)) diff --git a/example/02-pki-management/user_routing.py b/example/02-pki-management/user_routing.py index dcf677b..902d194 100644 --- a/example/02-pki-management/user_routing.py +++ b/example/02-pki-management/user_routing.py @@ -1,6 +1,6 @@ from pymongo import MongoClient -client = MongoClient('mongodb://127.0.0.1/') +client = MongoClient('mongodb://192.168.61.248/') mongodb = client.get_database('microesb') From a9069385364d1bfc67a204489640418b4b7b6981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 15:13:30 +0100 Subject: [PATCH 27/91] Fix incorrect variable name --- src/microesb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/microesb.py b/src/microesb.py index 86cbe04..38f8265 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -291,7 +291,7 @@ def set_json_dict(self): self.logger.debug('self._SYSProperties:{}'.format(self._SYSProperties)) - for class_ref in self._SYSClassNames: + for class_name in self._SYSClassNames: self.json_dict[class_name] = None self.logger.debug('JSONDict:{}'.format(self.json_dict)) From ccaf5b788397aa0402b568c7baf051e66e5e5957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 15:47:07 +0100 Subject: [PATCH 28/91] Rename according to global style --- example/02-pki-management/{0-main-ca.py => 00-main-ca.py} | 0 example/02-pki-management/{1-main-server.py => 01-main-server.py} | 0 example/02-pki-management/{2-main-client.py => 02-main-client.py} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename example/02-pki-management/{0-main-ca.py => 00-main-ca.py} (100%) rename example/02-pki-management/{1-main-server.py => 01-main-server.py} (100%) rename example/02-pki-management/{2-main-client.py => 02-main-client.py} (100%) diff --git a/example/02-pki-management/0-main-ca.py b/example/02-pki-management/00-main-ca.py similarity index 100% rename from example/02-pki-management/0-main-ca.py rename to example/02-pki-management/00-main-ca.py diff --git a/example/02-pki-management/1-main-server.py b/example/02-pki-management/01-main-server.py similarity index 100% rename from example/02-pki-management/1-main-server.py rename to example/02-pki-management/01-main-server.py diff --git a/example/02-pki-management/2-main-client.py b/example/02-pki-management/02-main-client.py similarity index 100% rename from example/02-pki-management/2-main-client.py rename to example/02-pki-management/02-main-client.py From 5973f8bca5963ba971c57b05e61ac1b5ccc68c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 15:49:25 +0100 Subject: [PATCH 29/91] Correct IP addresses to be used locally / inside docker example container --- example/02-pki-management/00-main-ca.py | 2 +- example/02-pki-management/01-main-server.py | 2 +- example/02-pki-management/02-main-client.py | 2 +- example/02-pki-management/user_routing.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example/02-pki-management/00-main-ca.py b/example/02-pki-management/00-main-ca.py index 22463fe..c61a45b 100644 --- a/example/02-pki-management/00-main-ca.py +++ b/example/02-pki-management/00-main-ca.py @@ -10,7 +10,7 @@ from pymongo import MongoClient -client = MongoClient('mongodb://192.168.61.248/') +client = MongoClient('mongodb://127.0.0.1/') mongodb = client.get_database('microesb') diff --git a/example/02-pki-management/01-main-server.py b/example/02-pki-management/01-main-server.py index 839edd7..7ce0ba6 100644 --- a/example/02-pki-management/01-main-server.py +++ b/example/02-pki-management/01-main-server.py @@ -10,7 +10,7 @@ from pymongo import MongoClient -client = MongoClient('mongodb://192.168.61.248/') +client = MongoClient('mongodb://127.0.0.1/') mongodb = client.get_database('microesb') diff --git a/example/02-pki-management/02-main-client.py b/example/02-pki-management/02-main-client.py index b8fc100..3dbbba2 100644 --- a/example/02-pki-management/02-main-client.py +++ b/example/02-pki-management/02-main-client.py @@ -10,7 +10,7 @@ from pymongo import MongoClient -client = MongoClient('mongodb://192.168.61.248/') +client = MongoClient('mongodb://127.0.0.1/') mongodb = client.get_database('microesb') diff --git a/example/02-pki-management/user_routing.py b/example/02-pki-management/user_routing.py index 902d194..dcf677b 100644 --- a/example/02-pki-management/user_routing.py +++ b/example/02-pki-management/user_routing.py @@ -1,6 +1,6 @@ from pymongo import MongoClient -client = MongoClient('mongodb://192.168.61.248/') +client = MongoClient('mongodb://127.0.0.1/') mongodb = client.get_database('microesb') From 9c35e6ae99985aa33ca8295ca2e42065f74c9972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 16:00:21 +0100 Subject: [PATCH 30/91] Shorten lines (pylint fails) --- src/microesb.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 38f8265..52cd964 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -663,8 +663,7 @@ def _connect_hierarchy(self, cm_ref_dict): Init method for connecting all generated json_dicts. """ - self.logger.debug('Processed class_mapper references dict (containing service hierarchy data):{}'.format(cm_ref_dict)) - + self.logger.debug('Processing class_mapper references dict:{}'.format(cm_ref_dict)) self.logger.debug('Mapping parent_object instances to child instances') self._map_hierarchy_level = -1 @@ -682,7 +681,10 @@ def _connect_hierarchy(self, cm_ref_dict): self._connect_hierarchy_recursive(cm_ref_dict) - self.logger.debug('Class hierarchy list:{} plain:{}'.format(self._class_hierarchy_list, self._class_hierarchy_list_plain)) + self.logger.debug('Class hierarchy list:{} plain:{}'.format( + self._class_hierarchy_list, + self._class_hierarchy_list_plain) + ) for class_hierarchy_item in self._class_hierarchy_list: @@ -705,9 +707,10 @@ def _map_object_instances(self, reference_dict): if 'children' in class_properties: - children_dict = class_properties['children'] + child_dict = class_properties['children'] first_child_key = next(iter(children_dict)) - reference_dict[class_name]['object_instance'] = children_dict[first_child_key]['parent_instance'] + first_child_elm = child_dict[first_child_key] + reference_dict[class_name]['object_instance'] = first_child_elm['parent_instance'] self._map_hierarchy_level += 1 self._map_object_instances(class_properties['children']) @@ -732,7 +735,11 @@ def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent if 'children' in class_properties: self._hierarchy_level += 1 - self._connect_hierarchy_recursive(class_properties['children'], class_name, reference_dict) + self._connect_hierarchy_recursive( + class_properties['children'], + class_name, + reference_dict + ) del self._class_hierarchy[self._hierarchy_level] self._hierarchy_level -= 1 else: @@ -741,7 +748,9 @@ def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent src_instance = getattr(parent_instance, class_name) parent_instance.json_dict[class_name] = src_instance.json_dict - self.logger.debug('Mapping class_name:{} parent_class_name:{}'.format(class_name, parent_class_name)) + self.logger.debug('Mapping class_name:{} parent_class_name:{}'.format( + class_name, + parent_class_name)) tmp_class_hierarchy = copy.deepcopy(self._class_hierarchy) new_class_hierarchy = {} @@ -749,7 +758,9 @@ def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent insert_index = 1 for i in range(1, len(tmp_class_hierarchy)+1): - self.logger.debug('Reorder hierarchy tmp:{} new:{}'.format(tmp_class_hierarchy, new_class_hierarchy)) + self.logger.debug('Reorder hierarchy tmp:{} new:{}'.format( + tmp_class_hierarchy, + new_class_hierarchy)) reorder_index = insert_index+1 try: new_class_hierarchy[reorder_index] = tmp_class_hierarchy[i] @@ -777,7 +788,9 @@ def _rename_dict_key(self, rename_dict, parent_dict=None, parent_class=None): if ChildCounter().get_sum_child_count(dict(rename_dict)) == 0: parent_dict['children_processed'] = parent_dict.pop('children') - self.logger.info('Hierarchy comp:{} orig:{}'.format(self._class_hierarchy, self._class_hierarchy_comp)) + self.logger.info('Hierarchy comp:{} orig:{}'.format( + self._class_hierarchy, + self._class_hierarchy_comp)) for key in list(rename_dict.keys()): self.logger.info('Processing dict key:{} value:{}'.format(key, rename_dict[key])) From b0e486dac56808fc88c68da5bd451c0b5b8e0825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 16:16:15 +0100 Subject: [PATCH 31/91] Update to current milestones / features --- README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a587ceb..ce0fdd5 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,16 @@ *Enterprise Service Bus* is still a pretty vague term, first introduced in the Gartner Report of 2002. -It is essential for running a large SOA infrastructure. +It is essential for maintaining large SOA infrastructures. ## 2. Features Our interpretation of what an ESB should consist of: -- ✅ Service Abstraction / Metadata Definition -- ✅ Centralized Service / API Registry containing clean XML, JSON Model +- ✅ Service Abstraction / Declarative Metadata Modeling +- ✅ Centralized Service / API Registry providing clean XML, JSON Models - ✅ Centralized Service AAA (Authentication / Authorization / Accounting) -- ✅ Internal Service XML / (Python) Class Mapping +- ✅ Service Metadata XML, JSON / Internal (Python) Class Abstraction - ✅ Relational Backend OOP / ORM / ODM Mapper - ✅ Service Model Documentation / API (Auto)-Generation @@ -40,23 +40,24 @@ pip3 install microesb pip3 install pytest pytest-pep8 ``` -## 4. Platform As A Service (PaaS) - -Building web applications on PaaS infrastructure also relies on a clean Service Abstraction Model. +## 4. Platform As A Service (PaaS) / Microservices -> **Note** -> The Python **micro-esb** module will help. +The NoSQL conform JSON abstraction / data transformation capabilities make the micro-esb +suitable for modern, scalable Next-Level applications. ## 5. Current Features - ✅ Service Abstraction / Metadata Definition - ✅ Internal Code (Python) Class / Service Properties Mapping - ✅ Graph-Based / Recursive JSON Result Abstraction +- ✅ OOP Relational ODM Mapper / MongoDB Integration ### 5.1. In Progress -- :hourglass: OOP Relational ODM Mapper - :hourglass: Service Documentation (Auto)-Generation +- :hourglass: Service Registry / Encapsulated Service Routing +- :hourglass: YANG Model Export / Transformation +- :hourglass: Web-Interface / Dashboard ## 6. Documentation / Examples From 97b2672c899a8990ef1a92e4f119d7057107e993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 16:19:28 +0100 Subject: [PATCH 32/91] Add import --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce0fdd5..9e9d178 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ suitable for modern, scalable Next-Level applications. - :hourglass: Service Documentation (Auto)-Generation - :hourglass: Service Registry / Encapsulated Service Routing -- :hourglass: YANG Model Export / Transformation +- :hourglass: YANG Model Import / Export / Transformation - :hourglass: Web-Interface / Dashboard ## 6. Documentation / Examples From d352c580dbc3bf2856ec2c15ed200b79a4d1cc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 16:21:03 +0100 Subject: [PATCH 33/91] Correct variable name --- src/microesb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/microesb.py b/src/microesb.py index 52cd964..cfd1fa0 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -708,7 +708,7 @@ def _map_object_instances(self, reference_dict): if 'children' in class_properties: child_dict = class_properties['children'] - first_child_key = next(iter(children_dict)) + first_child_key = next(iter(child_dict)) first_child_elm = child_dict[first_child_key] reference_dict[class_name]['object_instance'] = first_child_elm['parent_instance'] From 4a8f5571677bf384ffa30f79b25f7f874561959c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 17:09:52 +0100 Subject: [PATCH 34/91] Update version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d84fab8..4457e13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "microesb" -version = "1.0" +version = "1.1" authors = [ { name="Claus Prüfer", email="pruefer@webcodex.de" }, From 67c62f44cc4f88a9dbc536b8fa695dd37be62698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Wed, 7 Jan 2026 17:18:27 +0100 Subject: [PATCH 35/91] Update to new version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 540867c..c0687ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: run: python3 setup.py sdist - name: Install Python module - run: pip3 install ./dist/microesb-1.0.tar.gz + run: pip3 install ./dist/microesb-1.1.tar.gz - name: Run tests run: pytest --cov --cov-branch --cov-report=xml From de3143877cc7895bbc355d1cfcb6456f58ff7385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 06:55:51 +0100 Subject: [PATCH 36/91] Add json_transform integration tests --- test/integration/test_json_transform.py | 242 ++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 test/integration/test_json_transform.py diff --git a/test/integration/test_json_transform.py b/test/integration/test_json_transform.py new file mode 100644 index 0000000..3699f21 --- /dev/null +++ b/test/integration/test_json_transform.py @@ -0,0 +1,242 @@ +import pytest + +from microesb import microesb + + +@pytest.fixture +def config_class_listobject(): + config = { + 'class_mapping': { + 'Shipment': 'Shipment', + 'Palette': 'Palette', + }, + 'class_reference': { + 'Shipment': { + 'property_ref': 'Shipment', + 'children': { + 'Palette': { + 'property_ref': 'Palette', + } + } + } + } + } + return config + + +@pytest.fixture +def config_properties_listobject(): + config = { + 'Shipment': { + 'properties': { + 'id': { + 'type': 'int', + 'default': None, + 'required': True, + 'description': '', + } + } + }, + 'Palette': { + 'properties': { + 'id': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': '', + }, + 'label': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': '', + } + } + } + } + return config + + +@pytest.fixture +def config_service_listobject(): + config = { + 'id': 'processScan', + 'data': [ + { + 'Shipment': { + 'id': 'testshipment1', + 'Palette': [ + { + 'id': 1, + 'label': 'label1', + }, + { + 'id': 2, + 'label': 'label2', + } + ] + } + } + ] + } + return config + + +@pytest.fixture +def config_class_pki_service(): + config = { + 'class_mapping': { + 'CertCA': 'CertCA', + 'SmartcardCA': 'Smartcard', + 'SmartcardREQ': 'Smartcard', + 'SmartcardContainer': 'SmartcardContainer' + }, + 'class_reference': { + 'CertCA': { + 'property_ref': 'Cert', + 'children': { + 'SmartcardCA': { + 'property_ref': 'Smartcard', + 'children': { + 'SmartcardContainer': {'property_ref': 'SmartcardContainer'} + } + }, + 'SmartcardREQ': { + 'property_ref': 'Smartcard', + 'children': { + 'SmartcardContainer': {'property_ref': 'SmartcardContainer'} + } + } + } + } + } + } + return config + + +@pytest.fixture +def config_properties_pki_service(): + config = { + 'Cert': { + 'properties': { + 'id': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': '', + }, + 'country': { + 'type': 'str', + 'default': 'DE', + 'required': True, + 'description': '', + } + } + }, + 'Smartcard': { + 'properties': { + 'label': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': '', + } + } + }, + 'SmartcardContainer': { + 'properties': { + 'label': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': '', + } + } + } + } + return config + + +@pytest.fixture +def config_service_pki(): + config = { + 'id': 'addCACertificate', + 'data': [ + { + 'CertCA': { + 'id': 'testid1', + 'country': 'DE', + 'state': 'Berlin', + 'locality': 'Berlin', + 'org': 'WEBcodeX', + 'org_unit': 'Security', + 'common_name': 'testcn1', + 'email': 'pki@webcodex.de', + 'valid_days': 365, + 'key_ref': 'Smartcard', + 'SmartcardCA': { + 'label': 'label1', + 'user_pin': 'pin1', + 'SmartcardContainer': { + 'label': 'container_label1' + } + }, + 'SmartcardREQ': { + 'label': 'label2', + 'user_pin': 'pin2', + 'SmartcardContainer': { + 'label': 'container_label2' + } + } + } + } + ] + } + return config + + +class TestJSONTransform: + + def test_transform_pki(self, config_class_pki_service, config_properties_pki_service): + + class_mapper = microesb.ClassMapper( + class_references=config_class_extended_service['class_reference'], + class_mappings=config_class_extended_service['class_mapping'], + class_properties=config_properties_extended_service + ) + + r = microesb.ServiceExecuter().execute_get_hierarchy( + class_mapper=class_mapper, + service_data=config_service_pki_service + ) + + root_object = r[0]['CertCA']['object_instance'] + + assert root_object.json_dict == { + 'CertCA': { + 'id': 'testid1', + 'country': 'DE', + 'state': 'Berlin', + 'locality': 'Berlin', + 'org': 'WEBcodeX', + 'org_unit': 'Security', + 'common_name': 'testcn1', + 'email': 'pki@webcodex.de', + 'valid_days': 365, + 'key_ref': 'Smartcard', + 'SmartcardCA': { + 'label': 'label1', + 'user_pin': 'pin1', + 'SmartcardContainer': { + 'label': 'container_label1' + } + }, + 'SmartcardREQ': { + 'label': 'label2', + 'user_pin': 'pin2', + 'SmartcardContainer': { + 'label': 'container_label2' + } + } + } + } From 48afa200c7fc8c8655524737080d86881d5c3dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 06:57:32 +0100 Subject: [PATCH 37/91] Fix memeber names --- test/integration/test_json_transform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/test_json_transform.py b/test/integration/test_json_transform.py index 3699f21..5bdfe05 100644 --- a/test/integration/test_json_transform.py +++ b/test/integration/test_json_transform.py @@ -200,9 +200,9 @@ class TestJSONTransform: def test_transform_pki(self, config_class_pki_service, config_properties_pki_service): class_mapper = microesb.ClassMapper( - class_references=config_class_extended_service['class_reference'], - class_mappings=config_class_extended_service['class_mapping'], - class_properties=config_properties_extended_service + class_references=config_class_pki_service['class_reference'], + class_mappings=config_class_pki_service['class_mapping'], + class_properties=config_properties_pki_service ) r = microesb.ServiceExecuter().execute_get_hierarchy( From fd055624e21dfc64fec2ecc1c41b522b2c741157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 06:59:26 +0100 Subject: [PATCH 38/91] Correct naming --- test/integration/test_json_transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/test_json_transform.py b/test/integration/test_json_transform.py index 5bdfe05..ba784ef 100644 --- a/test/integration/test_json_transform.py +++ b/test/integration/test_json_transform.py @@ -207,7 +207,7 @@ def test_transform_pki(self, config_class_pki_service, config_properties_pki_ser r = microesb.ServiceExecuter().execute_get_hierarchy( class_mapper=class_mapper, - service_data=config_service_pki_service + service_data=config_service_pki ) root_object = r[0]['CertCA']['object_instance'] From 98c6cb42d1c33cfbc7046810ec9e194e7a929941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 07:05:11 +0100 Subject: [PATCH 39/91] Add missing function param --- test/integration/test_json_transform.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/integration/test_json_transform.py b/test/integration/test_json_transform.py index ba784ef..1620621 100644 --- a/test/integration/test_json_transform.py +++ b/test/integration/test_json_transform.py @@ -197,7 +197,12 @@ def config_service_pki(): class TestJSONTransform: - def test_transform_pki(self, config_class_pki_service, config_properties_pki_service): + def test_transform_pki( + self, + config_class_pki_service, + config_properties_pki_service, + config_service_pki + ): class_mapper = microesb.ClassMapper( class_references=config_class_pki_service['class_reference'], From 71cad92bc62f54f069416e376f53a7ff46cca6ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 09:25:02 +0100 Subject: [PATCH 40/91] Fix class alias processing (class mapping) --- src/microesb.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index cfd1fa0..1d0ceee 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -651,18 +651,19 @@ def execute_get_hierarchy(self, class_mapper, service_data): ) rlist.append( - self._connect_hierarchy( - class_mapper_copy.get_references() - ) + self._connect_hierarchy(class_mapper_copy) ) return rlist - def _connect_hierarchy(self, cm_ref_dict): + def _connect_hierarchy(self, class_mapper_ref): """ _connect_hierarchy() method. Init method for connecting all generated json_dicts. """ + self._cm_ref = class_mapper_ref + cm_ref_dict = self._cm_ref.get_references() + self.logger.debug('Processing class_mapper references dict:{}'.format(cm_ref_dict)) self.logger.debug('Mapping parent_object instances to child instances') @@ -701,24 +702,26 @@ def _connect_hierarchy(self, cm_ref_dict): return cm_ref_dict - def _map_object_instances(self, reference_dict): + def _map_object_instances(self, ref_dict): - for class_name, class_properties in reference_dict.items(): + for class_name, class_props in ref_dict.items(): - if 'children' in class_properties: + if 'children' in class_props: - child_dict = class_properties['children'] - first_child_key = next(iter(child_dict)) - first_child_elm = child_dict[first_child_key] - reference_dict[class_name]['object_instance'] = first_child_elm['parent_instance'] + child_dict = class_props['children'] + key_first = next(iter(child_dict)) + elm_first = child_dict[key_first] + cn_mapped = self._cm_ref._get_mapping(class_name) + #ref_dict[class_name]['object_instance'] = elm_first['parent_instance'] + ref_dict[cn_mapped]['object_instance'] = elm_first['parent_instance'] self._map_hierarchy_level += 1 - self._map_object_instances(class_properties['children']) + self._map_object_instances(class_props['children']) self._map_hierarchy_level -= 1 if self._map_hierarchy_level == -1: self.logger.debug('Root object JSON transform:{}'.format(class_name)) - class_properties['object_instance'].json_transform() + class_props['object_instance'].json_transform() def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent_dict=None): """ _connect_hierarchy_recursive() method. @@ -746,7 +749,9 @@ def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent parent_instance = class_properties['parent_instance'] parent_class_name = parent_instance.class_name src_instance = getattr(parent_instance, class_name) - parent_instance.json_dict[class_name] = src_instance.json_dict + cn_mapped = self._cm_ref._get_mapping(class_name) + #parent_instance.json_dict[class_name] = src_instance.json_dict + parent_instance.json_dict[cn_mapped] = src_instance.json_dict self.logger.debug('Mapping class_name:{} parent_class_name:{}'.format( class_name, From e1f66876f773f986e4a945cd744ec8b57b2b944a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 09:56:21 +0100 Subject: [PATCH 41/91] Remove, "should" only be relevant in _connect_hierarchy_recursive() --- src/microesb.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 1d0ceee..23ea13e 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -711,9 +711,7 @@ def _map_object_instances(self, ref_dict): child_dict = class_props['children'] key_first = next(iter(child_dict)) elm_first = child_dict[key_first] - cn_mapped = self._cm_ref._get_mapping(class_name) - #ref_dict[class_name]['object_instance'] = elm_first['parent_instance'] - ref_dict[cn_mapped]['object_instance'] = elm_first['parent_instance'] + ref_dict[class_name]['object_instance'] = elm_first['parent_instance'] self._map_hierarchy_level += 1 self._map_object_instances(class_props['children']) From 6ce21acc660a33f0d496d66823ea0680d0516233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 10:03:29 +0100 Subject: [PATCH 42/91] Remove, problem is in __iter__ and self.iterate() processing wrong __class__ name attribute --- src/microesb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 23ea13e..9097d8a 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -747,9 +747,9 @@ def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent parent_instance = class_properties['parent_instance'] parent_class_name = parent_instance.class_name src_instance = getattr(parent_instance, class_name) - cn_mapped = self._cm_ref._get_mapping(class_name) - #parent_instance.json_dict[class_name] = src_instance.json_dict - parent_instance.json_dict[cn_mapped] = src_instance.json_dict + #cn_mapped = self._cm_ref._get_mapping(class_name) + parent_instance.json_dict[class_name] = src_instance.json_dict + #parent_instance.json_dict[cn_mapped] = src_instance.json_dict self.logger.debug('Mapping class_name:{} parent_class_name:{}'.format( class_name, From 599c230e19f2c2e0c7e69ae1e26c96c43c02fd6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 10:58:50 +0100 Subject: [PATCH 43/91] Fix probably wrong adding of class_ref instead of class_name --- src/microesb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 9097d8a..bd99927 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -50,7 +50,7 @@ def __init__(self): :ivar classref logger: logging logger reference :ivar dict[SYSProperties] _SYSProperties: internal properties processing dict :ivar classref _SYSParentObject: internal (hierarchical) class instance ref - :ivar list[classref] _SYSClassNames: internal class refs dict + :ivar list[classname] _SYSClassNames: internal class names dict """ self.logger = logging.getLogger(__name__) @@ -247,7 +247,7 @@ def _add_class(self, *, class_name, class_ref): :param dict *: used for passing params as **args dictionary :param str class_name: class name - :param classref class_ref: class instance reference + :param classref class_ref: class instance reference name Append class_name to self._SYSClassNames. Setup new class instance in global namespace. @@ -255,7 +255,7 @@ def _add_class(self, *, class_name, class_ref): Primary called by overloaded __add__() method. """ - self._SYSClassNames.append(class_ref) + self._SYSClassNames.append(class_name) new_class = globals()[class_ref] instance = new_class() From 2e67139ea098166152c581584b95153e0adfbf82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 11:37:37 +0100 Subject: [PATCH 44/91] Update integration test --- test/integration/test_json_transform.py | 109 ++++++++++++++++-------- 1 file changed, 74 insertions(+), 35 deletions(-) diff --git a/test/integration/test_json_transform.py b/test/integration/test_json_transform.py index 1620621..094d205 100644 --- a/test/integration/test_json_transform.py +++ b/test/integration/test_json_transform.py @@ -15,7 +15,7 @@ def config_class_listobject(): 'property_ref': 'Shipment', 'children': { 'Palette': { - 'property_ref': 'Palette', + 'property_ref': 'Palette' } } } @@ -33,7 +33,7 @@ def config_properties_listobject(): 'type': 'int', 'default': None, 'required': True, - 'description': '', + 'description': '' } } }, @@ -43,13 +43,13 @@ def config_properties_listobject(): 'type': 'str', 'default': None, 'required': True, - 'description': '', + 'description': '' }, 'label': { 'type': 'str', 'default': None, 'required': True, - 'description': '', + 'description': '' } } } @@ -68,11 +68,11 @@ def config_service_listobject(): 'Palette': [ { 'id': 1, - 'label': 'label1', + 'label': 'label1' }, { 'id': 2, - 'label': 'label2', + 'label': 'label2' } ] } @@ -123,13 +123,19 @@ def config_properties_pki_service(): 'type': 'str', 'default': None, 'required': True, - 'description': '', + 'description': 'cert id' }, 'country': { 'type': 'str', 'default': 'DE', 'required': True, - 'description': '', + 'description': 'country' + }, + 'state': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': '' } } }, @@ -139,7 +145,13 @@ def config_properties_pki_service(): 'type': 'str', 'default': None, 'required': True, - 'description': '', + 'description': 'label' + }, + 'user_pin': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'user pin' } } }, @@ -149,7 +161,7 @@ def config_properties_pki_service(): 'type': 'str', 'default': None, 'required': True, - 'description': '', + 'description': 'label' } } } @@ -195,7 +207,7 @@ def config_service_pki(): return config -class TestJSONTransform: +class TestExecuteGetHierarchy: def test_transform_pki( self, @@ -218,30 +230,57 @@ def test_transform_pki( root_object = r[0]['CertCA']['object_instance'] assert root_object.json_dict == { - 'CertCA': { - 'id': 'testid1', - 'country': 'DE', - 'state': 'Berlin', - 'locality': 'Berlin', - 'org': 'WEBcodeX', - 'org_unit': 'Security', - 'common_name': 'testcn1', - 'email': 'pki@webcodex.de', - 'valid_days': 365, - 'key_ref': 'Smartcard', - 'SmartcardCA': { - 'label': 'label1', - 'user_pin': 'pin1', - 'SmartcardContainer': { - 'label': 'container_label1' - } - }, - 'SmartcardREQ': { - 'label': 'label2', - 'user_pin': 'pin2', - 'SmartcardContainer': { - 'label': 'container_label2' - } + 'id': 'testid1', + 'country': 'DE', + 'state': 'Berlin', + 'SmartcardCA': { + 'label': 'label1', + 'user_pin': 'pin1', + 'SmartcardContainer': { + 'label': 'container_label1' + } + }, + 'SmartcardREQ': { + 'label': 'label2', + 'user_pin': 'pin2', + 'SmartcardContainer': { + 'label': 'container_label2' } } } + + def test_list_object( + self, + config_service_listobject, + config_class_listobject, + config_properties_listobject + ): + + class_mapper = microesb.ClassMapper( + class_references=config_class_listobject['class_reference'], + class_mappings=config_class_listobject['class_mapping'], + class_properties=config_properties_listobject + ) + + r = microesb.ServiceExecuter().execute_get_hierarchy( + class_mapper=class_mapper, + service_data=config_service_listobject + ) + + root_object = r[0]['Shipment']['object_instance'] + + assert root_object.json_dict == { + 'id': 'testshipment1', + 'Palette': { + 'Palette': [ + { + 'id': 1, + 'label': 'label1' + }, + { + 'id': 2, + 'label': 'label2' + } + ] + } + } From 4ef435aa2a8fa02ba82d63fc4e1748ad5bdbb965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 11:39:02 +0100 Subject: [PATCH 45/91] Fix defining member outside __init__() --- src/microesb.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/microesb.py b/src/microesb.py index bd99927..5ab09ee 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -611,6 +611,7 @@ def __init__(self): self.logger = logging.getLogger(__name__) + self._cm_ref = None self._con_ref_dict = None self._class_hierarchy = None self._class_hierarchy_list = None From 2794c1b807b19480d008b17853e8806ca7f6834e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 15:43:06 +0100 Subject: [PATCH 46/91] Cleanup dirty code --- src/microesb.py | 46 +++++++++++++--------------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 5ab09ee..1b2f82e 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -615,7 +615,6 @@ def __init__(self): self._con_ref_dict = None self._class_hierarchy = None self._class_hierarchy_list = None - self._class_hierarchy_list_plain = None self._hierarchy_level = None self._map_hierarchy_level = None self._class_hierarchy_comp = None @@ -748,34 +747,15 @@ def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent parent_instance = class_properties['parent_instance'] parent_class_name = parent_instance.class_name src_instance = getattr(parent_instance, class_name) - #cn_mapped = self._cm_ref._get_mapping(class_name) parent_instance.json_dict[class_name] = src_instance.json_dict - #parent_instance.json_dict[cn_mapped] = src_instance.json_dict self.logger.debug('Mapping class_name:{} parent_class_name:{}'.format( class_name, parent_class_name)) - tmp_class_hierarchy = copy.deepcopy(self._class_hierarchy) - new_class_hierarchy = {} - new_class_hierarchy[0] = tmp_class_hierarchy[0] - - insert_index = 1 - for i in range(1, len(tmp_class_hierarchy)+1): - self.logger.debug('Reorder hierarchy tmp:{} new:{}'.format( - tmp_class_hierarchy, - new_class_hierarchy)) - reorder_index = insert_index+1 - try: - new_class_hierarchy[reorder_index] = tmp_class_hierarchy[i] - except KeyError as e: - pass - new_class_hierarchy[insert_index] = 'children' - insert_index +=2 - - self.logger.debug('Append hierarchy:{}'.format(new_class_hierarchy)) - self._class_hierarchy_list.append(new_class_hierarchy) - self._class_hierarchy_list_plain.append(tmp_class_hierarchy) + self._class_hierarchy_list.append( + copy.deepcopy(self._class_hierarchy) + ) def _rename_dict_key(self, rename_dict, parent_dict=None, parent_class=None): @@ -788,19 +768,19 @@ def _rename_dict_key(self, rename_dict, parent_dict=None, parent_class=None): if self._class_hierarchy == self._class_hierarchy_comp: self.logger.info('Match rename_dict:{}'.format(rename_dict)) - # only remove when all children have been altered to children_processed + # only remove when all 'children' keys have been altered to 'children_processed' if ChildCounter().get_sum_child_count(dict(rename_dict)) == 0: - parent_dict['children_processed'] = parent_dict.pop('children') - - self.logger.info('Hierarchy comp:{} orig:{}'.format( - self._class_hierarchy, - self._class_hierarchy_comp)) + self.logger.info('Parent dict:{}'.format(parent_dict)) + parent_dict[parent_class]['children_processed'] = parent_dict[parent_class].pop('children') - for key in list(rename_dict.keys()): - self.logger.info('Processing dict key:{} value:{}'.format(key, rename_dict[key])) - if isinstance(rename_dict[key], dict) and key != 'hierarchy': + for class_name, class_properties in rename_dict.items(): + if 'children' in class_properties: self._hierarchy_level_comp += 1 - self._rename_dict_key(rename_dict[key], rename_dict, key) + self._rename_dict_key( + class_properties['children'], + rename_dict, + class_name + ) del self._class_hierarchy_comp[self._hierarchy_level_comp] self._hierarchy_level_comp -= 1 From 20ceb16be591a78bd205234fd8261c8bb05e63d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 15:51:44 +0100 Subject: [PATCH 47/91] Fix pylint --- src/microesb.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 1b2f82e..b97eeb1 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -678,13 +678,11 @@ def _connect_hierarchy(self, class_mapper_ref): self._hierarchy_level = -1 self._class_hierarchy = {} self._class_hierarchy_list = [] - self._class_hierarchy_list_plain = [] self._connect_hierarchy_recursive(cm_ref_dict) - self.logger.debug('Class hierarchy list:{} plain:{}'.format( - self._class_hierarchy_list, - self._class_hierarchy_list_plain) + self.logger.debug('Class hierarchy list:{}'.format( + self._class_hierarchy_list) ) for class_hierarchy_item in self._class_hierarchy_list: @@ -771,7 +769,8 @@ def _rename_dict_key(self, rename_dict, parent_dict=None, parent_class=None): # only remove when all 'children' keys have been altered to 'children_processed' if ChildCounter().get_sum_child_count(dict(rename_dict)) == 0: self.logger.info('Parent dict:{}'.format(parent_dict)) - parent_dict[parent_class]['children_processed'] = parent_dict[parent_class].pop('children') + set_dict = parent_dict[parent_class].pop('children') + parent_dict[parent_class]['children_processed'] = set_dict for class_name, class_properties in rename_dict.items(): if 'children' in class_properties: From 66f5a9e82e529eb5406c6b2e0fd5ab79e51ae9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 16:20:49 +0100 Subject: [PATCH 48/91] Remove outdated reference --- doc/source/design.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/source/design.rst b/doc/source/design.rst index 74aa433..018f036 100644 --- a/doc/source/design.rst +++ b/doc/source/design.rst @@ -22,10 +22,6 @@ The following diagram illustrates the *Base Layout / Model* of a **scalable SOA The **microesb** module focuses on the "Service Mapping / Abstraction Layer" (4). -.. note:: - - The "Load Balancing Layer" and the "Transport Layer Security Layer" can be easily adapted using a Kubernetes Cluster / Ingress-nginx (see Example 2). - 2. Centralized Service Management ================================= From 30a8fb66cc64feca7bf9aa444ec7e160b334467b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 16:26:27 +0100 Subject: [PATCH 49/91] Add example2 ref --- doc/source/design.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/design.rst b/doc/source/design.rst index 018f036..ee6c89d 100644 --- a/doc/source/design.rst +++ b/doc/source/design.rst @@ -77,7 +77,7 @@ The primary focus of the Python **microesb** module is to map a clean JSON metad .. note:: - For a detailed example that includes transactional database access, refer to Example 1. + For a detailed example that includes transactional database access, refer to Example 1. Example 2 demonstrates a NoSQL approach using MongoDB. 4. Platform As A Service ======================== From eae6153ca5b15c7fba563413bb27898a092f06d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 16:53:45 +0100 Subject: [PATCH 50/91] Remove unclear statements --- doc/source/examples.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 1ada9a0..04c9091 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -194,11 +194,6 @@ After execution, the newly created domain will be in the `sys_core."domain"` tab 2. PKI Provisioning / Class Types ================================= -Example number 1 only covers a "plain" database model without local (e.g., bash) or remote (web service) invocations. - -.. note:: - - Example number 2 is a stripped-down excerpt from PKI management to demonstrate how *virtual class types* and a *clean OOP model setup* work. 2.1. CA Certificate Relations ****************************** From 3e00278274d73b40a578ab73cb79cad46fe2d144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 16:56:18 +0100 Subject: [PATCH 51/91] Remove example 4 --- doc/source/examples.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 04c9091..c5e97a0 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -526,10 +526,3 @@ Here is an example configuration for Alias Class Mapping: } .. _example-number4: - -4. SOA on Kubernetes -==================== - -This example includes a working Docker container and Kubernetes Minikube setup. It will be implemented after the FalconAS milestone *HTTP/1.1 implementation* is completed. - -For more information, see: https://github.com/WEBcodeX1/http-1.2. From 857d2db0739428e9f63059e03b882cd08e30ca8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 17:03:28 +0100 Subject: [PATCH 52/91] Correct / update future features --- doc/source/features.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/source/features.rst b/doc/source/features.rst index 9cd963e..b42575e 100644 --- a/doc/source/features.rst +++ b/doc/source/features.rst @@ -85,10 +85,11 @@ For the full example, see :ref:`example-number2`. 5. Planned Features ==================== -The following features are planned for future releases: - -- Service Registry -- Service Registry Management -- Service-Based AAA (HSM / Smartcard) -- Automatic Service Interface Documentation Generation -- Integration with the x0 JavaScript Framework +Planned for upcoming releases: + +- Service Registry / API Server +- Service Registry / YANG Model Export +- Service Registry - Web Interface +- Service API Auto Documentation +- Extended "Encapsulated" Service Routing +- Mincepy Integration / Metadata Mapping From 9a341a9602cf5c5fcbed26e263edad8ba05c0dc2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:13:00 +0000 Subject: [PATCH 53/91] Initial plan From 5bf3a5f12317862a187ec8069c6517b7260e7613 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:17:41 +0000 Subject: [PATCH 54/91] Update documentation for version 1.1 release Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- CHANGELOG.md | 11 ++++ doc/source/api.rst | 119 +++++++++++++++++++++++++++++++++++++++- doc/source/conf.py | 4 +- doc/source/examples.rst | 20 +++++++ doc/source/index.rst | 1 + doc/source/intro.rst | 14 ++++- doc/source/routing.rst | 107 ++++++++++++++++++++++++++++++++++++ example/README.md | 37 +++++++++++++ 8 files changed, 306 insertions(+), 7 deletions(-) create mode 100644 doc/source/routing.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index ba6ba6f..ebb8956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ ## Version 1.1 +- Add user-based service call routing via ServiceRouter class +- Introduce `property_dict` functionality for flexible property access excluding internal system properties +- Support recursive class hierarchy object deserialization in ServiceExecuter +- Improve JSONTransformer robustness with `_SYSClassNames` tracking for hierarchy processing +- Add property registration feature for internal system properties via `register_property()` +- Modify Example 02 to use NoSQL MongoDB backend for certificate management +- Add Example 04 with NLAP proxy integration documentation +- Add comprehensive unit and integration tests for recursive json_transform() +- Cleanup recursive hierarchy processing code in `execute_get_hierarchy()` +- Update documentation for version 1.1 features + ## Version 1.0 - Working pylint checks diff --git a/doc/source/api.rst b/doc/source/api.rst index fbf0347..c36310b 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -30,11 +30,48 @@ Example test case (`/test/integration/test_base.py`, test: `test_multi_item_obje 'id': 'testshipment1', 'SYSServiceMethod': None, 'Palette': [ - {'id': 1, 'label': 'label1'}, - {'id': 2, 'label': 'label2'} + {'id': 1, 'label': 'label2'} ] } +1.1. Property Dict +****************** + +The ``property_dict`` property (decorated with ``@property``) provides access to all class instance +properties excluding internal system properties like ``SYSServiceMethod`` and class references. + +This enables clean property access for serialization, logging, or external API integration without +exposing internal framework properties. + +Example usage: + +.. code-block:: python + + # Access all user-defined properties + properties = instance.property_dict + # Returns: {'id': 'value', 'name': 'test', ...} + # Excludes: 'SYSServiceMethod' and internal references + +1.2. Property Registration +*************************** + +Properties can be registered with metadata using the ``register_property()`` method. This is +useful for defining internal system properties that should be handled specially by the framework. + +Example from ``/example/02-pki-management/service_implementation.py``: + +.. code-block:: python + + self.register_property( + property_id='is_ca_cert', + property_data={ + 'type': 'bool', + 'default': True, + 'required': False, + 'description': 'Is CA Certificate' + } + ) + 2. Class Handler ================ @@ -251,3 +288,81 @@ Example: class_mapper=class_mapper, service_data=service_metadata ) + +7. Service Router +================= + +The `ServiceRouter` class provides user-defined service call routing capabilities. It enables +direct routing of service calls to user-defined functions in a ``user_routing.py`` module. + +This is particularly useful for: +- Database CRUD operations (MongoDB, PostgreSQL, etc.) +- External API integrations +- Custom business logic execution +- Decoupling service orchestration from implementation + +7.1. Usage +********** + +.. code-block:: python + + from microesb.router import ServiceRouter + + router = ServiceRouter() + result = router.send('FunctionName', metadata) + +The ``send()`` method dynamically invokes the specified function from the ``user_routing`` module +and passes the metadata as the first argument. + +7.2. User Routing Module +************************* + +Create a ``user_routing.py`` file in your project with functions that handle specific routing targets: + +.. code-block:: python + + from pymongo import MongoClient + + client = MongoClient('mongodb://127.0.0.1/') + mongodb = client.get_database('mydb') + + def GetDataById(metadata): + return mongodb.collection.find_one({"id": metadata}) + + def StoreData(metadata): + mongodb.collection.insert_one(metadata) + return True + +See the :ref:`routing` section for detailed routing documentation. + +8. Service Executer +=================== + +The `ServiceExecuter` class handles the execution of service methods and manages recursive class +hierarchy object deserialization. This enables complex hierarchical data structures to be properly +reconstructed from JSON representations. + +8.1. Recursive Deserialization +******************************* + +The framework automatically deserializes nested class hierarchies, ensuring that: +- Child class instances are properly instantiated +- Parent-child relationships are maintained +- JSON data is correctly mapped to class properties + +This is particularly useful when working with complex domain models that include multiple levels +of nested objects. + +Example: + +.. code-block:: python + + # Hierarchical structure: CertClient -> CertServer -> CertCA + # The framework automatically deserializes all levels + + service_executer = microesb.ServiceExecuter( + class_mapper=class_mapper, + service_mapper=service_mapper + ) + + result = service_executer.execute() diff --git a/doc/source/conf.py b/doc/source/conf.py index 80af39f..5b31cf3 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -12,8 +12,8 @@ project = 'Python microesb' copyright = '2016 - 2024, Claus Prüfer' author = 'Claus Prüfer' -version = '1.0' -release = '1.0' +version = '1.1' +release = '1.1' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/doc/source/examples.rst b/doc/source/examples.rst index c5e97a0..966c2fc 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -15,6 +15,10 @@ In this example, assume our "virtual" company runs a **Hosting Business**. The company's customer data, including a) Internet Domains and b) DNS Hostnames, should be manageable by different subsystems. +This example demonstrates the integration with **PostgreSQL database transactions**, showcasing +how the microesb framework handles atomic operations across multiple related entities while +maintaining data integrity through commit/rollback mechanisms. + .. note:: Example number 1 will only cover Local Service Mapping **without** *Python Application Server* encapsulation. @@ -194,6 +198,22 @@ After execution, the newly created domain will be in the `sys_core."domain"` tab 2. PKI Provisioning / Class Types ================================= +This example demonstrates PKI (Public Key Infrastructure) certificate provisioning with a focus +on class type hierarchies and **NoSQL MongoDB backend integration**. + +Unlike Example 1's relational database approach, this example showcases how the microesb framework +seamlessly integrates with document-based NoSQL databases. MongoDB is used for storing and retrieving +certificate metadata, demonstrating the framework's flexibility in handling both traditional and +modern database paradigms. + +The example implements a complete certificate generation workflow for: +- **Certificate Authority (CA)** certificates +- **Server** certificates +- **Client** certificates + +Each certificate type can optionally use Hardware Security Module (HSM) / Smartcard integration +for secure key pair generation. The implementation uses user-defined routing functions to interact +with MongoDB for certificate storage and retrieval operations. 2.1. CA Certificate Relations ****************************** diff --git a/doc/source/index.rst b/doc/source/index.rst index 6914ff6..3365f90 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -12,6 +12,7 @@ Contents: features apidoc api + routing examples Indices and tables diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 669dedd..5152b4a 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -5,9 +5,12 @@ Intro / Module Description ========================== The **microesb** Python module provides the foundational features to build a centralized, -structured Enterprise Service Bus (ESB) / SOA architecture. +structured Enterprise Service Bus (ESB) / Service-Oriented Architecture (SOA). -Its primary feature is a clean, OOP-based **Service Model to Python Class Mapping**. +It enables clean **Service Model to Python Class Mapping** with support for both traditional +relational databases and modern NoSQL backends. The module features user-based service call +routing, recursive class hierarchy object deserialization, and graph-based JSON result abstraction, +making it suitable for scalable Next-Level Platform-as-a-Service (PaaS) and Microservices architectures. A Docker example container can be downloaded here: `microesb-examples-latest.tar `_. @@ -39,13 +42,18 @@ Using the **microesb** module generally does not require the **psycopg2** Postgr Python module. .. warning:: - Running example code requires **psycopg2**. + Running example code requires **psycopg2** for Example 1 and **pymongo** for Example 2. .. code-block:: bash # install psycopg2 apt-get install python3-psycopg2 +.. note:: + Example 2 uses MongoDB as a NoSQL backend. MongoDB cannot be installed via apt on all + systems and is preinstalled in the Docker example container. For local development, + install MongoDB separately or use the provided Docker container. + 4. Build Dependencies ===================== diff --git a/doc/source/routing.rst b/doc/source/routing.rst new file mode 100644 index 0000000..23043de --- /dev/null +++ b/doc/source/routing.rst @@ -0,0 +1,107 @@ +.. routing + +======= +Routing +======= + +The **microesb** module provides flexible routing capabilities to direct service calls to +appropriate backend implementations. This enables separation of concerns between service +orchestration and business logic implementation. + +1. Simple Routing +================= + +Simple Routing allows direct, unencapsulated routing of service calls to user-defined functions. +This approach is suitable for straightforward use cases where service calls map directly to +backend operations without complex orchestration requirements. + +1.1. Overview +************* + +Simple routing uses the ``ServiceRouter`` class to dynamically invoke functions defined in a +``user_routing.py`` module. Each routing function receives metadata from the service call and +returns the result of the backend operation. + +1.2. Implementation +******************* + +To implement simple routing, create a ``user_routing.py`` module in your project with functions +that match the routing identifiers used in your service calls. + +Example from ``/example/02-pki-management/user_routing.py``: + +.. literalinclude:: ../../example/02-pki-management/user_routing.py + :linenos: + +1.3. Usage Pattern +****************** + +The routing functions are invoked automatically by the framework when a service call specifies +a routing target. Each function receives the relevant metadata and can interact with databases, +external services, or other backend systems. + +**Key characteristics:** + +- Direct function mapping: Service routing IDs map to function names in ``user_routing.py`` +- Flexible return values: Functions can return data structures that match service requirements +- Database integration: Common pattern for MongoDB, PostgreSQL, or other data stores +- Stateless operations: Each routing function operates independently + +1.4. Common Use Cases +********************* + +Simple routing is ideal for: + +- Database CRUD operations (Create, Read, Update, Delete) +- External API integrations +- File system operations +- Cache management +- Message queue interactions + +1.5. Error Handling +******************* + +The ``ServiceRouter`` class provides basic error handling for missing routing targets. For +production deployments, implement appropriate error handling within your routing functions +to manage database connection failures, validation errors, and other exceptional conditions. + +2. Encapsulated Routing +======================= + +Encapsulated Routing provides a more sophisticated approach with service registry integration, +authentication, authorization, and load balancing capabilities. + +.. note:: + + Encapsulated Routing will be integrated in a future release and will include: + + - Service Registry integration for dynamic service discovery + - Built-in Authentication and Authorization (AAA) mechanisms + - Load balancing across multiple service instances + - Service versioning and compatibility management + - Centralized logging and monitoring + - Circuit breaker patterns for fault tolerance + +2.1. Planned Features +********************* + +The encapsulated routing implementation will provide: + +**Service Registry:** + Centralized registry for service endpoint discovery and metadata management. + +**AAA Integration:** + Authentication, Authorization, and Accounting for secure service access control. + +**Load Balancing:** + Distribute service calls across multiple backend instances for scalability. + +**Service Mesh:** + Advanced service-to-service communication with retry logic, timeouts, and fallbacks. + +2.2. Migration Path +******************* + +Applications using Simple Routing will be able to migrate to Encapsulated Routing with minimal +code changes. The routing interface will remain backwards compatible while providing optional +advanced features through configuration. diff --git a/example/README.md b/example/README.md index cb47e34..fd69b10 100644 --- a/example/README.md +++ b/example/README.md @@ -1,5 +1,42 @@ # Python MicroESB Examples +This directory contains comprehensive examples demonstrating different database paradigms and use cases with the microesb framework. + +## Example 1: Hosting Use Case - Relational Database Transactions + +Example 1 demonstrates the **classical relational database transactional model** using PostgreSQL. + +**Key Features:** +- **ACID Transactions:** Demonstrates atomic operations with commit/rollback functionality +- **Relational Integrity:** Shows how to maintain referential integrity across related entities (Customer → Domain → DNS Records) +- **Transaction Management:** Illustrates proper database transaction handling with the microesb framework +- **Normalized Schema:** Uses a normalized PostgreSQL database schema with foreign key relationships + +This example is ideal for understanding how microesb integrates with traditional SQL databases where data consistency and transaction atomicity are critical requirements. + +## Example 2: PKI Management - NoSQL Document Model + +Example 2 showcases the **NoSQL document-oriented model** using MongoDB. + +**Key Features:** +- **Document Storage:** Demonstrates flexible schema design with MongoDB collections +- **Denormalized Data:** Shows how to work with embedded documents and flexible structures +- **Schema Evolution:** Illustrates the advantages of schema-less document storage +- **User-Defined Routing:** Implements custom routing functions for MongoDB CRUD operations +- **Horizontal Scalability:** Designed for scenarios requiring easy horizontal scaling + +This example highlights how microesb seamlessly works with modern NoSQL databases, making it suitable for applications requiring rapid development, flexible data models, and high scalability. + +## Database Model Comparison + +| Aspect | Example 1 (PostgreSQL) | Example 2 (MongoDB) | +|--------|------------------------|---------------------| +| **Model** | Relational / Normalized | Document / Denormalized | +| **Transactions** | ACID compliant | Eventually consistent | +| **Schema** | Fixed schema | Flexible schema | +| **Relationships** | Foreign keys | Embedded documents | +| **Use Case** | Data integrity critical | Rapid development, scalability | + Refer to detailed documentation at: https://pythondocs.webcodex.de/micro-esb/examples.html From f928043c54ac37b9cdcc790cb3fbe1b57a5d809e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:19:17 +0000 Subject: [PATCH 55/91] Fix documentation cross-references and test data consistency Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- doc/source/api.rst | 2 +- doc/source/routing.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/api.rst b/doc/source/api.rst index c36310b..de7be5a 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -30,7 +30,7 @@ Example test case (`/test/integration/test_base.py`, test: `test_multi_item_obje 'id': 'testshipment1', 'SYSServiceMethod': None, 'Palette': [ - {'id': 1, 'label': 'label2'} + {'id': 1, 'label': 'label1'} ] } diff --git a/doc/source/routing.rst b/doc/source/routing.rst index 23043de..da98cfc 100644 --- a/doc/source/routing.rst +++ b/doc/source/routing.rst @@ -1,4 +1,4 @@ -.. routing +.. _routing: ======= Routing From 459c47d120790661fcde7e79312ec8649b2c2eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 17:31:49 +0100 Subject: [PATCH 56/91] Update api.rst --- doc/source/api.rst | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/doc/source/api.rst b/doc/source/api.rst index de7be5a..37124fb 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -22,16 +22,20 @@ It provides template functions described in the *Internal Classes / Class Repres It inherits `transformer.JSONTransformer` to enable a generic, recursive dictionary representation of the class instance hierarchy. This feature allows for efficient assertions on complex object hierarchies. -Example test case (`/test/integration/test_base.py`, test: `test_multi_item_object`): +Example test case (`/test/integration/test_json_transform.py`, test: `test_list_object`): .. code-block:: python - assert shipment.json_dict == { + root_object = r[0]['Shipment']['object_instance'] + + assert root_object.json_dict == { 'id': 'testshipment1', - 'SYSServiceMethod': None, - 'Palette': [ - {'id': 1, 'label': 'label1'} - ] + 'Palette': { + 'Palette': [ + { 'id': 1, 'label': 'label1' }, + { 'id': 2, 'label': 'label2' } + ] + } } 1.1. Property Dict From cd7daec0e85586ea09b8274cde0eaec6124d86a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 17:33:34 +0100 Subject: [PATCH 57/91] Refactor comments for example usage in api.rst Updated comments for consistency in casing and style. --- doc/source/api.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/api.rst b/doc/source/api.rst index 37124fb..adf648e 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -51,10 +51,10 @@ Example usage: .. code-block:: python - # Access all user-defined properties + # access all user-defined properties properties = instance.property_dict - # Returns: {'id': 'value', 'name': 'test', ...} - # Excludes: 'SYSServiceMethod' and internal references + # returns: {'id': 'value', 'name': 'test', ...} + # excludes: 'SYSServiceMethod' and internal references 1.2. Property Registration *************************** @@ -361,8 +361,8 @@ Example: .. code-block:: python - # Hierarchical structure: CertClient -> CertServer -> CertCA - # The framework automatically deserializes all levels + # hierarchical structure: CertClient -> CertServer -> CertCA + # the framework automatically deserializes all levels service_executer = microesb.ServiceExecuter( class_mapper=class_mapper, From ed686e95fe9053bc5a94b3b9ecb36c1bc3573aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sun, 11 Jan 2026 17:35:29 +0100 Subject: [PATCH 58/91] Update service execution method and retrieve root object --- doc/source/api.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/api.rst b/doc/source/api.rst index adf648e..0730c45 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -369,4 +369,6 @@ Example: service_mapper=service_mapper ) - result = service_executer.execute() + result = service_executer.execute_get_hierarchy() + root_object = r[0]['RootObjectID']['object_instance'] + print(root_object.json_dict) From 13e1247bba05a866996c7bd29d968fe2e35f367c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 09:12:18 +0000 Subject: [PATCH 59/91] Initial plan From 6e532c174e84f830c97a43efbc9e0f938bd4e182 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 09:15:06 +0000 Subject: [PATCH 60/91] Add README.md for example 2 PKI management with MongoDB details Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- example/02-pki-management/README.md | 182 ++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 example/02-pki-management/README.md diff --git a/example/02-pki-management/README.md b/example/02-pki-management/README.md new file mode 100644 index 0000000..69c33d5 --- /dev/null +++ b/example/02-pki-management/README.md @@ -0,0 +1,182 @@ +# Example 2: PKI Management with NoSQL (MongoDB) + +This example demonstrates a **Public Key Infrastructure (PKI) Certificate Management System** using the microesb framework with a **NoSQL MongoDB** backend. + +## Overview + +Example 2 showcases how microesb can manage a hierarchical PKI system with three certificate types: + +- **Certificate Authority (CA)** - Root certificate that signs server and client certificates +- **Server Certificates** - Signed by the CA, used for server authentication +- **Client Certificates** - Signed by the CA, used for client authentication with server verification + +The example demonstrates: +- **NoSQL Document Storage** using MongoDB for flexible certificate data management +- **User-Defined Routing** with custom routing functions for MongoDB operations +- **Hierarchical Class Dependencies** showing CA → Server → Client certificate relationships +- **Smartcard/HSM Integration** for secure key storage (simulated in this example) + +## Prerequisites + +### MongoDB Requirement + +This example requires a running MongoDB instance. You have two options: + +1. **Local MongoDB Installation:** + ```bash + # Install MongoDB (varies by OS) + # For Ubuntu/Debian: + sudo apt-get install mongodb + + # Start MongoDB service + sudo systemctl start mongodb + ``` + +2. **Docker Example Container (Recommended):** + Use the shipped docker example container which includes MongoDB pre-installed and pre-configured: + ```bash + # Navigate to the docker example directory + cd docker/example + + # Build and run the container + docker-compose up -d + ``` + +### Python Dependencies + +- Python 3.8 or later +- pymongo library +- microesb package + +## Execution Order + +**IMPORTANT:** The execution order of the scripts is critical due to certificate dependencies: + +1. **First:** `00-main-ca.py` - Creates the Certificate Authority (CA) +2. **Second:** `01-main-server.py` - Creates Server Certificate (requires CA to exist) +3. **Third:** `02-main-client.py` - Creates Client Certificate (requires both CA and Server certificates to exist) + +The hierarchical dependencies mean: +- Server certificates require the CA certificate to be created first (for signing) +- Client certificates require both CA (for signing) and Server certificates (for verification) + +Running scripts out of order will result in missing certificate references in the MongoDB database. + +## Execution + +Navigate to the example directory and execute the scripts in the correct order: + +```bash +# Step 1: Create Certificate Authority +python3 ./00-main-ca.py + +# Step 2: Create Server Certificate +python3 ./01-main-server.py + +# Step 3: Create Client Certificate +python3 ./02-main-client.py +``` + +## How It Works + +### 1. Service Metadata + +Each script uses different service metadata defining the certificate hierarchy: + +- `service_metadata_ca` - CA certificate with Smartcard container +- `service_metadata_server` - Server certificate referencing CA +- `service_metadata_client` - Client certificate referencing both CA and Server + +See: [service_call_metadata.py](service_call_metadata.py) + +### 2. Class Hierarchy + +The class reference structures define parent-child relationships: + +``` +CertCA +├── Smartcard +│ └── SmartcardContainer + +CertServer +├── Smartcard +│ └── SmartcardContainer +└── CertCA (reference) + └── Smartcard + └── SmartcardContainer + +CertClient +├── Smartcard +│ └── SmartcardContainer +├── CertCA (reference) +│ └── Smartcard +│ └── SmartcardContainer +└── CertServer (reference) + └── Smartcard + └── SmartcardContainer +``` + +See: [class_reference.py](class_reference.py) + +### 3. User-Defined Routing + +The `user_routing.py` module provides custom routing functions for MongoDB operations: + +- `CertGetById(metadata)` - Retrieves certificate documents by ID +- `CertStore(metadata)` - Stores certificate documents +- `KeypairGenerate(metadata)` - Simulates key pair generation + +See: [user_routing.py](user_routing.py) + +### 4. MongoDB Collections + +The example uses the following MongoDB collections in the `microesb` database: + +- `cert` - Individual certificate data with properties +- `cert_hierarchy` - Complete certificate hierarchies with all related objects + +### 5. Class Implementation + +The implementation includes abstract base class `Cert` with three concrete implementations: + +- `CertCA` - Certificate Authority implementation +- `CertServer` - Server certificate implementation +- `CertClient` - Client certificate implementation + +Each class handles: +- Loading referenced certificate data from MongoDB +- Simulating OpenSSL certificate generation +- Smartcard/HSM key pair generation (simulated) +- Storing generated certificate data + +See: [service_implementation.py](service_implementation.py) + +## Post-Execution + +After executing all three scripts, you can verify the data in MongoDB: + +```bash +# Connect to MongoDB +mongo + +# Switch to microesb database +use microesb + +# View individual certificates +db.cert.find().pretty() + +# View complete certificate hierarchies +db.cert_hierarchy.find().pretty() +``` + +## Key Features Demonstrated + +1. **NoSQL Flexibility:** MongoDB's schema-less design allows flexible certificate document structures +2. **Hierarchical Processing:** Automatic deserialization of nested certificate references +3. **Custom Routing:** User-defined routing functions for database operations +4. **Property Registration:** Dynamic property management with type checking and validation +5. **Recursive Class Hierarchy:** Automatic resolution and connection of certificate dependencies + +## Additional Information + +For more detailed documentation, see: [https://pythondocs.webcodex.de/micro-esb/examples.html](https://pythondocs.webcodex.de/micro-esb/examples.html) From 76ceab1ea5a47d3377a0ca1369e01af086a9b8e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 09:17:58 +0000 Subject: [PATCH 61/91] Fix MongoDB installation instructions in example 2 README Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- example/02-pki-management/README.md | 39 +++++++++++++---------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/example/02-pki-management/README.md b/example/02-pki-management/README.md index 69c33d5..ceae289 100644 --- a/example/02-pki-management/README.md +++ b/example/02-pki-management/README.md @@ -20,27 +20,24 @@ The example demonstrates: ### MongoDB Requirement -This example requires a running MongoDB instance. You have two options: - -1. **Local MongoDB Installation:** - ```bash - # Install MongoDB (varies by OS) - # For Ubuntu/Debian: - sudo apt-get install mongodb - - # Start MongoDB service - sudo systemctl start mongodb - ``` - -2. **Docker Example Container (Recommended):** - Use the shipped docker example container which includes MongoDB pre-installed and pre-configured: - ```bash - # Navigate to the docker example directory - cd docker/example - - # Build and run the container - docker-compose up -d - ``` +This example requires a running MongoDB instance. + +**Local MongoDB Installation:** + +```bash +# Install MongoDB (varies by OS) +# For Ubuntu/Debian - install official MongoDB package: +wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add - +echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list +sudo apt-get update +sudo apt-get install -y mongodb-org + +# Start MongoDB service +sudo systemctl start mongod +sudo systemctl enable mongod +``` + +**Note:** Integration of example 2 into the docker example container with MongoDB support is planned for a future release. ### Python Dependencies From c638094c2efaccaca8cf7c24084894075afa5ecd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 09:18:50 +0000 Subject: [PATCH 62/91] Update MongoDB instructions to use modern commands Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- example/02-pki-management/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/example/02-pki-management/README.md b/example/02-pki-management/README.md index ceae289..61a3318 100644 --- a/example/02-pki-management/README.md +++ b/example/02-pki-management/README.md @@ -27,8 +27,10 @@ This example requires a running MongoDB instance. ```bash # Install MongoDB (varies by OS) # For Ubuntu/Debian - install official MongoDB package: -wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add - -echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list +curl -fsSL https://www.mongodb.org/static/pgp/server-6.0.asc | \ + sudo gpg --dearmor -o /usr/share/keyrings/mongodb-archive-keyring.gpg +echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-archive-keyring.gpg ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | \ + sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list sudo apt-get update sudo apt-get install -y mongodb-org @@ -153,8 +155,8 @@ See: [service_implementation.py](service_implementation.py) After executing all three scripts, you can verify the data in MongoDB: ```bash -# Connect to MongoDB -mongo +# Connect to MongoDB (using mongosh for MongoDB 5.0+) +mongosh # Switch to microesb database use microesb From 02f487c4d143eea17254c8fd0a91b870961bfb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 12 Jan 2026 12:16:06 +0100 Subject: [PATCH 63/91] Corrected --- doc/source/api.rst | 28 ++++++------------ doc/source/examples.rst | 63 +++++++++++------------------------------ doc/source/intro.rst | 18 ++++-------- doc/source/routing.rst | 45 +++++++++-------------------- 4 files changed, 42 insertions(+), 112 deletions(-) diff --git a/doc/source/api.rst b/doc/source/api.rst index 0730c45..d1a2aef 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -206,31 +206,22 @@ All *implementation classes* must be imported into the global Python namespace. from microesb import microesb -Importing the main `microesb` module will call `import esbconfig` (see the following code, line 14). +Importing the main `microesb` module will call `import esbconfig` (see the following code, line 5). .. code-block:: python :linenos: - # ]*[ --------------------------------------------------------------------- ]*[ - # . Micro ESB Python Module . - # ]*[ --------------------------------------------------------------------- ]*[ - # . . - # . Copyright Claus Prüfer (2016 - 2024) . - # . . - # . . - # ]*[ --------------------------------------------------------------------- ]*[ - import abc import sys import logging import importlib import esbconfig -The **microesb** standard installation will include an empty `esbconfig.py` in the global Python dist-packages or in the active Python environment. +The **microesb** default installation will include an empty `esbconfig.py` in the global Python dist-packages for working pytest tests. .. warning:: - If you do not provide an `esbconfig.py` in your project folder, the default installation file will be used. + If you do not provide an `esbconfig.py` in your project folder, the default installation file will be imported. The following **esbconfig.py** tells the `microesb` importer to use the file **service_classes.py** and import *implementation classes* `Class1`, `Class2`, and `Class3` into the global namespace. These classes will then be usable by `ClassMapper` and `ServiceMapper` during internal processing. @@ -296,10 +287,10 @@ Example: 7. Service Router ================= -The `ServiceRouter` class provides user-defined service call routing capabilities. It enables -direct routing of service calls to user-defined functions in a ``user_routing.py`` module. +The `ServiceRouter` class provides user-defined service call routing capabilities. It enables direct routing of service calls to user-defined functions in a ``user_routing.py`` module. This is particularly useful for: + - Database CRUD operations (MongoDB, PostgreSQL, etc.) - External API integrations - Custom business logic execution @@ -342,9 +333,7 @@ See the :ref:`routing` section for detailed routing documentation. 8. Service Executer =================== -The `ServiceExecuter` class handles the execution of service methods and manages recursive class -hierarchy object deserialization. This enables complex hierarchical data structures to be properly -reconstructed from JSON representations. +The `ServiceExecuter` class handles the execution of service methods and manages recursive class hierarchy object deserialization. This enables complex hierarchical data structures to be properly reconstructed from JSON representations. 8.1. Recursive Deserialization ******************************* @@ -354,8 +343,7 @@ The framework automatically deserializes nested class hierarchies, ensuring that - Parent-child relationships are maintained - JSON data is correctly mapped to class properties -This is particularly useful when working with complex domain models that include multiple levels -of nested objects. +This is particularly useful when working with complex domain models that include multiple levels of nested objects. Example: @@ -370,5 +358,5 @@ Example: ) result = service_executer.execute_get_hierarchy() - root_object = r[0]['RootObjectID']['object_instance'] + root_object = r[0]['RootObjectName']['object_instance'] print(root_object.json_dict) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 966c2fc..205bc0f 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -15,9 +15,7 @@ In this example, assume our "virtual" company runs a **Hosting Business**. The company's customer data, including a) Internet Domains and b) DNS Hostnames, should be manageable by different subsystems. -This example demonstrates the integration with **PostgreSQL database transactions**, showcasing -how the microesb framework handles atomic operations across multiple related entities while -maintaining data integrity through commit/rollback mechanisms. +This example demonstrates the integration with **PostgreSQL database transactions**, showcasing how the microesb framework handles atomic operations across multiple related entities while maintaining data integrity through commit/rollback mechanisms. .. note:: @@ -198,22 +196,16 @@ After execution, the newly created domain will be in the `sys_core."domain"` tab 2. PKI Provisioning / Class Types ================================= -This example demonstrates PKI (Public Key Infrastructure) certificate provisioning with a focus -on class type hierarchies and **NoSQL MongoDB backend integration**. +This example demonstrates PKI (Public Key Infrastructure) certificate provisioning with a focus on class type hierarchies and **NoSQL MongoDB backend integration**. -Unlike Example 1's relational database approach, this example showcases how the microesb framework -seamlessly integrates with document-based NoSQL databases. MongoDB is used for storing and retrieving -certificate metadata, demonstrating the framework's flexibility in handling both traditional and -modern database paradigms. +Unlike Example 1's relational database approach, this example showcases how the microesb framework seamlessly integrates with document-based NoSQL databases. MongoDB is used for storing and retrieving certificate metadata, demonstrating the framework's flexibility in handling both traditional and modern database paradigms. The example implements a complete certificate generation workflow for: - **Certificate Authority (CA)** certificates - **Server** certificates - **Client** certificates -Each certificate type can optionally use Hardware Security Module (HSM) / Smartcard integration -for secure key pair generation. The implementation uses user-defined routing functions to interact -with MongoDB for certificate storage and retrieval operations. +Each certificate type can optionally use Hardware Security Module (HSM) / Smartcard integration for secure key pair generation. The implementation uses user-defined routing functions to interact with MongoDB for certificate storage and retrieval operations. 2.1. CA Certificate Relations ****************************** @@ -503,46 +495,23 @@ The following is an example of the *Service Call Metadata* for all certificate t .. _example-number3: -3. Alias Class Mapping -====================== +3. Hosting Use Case (Using DBPool) +================================== -Alias Class Mapping **must** be used when setting up multiple *Child Class Instances*. +Example number 3 is an exact copy of example number 1 with the difference that a database pooling mechanism `python-dbpool` is used. -3.1. Requirements -***************** - -The *Alias Definition* must exist in the *Class Mapping Config* and map to an existing *Implementation Class*. +Project information: -Children of classes defined in the *Class Reference Config* must map to the *Alias Class(es)*. +- https://github.com/clauspruefer/python-dbpool -The *Alias Class* `property_ref` property in the *Class Reference Config* must always reference an existing *Implementation Class*. - -3.2. Example -************ +.. _example-number4: -Here is an example configuration for Alias Class Mapping: +4. NLAP Integration +=================== -.. code-block:: python +Example number 4 will be added on NLAP (Next Level Application Protocol) completion. - config = { - 'class_mapping': { - 'Test': 'Test', - 'Test2Ref1': 'Test2', - 'Test2Ref2': 'Test2' - }, - 'class_reference': { - 'Test': { - 'property_ref': 'Test', - 'children': { - 'Test2Ref1': { - 'property_ref': 'Test2' - }, - 'Test2Ref2': { - 'property_ref': 'Test2' - } - } - } - } - } +Ongoing project status can be viewed here: -.. _example-number4: +- https://www.der-it-pruefer.de/network/Exemplary-HTTP-Processing-Protocol-Design +- https://github.com/WEBcodeX1/http-1.2 diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 5152b4a..e7b1827 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -4,13 +4,9 @@ Intro / Module Description ========================== -The **microesb** Python module provides the foundational features to build a centralized, -structured Enterprise Service Bus (ESB) / Service-Oriented Architecture (SOA). +The **microesb** Python module provides the foundational features to build a centralized, structured Enterprise Service Bus (ESB) / Service-Oriented Architecture (SOA). -It enables clean **Service Model to Python Class Mapping** with support for both traditional -relational databases and modern NoSQL backends. The module features user-based service call -routing, recursive class hierarchy object deserialization, and graph-based JSON result abstraction, -making it suitable for scalable Next-Level Platform-as-a-Service (PaaS) and Microservices architectures. +It enables clean **Service Model to Python Class Mapping** with support for both traditional relational databases and modern NoSQL backends. The module features user-based service call routing, recursive class hierarchy object deserialization, and graph-based JSON result abstraction, making it suitable for scalable Next-Level Platform-as-a-Service (PaaS) and Microservices architectures. A Docker example container can be downloaded here: `microesb-examples-latest.tar `_. @@ -38,8 +34,7 @@ Refer to ``/docker/README.md``. 3. Dependencies =============== -Using the **microesb** module generally does not require the **psycopg2** PostgreSQL -Python module. +Using the **microesb** module generally does not require the **psycopg2** PostgreSQL Python module. .. warning:: Running example code requires **psycopg2** for Example 1 and **pymongo** for Example 2. @@ -50,15 +45,12 @@ Python module. apt-get install python3-psycopg2 .. note:: - Example 2 uses MongoDB as a NoSQL backend. MongoDB cannot be installed via apt on all - systems and is preinstalled in the Docker example container. For local development, - install MongoDB separately or use the provided Docker container. + Example 2 uses MongoDB as a NoSQL backend. MongoDB cannot be installed via apt on all systems and is preinstalled in the Docker example container. For local development, install MongoDB separately or use the provided Docker container. 4. Build Dependencies ===================== -On current Debian 12 / Ubuntu 22.04.3 or 24.04.1, install the following additional packages -(for documentation rendering and testing purposes). +On current Debian 12 / Ubuntu 22.04.3 or 24.04.1, install the following additional packages (for documentation rendering and testing purposes). .. code-block:: bash diff --git a/doc/source/routing.rst b/doc/source/routing.rst index da98cfc..de387a8 100644 --- a/doc/source/routing.rst +++ b/doc/source/routing.rst @@ -4,66 +4,47 @@ Routing ======= -The **microesb** module provides flexible routing capabilities to direct service calls to -appropriate backend implementations. This enables separation of concerns between service -orchestration and business logic implementation. +The **microesb** module provides flexible routing capabilities to direct service calls to appropriate backend implementations. This enables data aggregation (routing) to multiple data-sources like traditional relational databases or modern NoSQL platforms. 1. Simple Routing ================= -Simple Routing allows direct, unencapsulated routing of service calls to user-defined functions. -This approach is suitable for straightforward use cases where service calls map directly to -backend operations without complex orchestration requirements. +Simple Routing allows **direct**, **unencapsulated** routing of service calls to user-defined functions. This approach is suitable for straightforward use cases where service calls map directly to backend operations without complex orchestration requirements. + +**Encapsulated Routing** is a methodology to abstract / handle each single service entity as an external callable service, encapsulated inside a network-callable container (e.g. application server) with scaling and AAA functionality like using "Kubernetes / NginX / https" or "FalconAS / NLAMP". 1.1. Overview ************* -Simple routing uses the ``ServiceRouter`` class to dynamically invoke functions defined in a -``user_routing.py`` module. Each routing function receives metadata from the service call and -returns the result of the backend operation. +Simple routing uses the ``ServiceRouter`` class to dynamically invoke functions defined in a ``user_routing.py`` module. Each routing function receives metadata from the service call and returns the result of the backend operation. 1.2. Implementation ******************* -To implement simple routing, create a ``user_routing.py`` module in your project with functions -that match the routing identifiers used in your service calls. +To implement simple routing, create a ``user_routing.py`` module in your project with functions that match the routing identifiers used in your service calls (see next chapter). Example from ``/example/02-pki-management/user_routing.py``: .. literalinclude:: ../../example/02-pki-management/user_routing.py :linenos: -1.3. Usage Pattern -****************** - -The routing functions are invoked automatically by the framework when a service call specifies -a routing target. Each function receives the relevant metadata and can interact with databases, -external services, or other backend systems. +1.3. Implementation +******************* -**Key characteristics:** +Simply insert -- Direct function mapping: Service routing IDs map to function names in ``user_routing.py`` -- Flexible return values: Functions can return data structures that match service requirements -- Database integration: Common pattern for MongoDB, PostgreSQL, or other data stores -- Stateless operations: Each routing function operates independently +self._ServiceRouter.send( +'CertGetById', metadata=self.id)` inside the service_implementation.py class definitions. 1.4. Common Use Cases ********************* -Simple routing is ideal for: - -- Database CRUD operations (Create, Read, Update, Delete) -- External API integrations -- File system operations -- Cache management -- Message queue interactions +Simple routing should be used for net-internal trusted operations / systems where no authentication security is required. 1.5. Error Handling ******************* -The ``ServiceRouter`` class provides basic error handling for missing routing targets. For -production deployments, implement appropriate error handling within your routing functions -to manage database connection failures, validation errors, and other exceptional conditions. +The ``ServiceRouter`` class provides no error handling, the user is responsible implementing error / exception handling by himself. 2. Encapsulated Routing ======================= From 5151ad8be3bce227cdd17e7aa0cece757b70d850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 12 Jan 2026 12:19:20 +0100 Subject: [PATCH 64/91] Remove direct mongodb installation, correct container remark --- example/02-pki-management/README.md | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/example/02-pki-management/README.md b/example/02-pki-management/README.md index 61a3318..61de2a7 100644 --- a/example/02-pki-management/README.md +++ b/example/02-pki-management/README.md @@ -22,24 +22,13 @@ The example demonstrates: This example requires a running MongoDB instance. -**Local MongoDB Installation:** +**Local MongoDB Community Edition Installation:** -```bash -# Install MongoDB (varies by OS) -# For Ubuntu/Debian - install official MongoDB package: -curl -fsSL https://www.mongodb.org/static/pgp/server-6.0.asc | \ - sudo gpg --dearmor -o /usr/share/keyrings/mongodb-archive-keyring.gpg -echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-archive-keyring.gpg ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | \ - sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list -sudo apt-get update -sudo apt-get install -y mongodb-org - -# Start MongoDB service -sudo systemctl start mongod -sudo systemctl enable mongod -``` +https://www.mongodb.com/docs/manual/administration/install-community/#std-label-install-mdb-community-edition + +**Docker Example Container:** -**Note:** Integration of example 2 into the docker example container with MongoDB support is planned for a future release. +The docker example container contains both an installed MongoDB instance and the example code. ### Python Dependencies From bcd33ed77548bb684e3a1319a46ae41d6609d627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 08:30:59 +0100 Subject: [PATCH 65/91] Finalize (non-reviewed) --- doc/source/routing.rst | 76 ++++++++++++++++++-------------------- docker/examples.dockerfile | 17 +++++++-- 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/doc/source/routing.rst b/doc/source/routing.rst index de387a8..7154c2f 100644 --- a/doc/source/routing.rst +++ b/doc/source/routing.rst @@ -4,7 +4,7 @@ Routing ======= -The **microesb** module provides flexible routing capabilities to direct service calls to appropriate backend implementations. This enables data aggregation (routing) to multiple data-sources like traditional relational databases or modern NoSQL platforms. +The **microesb** module provides flexible routing capabilities to direct service calls to appropriate backend implementations. This enables data aggregation (esb terminology calls this *routing*) to multiple data-sources like traditional relational databases, modern NoSQL platforms or similar. 1. Simple Routing ================= @@ -13,10 +13,12 @@ Simple Routing allows **direct**, **unencapsulated** routing of service calls to **Encapsulated Routing** is a methodology to abstract / handle each single service entity as an external callable service, encapsulated inside a network-callable container (e.g. application server) with scaling and AAA functionality like using "Kubernetes / NginX / https" or "FalconAS / NLAMP". +The **Encapsulated Routing** concept is described under 2.x more detailed. + 1.1. Overview ************* -Simple routing uses the ``ServiceRouter`` class to dynamically invoke functions defined in a ``user_routing.py`` module. Each routing function receives metadata from the service call and returns the result of the backend operation. +Simple routing uses the ``ServiceRouter`` class to dynamically invoke functions defined in a ``user_routing.py`` module. Each routing function receives metadata from the service call and is able to return user modified results (as any type) of the backend operation. 1.2. Implementation ******************* @@ -28,61 +30,55 @@ Example from ``/example/02-pki-management/user_routing.py``: .. literalinclude:: ../../example/02-pki-management/user_routing.py :linenos: -1.3. Implementation -******************* +1.3. Service Calls +****************** + +A service call must be placed inside the *service implementation*. The micro-esb's ServiceRouter class places a reference there (self._ServiceRouter), so it is easily callable like this: -Simply insert +``self._ServiceRouter.send('MethodId', metadata=metadata)`. -self._ServiceRouter.send( -'CertGetById', metadata=self.id)` inside the service_implementation.py class definitions. +A good approach is to pass metadata as a dictionary type into the routing function (JSON serializable) and also expect a dictionary type as result (JSON serializable) to be conform to modern software development practices. 1.4. Common Use Cases ********************* -Simple routing should be used for net-internal trusted operations / systems where no authentication security is required. +Simple routing is suitable for example: -1.5. Error Handling -******************* +- to aggregate data from internal systems (centralized DB with NoSQL data sources) +- to route / propagate service calls (e.g. certificate generation) to network-attached sub-systems -The ``ServiceRouter`` class provides no error handling, the user is responsible implementing error / exception handling by himself. +> [!WARNING] +> Authenticaton, Accouting and Load-Balancing has to be implemented by the user itself. -2. Encapsulated Routing -======================= +1.5. Error Handling / Logging +***************************** -Encapsulated Routing provides a more sophisticated approach with service registry integration, -authentication, authorization, and load balancing capabilities. +The ``ServiceRouter`` class provides no error handling nor logging, the user is responsible implementing error / exception handling / logging by himself. -.. note:: +2. Encapsulated Routing +======================= - Encapsulated Routing will be integrated in a future release and will include: - - - Service Registry integration for dynamic service discovery - - Built-in Authentication and Authorization (AAA) mechanisms - - Load balancing across multiple service instances - - Service versioning and compatibility management - - Centralized logging and monitoring - - Circuit breaker patterns for fault tolerance +**Encapsulated Routing** is a mechanism to host ESB API services securely inside an network-accesible entity which provides the following: -2.1. Planned Features -********************* +- Load Balancing / Scaling +- AAA (Authentication, Authorization, Accounting) +- Service (API) Registration / Versioning +- Service (API) Discovery +- Service (API) Documentation +- Service Security / PKI Abstraction -The encapsulated routing implementation will provide: +Detailed documentation (including examples) starting from release version 1.3 upwards. -**Service Registry:** - Centralized registry for service endpoint discovery and metadata management. +3. Operating Modes +================== -**AAA Integration:** - Authentication, Authorization, and Accounting for secure service access control. +There are **no** strict configurable modes, the micro-esb's concept is to be extraordinary flexible in class abstraction / modeling from the users point of view; the *implementation mode* results from the users program code. -**Load Balancing:** - Distribute service calls across multiple backend instances for scalability. +Nevertheless between two different **logical** modes can be distinguished: -**Service Mesh:** - Advanced service-to-service communication with retry logic, timeouts, and fallbacks. +- Native Routing Mode +- Non Native Routing Mode -2.2. Migration Path -******************* +**Native Routing** is the concept of decapsulate any service *calculations* (CPU) to external entities / application server; the ESB's service_implementation exclusively routes data to external services and **does not** process data internally which strongly enhances security. -Applications using Simple Routing will be able to migrate to Encapsulated Routing with minimal -code changes. The routing interface will remain backwards compatible while providing optional -advanced features through configuration. +**Non Native Routing** does not encapsulate data calls so data fetching is directly executed inside the ESB's service_implementation (e.g. direct MongoDB driver). This concept should not be adapted on high security requirements / non-reverse-proxied access. diff --git a/docker/examples.dockerfile b/docker/examples.dockerfile index 673cad9..cfec880 100644 --- a/docker/examples.dockerfile +++ b/docker/examples.dockerfile @@ -1,8 +1,8 @@ -FROM postgres:latest +FROM postgres:18-bookworm MAINTAINER Claus Prüfer ADD ./example / -COPY ./dist/microesb-1.0rc1.tar.gz / +COPY ./dist/microesb-1.1.tar.gz / COPY ./example/01-hosting-use-case/01-create-schema-sequence.sql /docker-entrypoint-initdb.d/ COPY ./example/01-hosting-use-case/02-create-table.sql /docker-entrypoint-initdb.d/ @@ -14,12 +14,21 @@ RUN apt-get -qq update -y RUN apt-get -qq install python3-pip python3-sphinx python3-sphinx-rtd-theme -y RUN apt-get -qq install python3-pytest python3-pytest-pep8 -y RUN apt-get -qq install python3-psycopg2 -y +RUN apt-get -qq install curl -y -RUN pip3 install /microesb-1.0rc1.tar.gz --break-system-packages +RUN curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor + +RUN echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/debian bookworm/mongodb-org/8.0 main" | tee /etc/apt/sources.list.d/mongodb-org-8.0.list + +RUN apt-get -qq update +RUN apt-get -qq install mongodb-org -y + +RUN mkdir -p /data/db + +RUN pip3 install /microesb-1.1.tar.gz --break-system-packages ENV POSTGRES_USER postgres ENV POSTGRES_PASSWORD password ENV POSTGRES_DB hosting-example EXPOSE 5432 - From 086d68bde74a2cfe7f1e4c8d1f287fbb11dc9b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 08:31:31 +0100 Subject: [PATCH 66/91] Enhance format --- test/integration/test_json_transform.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/integration/test_json_transform.py b/test/integration/test_json_transform.py index 094d205..6f4a809 100644 --- a/test/integration/test_json_transform.py +++ b/test/integration/test_json_transform.py @@ -273,14 +273,8 @@ def test_list_object( 'id': 'testshipment1', 'Palette': { 'Palette': [ - { - 'id': 1, - 'label': 'label1' - }, - { - 'id': 2, - 'label': 'label2' - } + { 'id': 1, 'label': 'label1' }, + { 'id': 2, 'label': 'label2' } ] } } From 6bcf3c32d82c0f35376ede4b3d7387d4a7fb526d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 08:32:00 +0100 Subject: [PATCH 67/91] Correct format --- doc/source/api.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/source/api.rst b/doc/source/api.rst index d1a2aef..0369ec9 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -59,8 +59,7 @@ Example usage: 1.2. Property Registration *************************** -Properties can be registered with metadata using the ``register_property()`` method. This is -useful for defining internal system properties that should be handled specially by the framework. +Properties can be registered with metadata using the ``register_property()`` method. This is useful for defining internal system properties that should be handled specially by the framework. Example from ``/example/02-pki-management/service_implementation.py``: From a71095d566a98e3309037335c5222e670f8a3511 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 07:48:30 +0000 Subject: [PATCH 68/91] Initial plan From 0f45ef7601bc1f37366e12b962647d4584ca2ab5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 07:53:09 +0000 Subject: [PATCH 69/91] Update documentation: improve routing.rst and add MongoDB examples to examples.rst Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- doc/source/examples.rst | 237 ++++++++++++++++++++++++++++++++++++++++ doc/source/routing.rst | 54 ++++----- 2 files changed, 264 insertions(+), 27 deletions(-) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 205bc0f..2881b87 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -201,12 +201,249 @@ This example demonstrates PKI (Public Key Infrastructure) certificate provisioni Unlike Example 1's relational database approach, this example showcases how the microesb framework seamlessly integrates with document-based NoSQL databases. MongoDB is used for storing and retrieving certificate metadata, demonstrating the framework's flexibility in handling both traditional and modern database paradigms. The example implements a complete certificate generation workflow for: + - **Certificate Authority (CA)** certificates - **Server** certificates - **Client** certificates Each certificate type can optionally use Hardware Security Module (HSM) / Smartcard integration for secure key pair generation. The implementation uses user-defined routing functions to interact with MongoDB for certificate storage and retrieval operations. +2.0.1. MongoDB Collections +*************************** + +The example uses two MongoDB collections within the ``microesb`` database: + +a) **cert** - Stores certificate properties as *flat* key/value pairs for basic certificate metadata +b) **cert_hierarchy** - Contains complete hierarchical JSON data representing the full certificate object graph including relationships to CA, Server, and Smartcard entities + +The ``cert`` collection is used for simple queries by certificate ID, while ``cert_hierarchy`` stores the complete object hierarchy for complex certificate relationships and dependency tracking. + +2.0.2. MongoDB Collection Examples +*********************************** + +The following examples illustrate the structure of documents stored in the ``cert`` collection. + +**CA Certificate (Flat Storage):** + +.. code-block:: json + + { + "_id": { + "$oid": "695e5b5c65ceda39b9c5388e" + }, + "id": "test-ca1", + "country": "DE", + "state": "Berlin", + "locality": "Berlin", + "org": "WEBcodeX", + "org_unit": "Security", + "common_name": "testca@domain.com", + "email": "pki@webcodex.de", + "valid_days": 365, + "generation_timestamp": "2026-01-07T14:10:52.927127", + "cert_data": "dummy_cacert_data" + } + +**CA Certificate (Hierarchical Storage with Smartcard):** + +.. code-block:: json + + { + "_id": { + "$oid": "695e5b5c65ceda39b9c5388f" + }, + "id": "test-ca1", + "country": "DE", + "state": "Berlin", + "locality": "Berlin", + "org": "WEBcodeX", + "org_unit": "Security", + "common_name": "testca@domain.com", + "email": "pki@webcodex.de", + "valid_days": 365, + "generation_timestamp": "2026-01-07T14:10:52.927127", + "cert_data": "dummy_cacert_data", + "SmartcardContainer": null, + "Smartcard": { + "label": "smartcard_ca_card", + "user_pin": "pin1", + "gen_status": true, + "SmartcardContainer": { + "label": "keypair_ca1" + } + } + } + +**Server Certificate (Flat Storage):** + +.. code-block:: json + + { + "_id": { + "$oid": "695e5b69bfa5128f5c1890f2" + }, + "id": "test-server1", + "country": "DE", + "state": "Berlin", + "locality": "Berlin", + "org": "WEBcodeX", + "org_unit": "Security", + "common_name": "testserver@domain.com", + "email": "pki@webcodex.de", + "valid_days": 365, + "generation_timestamp": "2026-01-07T14:11:05.030076", + "cert_data": "dummy_servercert_data" + } + +**Server Certificate (Hierarchical Storage with CA Reference):** + +.. code-block:: json + + { + "_id": { + "$oid": "695e5b69bfa5128f5c1890f3" + }, + "id": "test-server1", + "country": "DE", + "state": "Berlin", + "locality": "Berlin", + "org": "WEBcodeX", + "org_unit": "Security", + "common_name": "testserver@domain.com", + "email": "pki@webcodex.de", + "valid_days": 365, + "generation_timestamp": "2026-01-07T14:11:05.030076", + "cert_data": "dummy_servercert_data", + "SmartcardContainer": null, + "Smartcard": { + "label": "smartcard_customer1", + "user_pin": "pin2", + "gen_status": true, + "SmartcardContainer": { + "label": "testserver1_keypair" + } + }, + "CertCA": { + "id": "test-ca1", + "country": "DE", + "state": "Berlin", + "locality": "Berlin", + "org": "WEBcodeX", + "org_unit": "Security", + "common_name": "testca@domain.com", + "email": "pki@webcodex.de", + "valid_days": 365, + "generation_timestamp": "2026-01-07T14:10:52.927127", + "cert_data": "dummy_cacert_data", + "SmartcardContainer": null, + "Smartcard": { + "label": null, + "user_pin": null, + "gen_status": false, + "SmartcardContainer": { + "label": null + } + } + } + } + +**Client Certificate (Flat Storage):** + +.. code-block:: json + + { + "_id": { + "$oid": "695e5b81fa0258fe59b9461d" + }, + "id": "test-client1", + "country": "DE", + "state": "Berlin", + "locality": "Berlin", + "org": "WEBcodeX", + "org_unit": "Security", + "common_name": "testclient1@domain.com", + "email": "pki@webcodex.de", + "valid_days": 365, + "generation_timestamp": "2026-01-07T14:11:29.750133", + "cert_data": "dummy_clientcert_data" + } + +**Client Certificate (Hierarchical Storage with Full References):** + +.. code-block:: json + + { + "_id": { + "$oid": "695e5b81fa0258fe59b9461e" + }, + "id": "test-client1", + "country": "DE", + "state": "Berlin", + "locality": "Berlin", + "org": "WEBcodeX", + "org_unit": "Security", + "common_name": "testclient1@domain.com", + "email": "pki@webcodex.de", + "valid_days": 365, + "generation_timestamp": "2026-01-07T14:11:29.750133", + "cert_data": "dummy_clientcert_data", + "SmartcardContainer": null, + "Smartcard": { + "label": "smartcard_customer1", + "user_pin": "pin2", + "gen_status": true, + "SmartcardContainer": { + "label": "testserver1_client1_keypair" + } + }, + "CertCA": { + "id": "test-ca1", + "country": "DE", + "state": "Berlin", + "locality": "Berlin", + "org": "WEBcodeX", + "org_unit": "Security", + "common_name": "testca@domain.com", + "email": "pki@webcodex.de", + "valid_days": 365, + "generation_timestamp": "2026-01-07T14:10:52.927127", + "cert_data": "dummy_cacert_data", + "SmartcardContainer": null, + "Smartcard": { + "label": null, + "user_pin": null, + "gen_status": false, + "SmartcardContainer": { + "label": null + } + } + }, + "CertServer": { + "id": "test-server1", + "country": "DE", + "state": "Berlin", + "locality": "Berlin", + "org": "WEBcodeX", + "org_unit": "Security", + "common_name": "testserver@domain.com", + "email": "pki@webcodex.de", + "valid_days": 365, + "generation_timestamp": "2026-01-07T14:11:05.030076", + "cert_data": "dummy_servercert_data", + "SmartcardContainer": null, + "Smartcard": { + "label": null, + "user_pin": null, + "gen_status": false, + "SmartcardContainer": { + "label": null + } + } + } + } + +These examples demonstrate how the microesb framework stores both flat certificate data and hierarchical relationships in MongoDB, enabling efficient querying and complete object graph reconstruction. + 2.1. CA Certificate Relations ****************************** diff --git a/doc/source/routing.rst b/doc/source/routing.rst index 7154c2f..bedaf45 100644 --- a/doc/source/routing.rst +++ b/doc/source/routing.rst @@ -4,21 +4,21 @@ Routing ======= -The **microesb** module provides flexible routing capabilities to direct service calls to appropriate backend implementations. This enables data aggregation (esb terminology calls this *routing*) to multiple data-sources like traditional relational databases, modern NoSQL platforms or similar. +The **microesb** module provides flexible routing capabilities to direct service calls to appropriate backend implementations. This enables data aggregation (in ESB terminology, this is referred to as *routing*) from multiple data sources, including traditional relational databases, modern NoSQL platforms, and similar systems. 1. Simple Routing ================= Simple Routing allows **direct**, **unencapsulated** routing of service calls to user-defined functions. This approach is suitable for straightforward use cases where service calls map directly to backend operations without complex orchestration requirements. -**Encapsulated Routing** is a methodology to abstract / handle each single service entity as an external callable service, encapsulated inside a network-callable container (e.g. application server) with scaling and AAA functionality like using "Kubernetes / NginX / https" or "FalconAS / NLAMP". +**Encapsulated Routing** is a methodology designed to abstract and handle each service entity as an externally callable service, encapsulated within a network-accessible container (e.g., application server) with scaling and AAA (Authentication, Authorization, Accounting) functionality, such as "Kubernetes / Nginx / HTTPS" or "FalconAS / NLAMP". -The **Encapsulated Routing** concept is described under 2.x more detailed. +The **Encapsulated Routing** concept is described in greater detail in section 2. 1.1. Overview ************* -Simple routing uses the ``ServiceRouter`` class to dynamically invoke functions defined in a ``user_routing.py`` module. Each routing function receives metadata from the service call and is able to return user modified results (as any type) of the backend operation. +Simple routing uses the ``ServiceRouter`` class to dynamically invoke functions defined in a ``user_routing.py`` module. Each routing function receives metadata from the service call and can return user-modified results (of any type) representing the outcome of the backend operation. 1.2. Implementation ******************* @@ -33,52 +33,52 @@ Example from ``/example/02-pki-management/user_routing.py``: 1.3. Service Calls ****************** -A service call must be placed inside the *service implementation*. The micro-esb's ServiceRouter class places a reference there (self._ServiceRouter), so it is easily callable like this: +A service call must be placed within the *service implementation*. The micro-esb's ServiceRouter class provides a reference there (self._ServiceRouter), making it easily callable as follows: -``self._ServiceRouter.send('MethodId', metadata=metadata)`. +``self._ServiceRouter.send('MethodId', metadata=metadata)``. -A good approach is to pass metadata as a dictionary type into the routing function (JSON serializable) and also expect a dictionary type as result (JSON serializable) to be conform to modern software development practices. +A recommended approach is to pass metadata as a dictionary type (JSON serializable) into the routing function and expect a dictionary type as a result (JSON serializable), conforming to modern software development best practices. 1.4. Common Use Cases ********************* -Simple routing is suitable for example: +Simple routing is particularly suitable for: -- to aggregate data from internal systems (centralized DB with NoSQL data sources) -- to route / propagate service calls (e.g. certificate generation) to network-attached sub-systems +- Aggregating data from internal systems (e.g., centralized databases with NoSQL data sources) +- Routing and propagating service calls (e.g., certificate generation) to network-attached subsystems -> [!WARNING] -> Authenticaton, Accouting and Load-Balancing has to be implemented by the user itself. +.. warning:: + Authentication, accounting, and load balancing must be implemented by the user. -1.5. Error Handling / Logging -***************************** +1.5. Error Handling and Logging +******************************* -The ``ServiceRouter`` class provides no error handling nor logging, the user is responsible implementing error / exception handling / logging by himself. +The ``ServiceRouter`` class does not provide built-in error handling or logging. Users are responsible for implementing their own error and exception handling, as well as logging mechanisms. 2. Encapsulated Routing ======================= -**Encapsulated Routing** is a mechanism to host ESB API services securely inside an network-accesible entity which provides the following: +**Encapsulated Routing** is a mechanism for hosting ESB API services securely within a network-accessible entity that provides the following features: -- Load Balancing / Scaling +- Load balancing and scaling - AAA (Authentication, Authorization, Accounting) -- Service (API) Registration / Versioning -- Service (API) Discovery -- Service (API) Documentation -- Service Security / PKI Abstraction +- Service (API) registration and versioning +- Service (API) discovery +- Service (API) documentation +- Service security and PKI abstraction -Detailed documentation (including examples) starting from release version 1.3 upwards. +Detailed documentation (including examples) will be available starting from release version 1.3. 3. Operating Modes ================== -There are **no** strict configurable modes, the micro-esb's concept is to be extraordinary flexible in class abstraction / modeling from the users point of view; the *implementation mode* results from the users program code. +There are **no** strictly configurable modes. The micro-esb framework is designed to be extraordinarily flexible in class abstraction and modeling from the user's perspective; the *implementation mode* results from the user's program code. -Nevertheless between two different **logical** modes can be distinguished: +Nevertheless, two different **logical** modes can be distinguished: - Native Routing Mode -- Non Native Routing Mode +- Non-Native Routing Mode -**Native Routing** is the concept of decapsulate any service *calculations* (CPU) to external entities / application server; the ESB's service_implementation exclusively routes data to external services and **does not** process data internally which strongly enhances security. +**Native Routing** is the concept of delegating all service *computations* (CPU-intensive operations) to external entities or application servers. The ESB's service_implementation exclusively routes data to external services and **does not** process data internally, thereby strongly enhancing security. -**Non Native Routing** does not encapsulate data calls so data fetching is directly executed inside the ESB's service_implementation (e.g. direct MongoDB driver). This concept should not be adapted on high security requirements / non-reverse-proxied access. +**Non-Native Routing** does not encapsulate data calls, so data fetching is executed directly within the ESB's service_implementation (e.g., direct MongoDB driver usage). This approach should not be adopted in environments with high security requirements or non-reverse-proxied access. From 25b5c5d5f31559c836c5b687e58d4041ef32da2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 09:00:12 +0100 Subject: [PATCH 70/91] Change to global style --- doc/source/routing.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/routing.rst b/doc/source/routing.rst index bedaf45..9c6d955 100644 --- a/doc/source/routing.rst +++ b/doc/source/routing.rst @@ -60,12 +60,12 @@ The ``ServiceRouter`` class does not provide built-in error handling or logging. **Encapsulated Routing** is a mechanism for hosting ESB API services securely within a network-accessible entity that provides the following features: -- Load balancing and scaling +- Load Balancing / Scaling - AAA (Authentication, Authorization, Accounting) -- Service (API) registration and versioning -- Service (API) discovery -- Service (API) documentation -- Service security and PKI abstraction +- Service (API) Registration / Versioning +- Service (API) Discovery +- Service (API) Documentation +- Service Security / PKI Abstraction Detailed documentation (including examples) will be available starting from release version 1.3. From 5dabfef50801ac4a9f3e197d7bfe422d1a6cdf49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:09:42 +0000 Subject: [PATCH 71/91] Initial plan From e9c0291539e2aa305712a1cc05a986e7aab9ebd7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:12:15 +0000 Subject: [PATCH 72/91] Update documentation: fix method names in examples.rst and enhance example 01 README Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- doc/source/examples.rst | 8 ++++---- example/01-hosting-use-case/README.md | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 2881b87..9ce595a 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -547,7 +547,7 @@ The "Cert" base class provides three private abstract methods because the proces - `_load_ref_cert_data()` - `_gen_openssl_cert()` -- `_insert_cert_db_data()` +- `_store_cert_data()` .. code-block:: python @@ -561,15 +561,15 @@ The "Cert" base class provides three private abstract methods because the proces class CertServer(Cert): def _load_ref_cert_data(self): - self.CertCA._get_cert_dbdata_by_id() + self.CertCA._get_cert_data_by_id() .. code-block:: python class CertClient(Cert): def _load_ref_cert_data(self): - self.CertCA._get_cert_dbdata_by_id() - self.CertServer._get_cert_dbdata_by_id() + self.CertCA._get_cert_data_by_id() + self.CertServer._get_cert_data_by_id() 2.6.2. Generic Template Methods ------------------------------- diff --git a/example/01-hosting-use-case/README.md b/example/01-hosting-use-case/README.md index fd6ee6c..bbc62f1 100644 --- a/example/01-hosting-use-case/README.md +++ b/example/01-hosting-use-case/README.md @@ -1,8 +1,18 @@ -# Example 1: Hosting Use Case +# Example 1: Hosting Use Case with PostgreSQL Transactions -In this example, assume our "virtual" company runs a **Hosting Business**. +This example demonstrates a **DNS Hosting Management System** using the microesb framework with a **relational PostgreSQL** database backend and transactional processing. -The company's customer data, including a) Internet Domains and b) DNS Hostnames, should be manageable by different subsystems. +## Overview + +Example 1 showcases how microesb can manage hierarchical customer domain and DNS hostname data using the traditional relational database model with ACID transaction guarantees. + +In this example, assume our "virtual" company runs a **Hosting Business**. The company's customer data, including a) Internet Domains and b) DNS Hostnames, should be manageable by different subsystems. + +The example demonstrates: +- **Relational Database Transactions** with PostgreSQL for data integrity and atomic operations +- **Hierarchical Service Metadata Processing** to manage domain and hostname relationships +- **ACID Compliance** ensuring commit/rollback mechanisms for data consistency +- **1:n Relationships** between Customer → Domain → Hostname entities > **Note:** Example number 1 will only cover Local Service Mapping **without** a *Python Application Server* encapsulation. From 61488aa509d43a23d3d02332fe13b524e559328d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:13:31 +0000 Subject: [PATCH 73/91] Fix remaining method name references in examples.rst Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- doc/source/examples.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 9ce595a..3f4dae7 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -577,7 +577,7 @@ The "Cert" base class provides three private abstract methods because the proces The following methods are generic template methods inherited by each Child Class: - `_gen_openssl_privkey()` -- `_get_cert_dbdata_by_id()` +- `_get_cert_data_by_id()` - `_hsm_gen_keypair()` 2.7. Accessing Properties @@ -594,7 +594,7 @@ Label as follows: In `CertClient` and `CertServer`, it is also possible to access the `CertCA's` `Smartcard` Properties (from Referenced Classes in Class Reference Config) to fill data from the database -inside `_get_cert_dbdata_by_id()`: +inside `_get_cert_data_by_id()`: .. code-block:: python From 584392e28514128b065f853ffca5bd5506d40d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 09:15:19 +0100 Subject: [PATCH 74/91] Add collection --- doc/source/examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 3f4dae7..8471d0c 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -221,7 +221,7 @@ The ``cert`` collection is used for simple queries by certificate ID, while ``ce 2.0.2. MongoDB Collection Examples *********************************** -The following examples illustrate the structure of documents stored in the ``cert`` collection. +The following examples illustrate the structure of documents stored in the ``cert`` and ``cert_hierarchy`` collections. **CA Certificate (Flat Storage):** From f8e0e994b1fa0aae9b15c2f9680ac61661e30c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 10:36:32 +0100 Subject: [PATCH 75/91] Modify all logging from "info" to "debug" --- src/microesb.py | 8 ++++---- src/router.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index b97eeb1..fb3d78f 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -725,7 +725,7 @@ def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent Recursive connect all generated json_dicts to its parents. """ - self.logger.info('Parent dict:{}'.format(parent_dict)) + self.logger.debug('Parent dict:{}'.format(parent_dict)) if parent_class is not None: self._class_hierarchy[self._hierarchy_level] = parent_class @@ -764,11 +764,11 @@ def _rename_dict_key(self, rename_dict, parent_dict=None, parent_class=None): self._class_hierarchy_comp[self._hierarchy_level_comp] = parent_class if self._class_hierarchy == self._class_hierarchy_comp: - self.logger.info('Match rename_dict:{}'.format(rename_dict)) + self.logger.debug('Match rename_dict:{}'.format(rename_dict)) # only remove when all 'children' keys have been altered to 'children_processed' if ChildCounter().get_sum_child_count(dict(rename_dict)) == 0: - self.logger.info('Parent dict:{}'.format(parent_dict)) + self.logger.debug('Parent dict:{}'.format(parent_dict)) set_dict = parent_dict[parent_class].pop('children') parent_dict[parent_class]['children_processed'] = set_dict @@ -798,7 +798,7 @@ def get_sum_child_count(self, reference_dict): Count children nodes recursive and return sum. """ - self.logger.info('Sum count ref-dict:{}'.format(reference_dict)) + self.logger.debug('Sum count ref-dict:{}'.format(reference_dict)) for class_name, class_properties in reference_dict.items(): if 'children' in class_properties: diff --git a/src/router.py b/src/router.py index 69e4484..cb94dce 100644 --- a/src/router.py +++ b/src/router.py @@ -32,7 +32,7 @@ def send(self, send_id, metadata): Execute method with given id in `send_id` from imported user_routing.py module and return result dict or None. """ - logger.info('ServiceRouter send_id:{} metadata:{}'.format(send_id, metadata)) + logger.debug('ServiceRouter send_id:{} metadata:{}'.format(send_id, metadata)) func_ref = getattr(mod_ref, send_id) logger.debug('FuncRef:{}'.format(func_ref)) return func_ref(metadata) From 57aa35e6500854413181c60da6f72b475a012400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 10:37:14 +0100 Subject: [PATCH 76/91] Make example2 running correctly --- docker/examples.dockerfile | 2 +- docker/exec-example2.sh | 7 +++---- docker/start-examples-container.sh | 3 +++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docker/examples.dockerfile b/docker/examples.dockerfile index cfec880..3c660af 100644 --- a/docker/examples.dockerfile +++ b/docker/examples.dockerfile @@ -13,7 +13,7 @@ RUN apt-get -qq update -y RUN apt-get -qq install python3-pip python3-sphinx python3-sphinx-rtd-theme -y RUN apt-get -qq install python3-pytest python3-pytest-pep8 -y -RUN apt-get -qq install python3-psycopg2 -y +RUN apt-get -qq install python3-psycopg2 python3-pymongo -y RUN apt-get -qq install curl -y RUN curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor diff --git a/docker/exec-example2.sh b/docker/exec-example2.sh index e28d502..7fbcbac 100755 --- a/docker/exec-example2.sh +++ b/docker/exec-example2.sh @@ -1,14 +1,13 @@ #/bin/sh # exec cert gen ca service -docker exec microesb-postgres python3 /02-pki-management/main-ca.py +docker exec microesb-postgres python3 /02-pki-management/00-main-ca.py echo "" # exec cert gen server service -docker exec microesb-postgres python3 /02-pki-management/main-server.py +docker exec microesb-postgres python3 /02-pki-management/01-main-server.py echo "" # exec cert gen client service -docker exec microesb-postgres python3 /02-pki-management/main-client.py +docker exec microesb-postgres python3 /02-pki-management/02-main-client.py echo "" - diff --git a/docker/start-examples-container.sh b/docker/start-examples-container.sh index 26d583f..09de49f 100755 --- a/docker/start-examples-container.sh +++ b/docker/start-examples-container.sh @@ -1,7 +1,10 @@ #!/bin/sh +# run container docker run --name microesb-postgres \ -d -p 5432:5432 \ --add-host=localdb:127.0.0.1 \ microesb-examples:latest postgres +# start mongodb +docker exec microesb-postgres mongod >/dev/null 2>/dev/null & From b7d7c8e08d5b50f29179a89e6952abb682631ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 10:44:23 +0100 Subject: [PATCH 77/91] Correct example file (inclusion) names --- doc/source/examples.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 8471d0c..cd03610 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -618,7 +618,7 @@ Currently, the *Service Registry* feature is unimplemented. Execution is only po The following implementation demonstrates how to execute a `CertCA` type service: -.. literalinclude:: ../../example/02-pki-management/main-ca.py +.. literalinclude:: ../../example/02-pki-management/00-main-ca.py :linenos: To execute the script, run: @@ -640,7 +640,7 @@ Expected output: The following implementation demonstrates how to execute a `CertServer` type service: -.. literalinclude:: ../../example/02-pki-management/main-server.py +.. literalinclude:: ../../example/02-pki-management/01-main-server.py :linenos: To execute the script, run: @@ -664,7 +664,7 @@ Expected output: The following implementation demonstrates how to execute a `CertClient` type service: -.. literalinclude:: ../../example/02-pki-management/main-client.py +.. literalinclude:: ../../example/02-pki-management/02-main-client.py :linenos: To execute the script, run: From 6fa9adca2569912cdd371270792503478958976a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 10:44:48 +0100 Subject: [PATCH 78/91] Update to more fitting content --- doc/source/intro.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/intro.rst b/doc/source/intro.rst index e7b1827..968287a 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -50,7 +50,7 @@ Using the **microesb** module generally does not require the **psycopg2** Postgr 4. Build Dependencies ===================== -On current Debian 12 / Ubuntu 22.04.3 or 24.04.1, install the following additional packages (for documentation rendering and testing purposes). +On Debian 12 / Ubuntu 22.04 or 24.04, install the following additional packages (for documentation rendering and testing purposes). .. code-block:: bash From 17b17a24ef68a960bf6676b7fd753093579ca95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 10:54:45 +0100 Subject: [PATCH 79/91] Integrate features and planned features --- doc/source/features.rst | 4 ++-- doc/source/intro.rst | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/source/features.rst b/doc/source/features.rst index b42575e..386cd33 100644 --- a/doc/source/features.rst +++ b/doc/source/features.rst @@ -89,7 +89,7 @@ Planned for upcoming releases: - Service Registry / API Server - Service Registry / YANG Model Export -- Service Registry - Web Interface -- Service API Auto Documentation +- Service Registry / Web Interface +- Service API / Auto Documentation - Extended "Encapsulated" Service Routing - Mincepy Integration / Metadata Mapping diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 968287a..c6f2d53 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -87,8 +87,15 @@ To run all tests (unit and integration) after the system-wide package installati =================== - JSON Service Metadata to Python Internal Class / Object Mapping +- JSON Result Hierarchical Deserialization +- Service Routing / Data Aggregation 8. Planned Features ==================== -- Database Abstraction on "top" of the Object Mapping Model +- Service Registry / API Server +- Service Registry / YANG Model Export +- Service Registry / Web Interface +- Service API / Auto Documentation +- Extended “Encapsulated” Service Routing +- Mincepy Integration / Metadata Mapping From cccadae4bf4547eabc02b5845f701d2aec3293d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:01:48 +0000 Subject: [PATCH 80/91] Initial plan From dde59e860ae4e50f23dd986527b5ac3ae199061f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:07:14 +0000 Subject: [PATCH 81/91] Complete Python docstrings in /src/*.py files Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com> --- src/microesb.py | 100 +++++++++++++++++++++++++++++++++++++++++---- src/router.py | 11 +++-- src/testclasses.py | 50 +++++++++++++++++++++++ src/transformer.py | 11 +++-- 4 files changed, 156 insertions(+), 16 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index fb3d78f..8867c4d 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -74,6 +74,9 @@ def set_properties(self): def iterate(self): """ Recursive iterate through hierarchical class instances. + + :return: generator yielding class instances in hierarchy + :rtype: generator """ yield self for x in self: @@ -172,6 +175,11 @@ def property_dict(self): """ property_dict() method. Return all classes self._SYSProperties property_id, value dictionary. + + :return: dictionary of all properties excluding 'SYSServiceMethod' + :rtype: dict + + Decorated with @property so direct property access possible """ return_dict = {} @@ -203,7 +211,12 @@ def class_name(self): return self.__class__.__name__ def get_value_by_property_id(self, property_id): - """ get_value_by_property_id() method.""" + """ get_value_by_property_id() method. + + :param str property_id: property identifier + :return: attribute value for given property_id + :rtype: dynamic + """ return getattr(self, property_id) @@ -214,6 +227,7 @@ class ClassHandler(BaseHandler): def __init__(self): """ :ivar str _SYSType: const internal system type to differentiate handler types + :ivar classref _ServiceRouter: ServiceRouter instance reference """ super().__init__() self._SYSType = 'class_instance' @@ -238,6 +252,9 @@ def __iter__(self): """ overloaded internal __iter__() method. Overloaded for using iter() on class references. + + :return: generator yielding class instances from _SYSClassNames + :rtype: generator """ for class_name in self._SYSClassNames: yield getattr(self, class_name) @@ -278,6 +295,8 @@ def set_json_dict(self): Propagate self.json_dict with current class instance attribute values (self._SYSProperties) and with empty (None) class instance references (processed from JSONTransformer). + + Removes 'SYSServiceMethod' from json_dict and initializes child class references to None. """ for property_id in self._SYSProperties: @@ -314,6 +333,9 @@ def __iter__(self): """ overloaded internal __iter__() method. Overloaded for using iter() on class references. + + :return: generator yielding class instances from _object_container + :rtype: generator """ for class_instance in self._object_container: yield class_instance @@ -351,7 +373,10 @@ def set_properties(self, property_list): def set_json_dict(self): """ set_json_dict() method. - Preprare self.json_dict from self (self._object_container)). + Prepare self.json_dict from self._object_container. + + Iterates through all instances in _object_container and appends their + json_dict to create a list. Removes empty entries. """ self.logger.debug('Object container:{}'.format(self._object_container)) class_name = self.class_name @@ -366,7 +391,10 @@ def set_json_dict(self): def set_instance_json_dict(self): """ set_instance_json_dict() method. - Preprare self.json_dict from self._SYSProperties (used by JSONTransformer). + Prepare self.json_dict from self._SYSProperties (used by JSONTransformer). + + Extracts all property values and populates json_dict, ignoring any errors + from missing or inaccessible attributes. """ for property_id in self._SYSProperties: try: @@ -415,6 +443,9 @@ def __repr__(self): """ overloaded __repr__() method. Print out class mappings, properties and references. + + :return: formatted string with class mappings, properties and references + :rtype: str """ return 'Class mappings:{} properties:{} references:{}'.format( self._class_mappings, @@ -608,6 +639,17 @@ class ServiceExecuter(): """ def __init__(self): + """ + :ivar classref logger: logging logger reference + :ivar classref _cm_ref: class mapper reference + :ivar dict _con_ref_dict: connection reference dictionary + :ivar dict _class_hierarchy: class hierarchy dictionary + :ivar list _class_hierarchy_list: list of class hierarchy items + :ivar int _hierarchy_level: current hierarchy level counter + :ivar int _map_hierarchy_level: mapping hierarchy level counter + :ivar dict _class_hierarchy_comp: class hierarchy comparison dictionary + :ivar int _hierarchy_level_comp: hierarchy level comparison counter + """ self.logger = logging.getLogger(__name__) @@ -621,9 +663,14 @@ def __init__(self): self._hierarchy_level_comp = None def execute(self, class_mapper, service_data): - """ + """ execute() method. + + Execute service calls by creating ServiceMapper instances for each data item. + :param classref class_mapper: class mapper instance reference - :param list service_data: list of service call metadata dictionary items + :param dict service_data: service call data dictionary with 'data' key containing list of items + :return: list of ServiceMapper instances + :rtype: list """ rlist = [] @@ -637,9 +684,14 @@ def execute(self, class_mapper, service_data): return rlist def execute_get_hierarchy(self, class_mapper, service_data): - """ + """ execute_get_hierarchy() method. + + Execute service calls and return connected class hierarchies with json_dicts. + :param classref class_mapper: class mapper instance reference - :param list service_data: list of service call metadata dictionary items + :param dict service_data: service call data dictionary with 'data' key containing list of items + :return: list of connected class hierarchy dictionaries + :rtype: list """ rlist = [] @@ -659,6 +711,10 @@ def _connect_hierarchy(self, class_mapper_ref): """ _connect_hierarchy() method. Init method for connecting all generated json_dicts. + + :param classref class_mapper_ref: class mapper instance reference + :return: connected class hierarchy reference dictionary + :rtype: dict """ self._cm_ref = class_mapper_ref @@ -701,6 +757,12 @@ def _connect_hierarchy(self, class_mapper_ref): return cm_ref_dict def _map_object_instances(self, ref_dict): + """ _map_object_instances() method. + + Recursively map object instances to reference dictionary and perform JSON transform on root. + + :param dict ref_dict: reference dictionary to process + """ for class_name, class_props in ref_dict.items(): @@ -722,7 +784,11 @@ def _map_object_instances(self, ref_dict): def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent_dict=None): """ _connect_hierarchy_recursive() method. - Recursive connect all generated json_dicts to its parents. + Recursively connect all generated json_dicts to their parents. + + :param dict reference_dict: reference dictionary to process + :param str parent_class: parent class name (optional) + :param dict parent_dict: parent dictionary reference (optional) """ self.logger.debug('Parent dict:{}'.format(parent_dict)) @@ -756,6 +822,14 @@ def _connect_hierarchy_recursive(self, reference_dict, parent_class=None, parent ) def _rename_dict_key(self, rename_dict, parent_dict=None, parent_class=None): + """ _rename_dict_key() method. + + Recursively rename 'children' keys to 'children_processed' when hierarchy matches. + + :param dict rename_dict: dictionary to process + :param dict parent_dict: parent dictionary reference (optional) + :param str parent_class: parent class name (optional) + """ self.logger.debug('Parent class:{}'.format(parent_class)) self.logger.debug('RenameDict:{}'.format(rename_dict)) @@ -789,13 +863,21 @@ class ChildCounter(): """ def __init__(self): + """ + :ivar int _children_occurences: counter for children node occurrences + :ivar classref logger: logging logger reference + """ self._children_occurences = 0 self.logger = logging.getLogger(__name__) def get_sum_child_count(self, reference_dict): """ get_sum_child_count() method. - Count children nodes recursive and return sum. + Count children nodes recursively and return sum. + + :param dict reference_dict: reference dictionary to process + :return: total count of 'children' keys in hierarchy + :rtype: int """ self.logger.debug('Sum count ref-dict:{}'.format(reference_dict)) diff --git a/src/router.py b/src/router.py index cb94dce..eedea47 100644 --- a/src/router.py +++ b/src/router.py @@ -20,17 +20,20 @@ class ServiceRouter(): """ ServiceRouter class. + + Provides routing functionality to user-defined service methods in user_routing module. """ def send(self, send_id, metadata): """ send() method. - :param str send_id: service method id - :param dynamic metadata: first argument passed to service method function - :rtype: dict | None - Execute method with given id in `send_id` from imported user_routing.py module and return result dict or None. + + :param str send_id: service method id (function name in user_routing module) + :param dynamic metadata: first argument passed to service method function + :return: result from user routing function + :rtype: dict | None """ logger.debug('ServiceRouter send_id:{} metadata:{}'.format(send_id, metadata)) func_ref = getattr(mod_ref, send_id) diff --git a/src/testclasses.py b/src/testclasses.py index 4502674..46687b4 100644 --- a/src/testclasses.py +++ b/src/testclasses.py @@ -1,43 +1,93 @@ +# ]*[ --------------------------------------------------------------------- ]*[ +# . Micro ESB Test Classes Module . +# ]*[ --------------------------------------------------------------------- ]*[ +# . . +# . Copyright Claus Prüfer (2016 - 2026) . +# . . +# . . +# ]*[ --------------------------------------------------------------------- ]*[ + from microesb import microesb class Cert(microesb.ClassHandler): + """ Certificate handler class. + + Base class for certificate types (CA, Server, Client). + """ pass class CertCA(Cert): + """ Certificate Authority handler class. + + Handles CA certificate instances. + """ def __init__(self): + """ + :ivar str type: certificate type identifier + """ self.type = 'ca' super().__init__() class CertServer(Cert): + """ Server certificate handler class. + + Handles server certificate instances. + """ def __init__(self): + """ + :ivar str type: certificate type identifier + """ self.type = 'server' super().__init__() class CertClient(Cert): + """ Client certificate handler class. + + Handles client certificate instances. + """ def __init__(self): + """ + :ivar str type: certificate type identifier + """ self.type = 'client' super().__init__() class Smartcard(microesb.ClassHandler): + """ Smartcard handler class. + + Handles smartcard instances for certificate storage. + """ def __init__(self): super().__init__() class SmartcardContainer(microesb.ClassHandler): + """ Smartcard container handler class. + + Handles smartcard container instances for key pair storage. + """ def __init__(self): super().__init__() class Shipment(microesb.ClassHandler): + """ Shipment handler class. + + Handles shipment instances. + """ def __init__(self): super().__init__() class Palette(microesb.MultiClassHandler): + """ Palette handler class. + + Handles multiple palette instances using MultiClassHandler. + """ def __init__(self): super().__init__() diff --git a/src/transformer.py b/src/transformer.py index 63c88c0..351cfa5 100644 --- a/src/transformer.py +++ b/src/transformer.py @@ -11,19 +11,24 @@ class JSONTransformer(): - """ JSON transfomer class. + """ JSON transformer class. + + Provides JSON transformation functionality for class hierarchies. """ def __init__(self): """ - :ivar dict[dict] _json_dict: recursive internal properties processing dict + :ivar dict _json_dict: recursive internal properties processing dict """ self._json_dict = {} def json_transform(self): """ json_transform() method. - Recursive generate _json_dict for complete object hierarchy. + Recursively generate _json_dict for complete object hierarchy. + + Iterates through all elements in the hierarchy and calls set_json_dict() + on each to populate their json_dict representation. """ for element in self.iterate(): From 217c9e510c1c3c29037e851833cfb158df0effbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 11:37:03 +0100 Subject: [PATCH 82/91] Add missing references --- doc/source/api.rst | 4 +++- doc/source/conf.rst | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/source/api.rst b/doc/source/api.rst index 0369ec9..3371220 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -121,6 +121,8 @@ Example: Standard `ClassHandler` vs. `MultiClassHandler` implementation: For an example *service call metadata* (Host properties), see Example 1 (:ref:`example-number1`). +.. _implementation-classes: + 4. Implementation Classes ========================= @@ -270,7 +272,7 @@ The `ServiceMapper` class is responsible for mapping and populating the existing It requires the following as input parameters: - A reference to the `ClassMapper` instance -- **Service Call Metadata Dictionary** (see "Configuration / :ref:`service-call-metadata-config`") +- **Service Call Metadata Dictionary** (Metadata passed to the ServiceExecuter as input data) Example: diff --git a/doc/source/conf.rst b/doc/source/conf.rst index 7f7bf56..ca4b099 100644 --- a/doc/source/conf.rst +++ b/doc/source/conf.rst @@ -289,6 +289,8 @@ Example configuration: } } +.. _example-test-execution: + 5.3. Test Execution ******************* @@ -322,3 +324,8 @@ Example execution: class_mapper=class_mapper, service_data=service_metadata ) + +.. _class-mapping-config: + +3. Class Mappings +================= From 095b5c7320959b054e494e4b4d0112803e5a0c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 11:39:23 +0100 Subject: [PATCH 83/91] Reduce line length --- src/microesb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 8867c4d..b247f24 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -668,7 +668,7 @@ def execute(self, class_mapper, service_data): Execute service calls by creating ServiceMapper instances for each data item. :param classref class_mapper: class mapper instance reference - :param dict service_data: service call data dictionary with 'data' key containing list of items + :param dict service_data: data dictionary with 'data' key containing list of items :return: list of ServiceMapper instances :rtype: list """ @@ -689,7 +689,7 @@ def execute_get_hierarchy(self, class_mapper, service_data): Execute service calls and return connected class hierarchies with json_dicts. :param classref class_mapper: class mapper instance reference - :param dict service_data: service call data dictionary with 'data' key containing list of items + :param dict service_data: data dictionary with 'data' key containing list of items :return: list of connected class hierarchy dictionaries :rtype: list """ From 71f3858e4609f188bfd309c0b26fa6aaa2bf3aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 11:40:52 +0100 Subject: [PATCH 84/91] Remove unneeded "pass" statement --- src/testclasses.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/testclasses.py b/src/testclasses.py index 46687b4..86aaad7 100644 --- a/src/testclasses.py +++ b/src/testclasses.py @@ -15,7 +15,6 @@ class Cert(microesb.ClassHandler): Base class for certificate types (CA, Server, Client). """ - pass class CertCA(Cert): From 5fca329a072d040d0e4272199da8dcd1e462a223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 11:43:55 +0100 Subject: [PATCH 85/91] Fix numbering --- doc/source/conf.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/conf.rst b/doc/source/conf.rst index ca4b099..f04c3ad 100644 --- a/doc/source/conf.rst +++ b/doc/source/conf.rst @@ -291,7 +291,7 @@ Example configuration: .. _example-test-execution: -5.3. Test Execution +2.7. Test Execution ******************* Before executing the configuration, ensure all referenced files are correctly provided: From 84fa330512ca48a851d0cd469a42c775dbcfdbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 12:04:50 +0100 Subject: [PATCH 86/91] Add missing class mappings section --- doc/source/conf.rst | 93 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/doc/source/conf.rst b/doc/source/conf.rst index f04c3ad..718af6e 100644 --- a/doc/source/conf.rst +++ b/doc/source/conf.rst @@ -329,3 +329,96 @@ Example execution: 3. Class Mappings ================= + +The *Class Mapping Config* dictionary defines the mapping between class names used in the *Class Reference Config* and the actual *Implementation Classes*. + +3.1. Dictionary Format +********************** + +The *Class Mapping Config* is a simple dictionary where each key represents a class name used in the configuration, and each value represents the corresponding *Implementation Class* name. + +.. code-block:: python + + class_mapping = { + '$config_class_name1': '$implementation_class_name1', + '$config_class_name2': '$implementation_class_name2', + '$config_class_name3': '$implementation_class_name3' + } + +3.2. Flat Mappings +****************** + +In most cases, a "flat" mapping is used where each class name maps directly to itself. This is a 1:1 mapping without any aliasing. + +.. note:: + + All examples in this documentation use flat mappings without aliasing. + +Example from :ref:`example-number1`: + +.. literalinclude:: ../../example/01-hosting-use-case/class_mapping.py + :linenos: + +Example from :ref:`example-number2`: + +.. literalinclude:: ../../example/02-pki-management/class_mapping.py + :linenos: + +3.3. Aliasing +************* + +**Aliasing** is a more advanced technique that **must** be used when adding multiple classes with the same name as child classes to a parent class. + +With aliasing, multiple different configuration class names can map to the same *Implementation Class*. This allows you to instantiate the same class type multiple times with different property values in a hierarchical structure. + +.. warning:: + + Aliasing is only necessary when you need multiple child instances of the same class type under a single parent. For simple hierarchies, use flat mappings. + +3.4. Aliasing Example +********************* + +The integration test `TestMapping.test_class_mapping` in `/test/integration/test_base.py` demonstrates the use of aliasing: + +.. code-block:: python + + class_mapping = { + 'CertCA': 'CertCA', + 'SmartcardCA': 'Smartcard', + 'SmartcardREQ': 'Smartcard', + 'SmartcardContainer': 'SmartcardContainer' + } + +In this example: + +- `SmartcardCA` and `SmartcardREQ` are aliases that both map to the `Smartcard` implementation class. +- This allows the `CertCA` parent class to have two separate child instances of the `Smartcard` class with different configurations. +- Each alias can be referenced independently in the *Class Reference Config* and *Service Call Metadata*. + +The corresponding *Class Reference Config* shows how these aliases are used: + +.. code-block:: python + + class_reference = { + 'CertCA': { + 'property_ref': 'Cert', + 'children': { + 'SmartcardCA': { + 'property_ref': 'Smartcard', + 'children': { + 'SmartcardContainer': {'property_ref': 'SmartcardContainer'} + } + }, + 'SmartcardREQ': { + 'property_ref': 'Smartcard', + 'children': { + 'SmartcardContainer': {'property_ref': 'SmartcardContainer'} + } + } + } + } + } + +.. note:: + + For more information on aliasing in practical use, see :ref:`example-number3` in the Examples documentation. From 311617dd10b4acda4e743e0cbe95431f2844a634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 12:41:26 +0100 Subject: [PATCH 87/91] User NLAP over NLAMP, better! --- doc/source/routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/routing.rst b/doc/source/routing.rst index 9c6d955..f7f6a19 100644 --- a/doc/source/routing.rst +++ b/doc/source/routing.rst @@ -11,7 +11,7 @@ The **microesb** module provides flexible routing capabilities to direct service Simple Routing allows **direct**, **unencapsulated** routing of service calls to user-defined functions. This approach is suitable for straightforward use cases where service calls map directly to backend operations without complex orchestration requirements. -**Encapsulated Routing** is a methodology designed to abstract and handle each service entity as an externally callable service, encapsulated within a network-accessible container (e.g., application server) with scaling and AAA (Authentication, Authorization, Accounting) functionality, such as "Kubernetes / Nginx / HTTPS" or "FalconAS / NLAMP". +**Encapsulated Routing** is a methodology designed to abstract and handle each service entity as an externally callable service, encapsulated within a network-accessible container (e.g., application server) with scaling and AAA (Authentication, Authorization, Accounting) functionality, such as "Kubernetes / Nginx / HTTPS" or "FalconAS / NLAP". The **Encapsulated Routing** concept is described in greater detail in section 2. From 23daaa7451d3e9da652e05d0a49642ba014283cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 12:42:08 +0100 Subject: [PATCH 88/91] Add return result --- example/02-pki-management/user_routing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/02-pki-management/user_routing.py b/example/02-pki-management/user_routing.py index dcf677b..5e6cbe8 100644 --- a/example/02-pki-management/user_routing.py +++ b/example/02-pki-management/user_routing.py @@ -10,7 +10,7 @@ def CertGetById(metadata): ) def CertStore(metadata): - mongodb.cert.insert_one(metadata) + return mongodb.cert.insert_one(metadata) def KeypairGenerate(metadata): return True From 49076fc756bbd678f175931a87693c5f9d4f09de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 12:42:28 +0100 Subject: [PATCH 89/91] Call as public member --- example/02-pki-management/service_implementation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/02-pki-management/service_implementation.py b/example/02-pki-management/service_implementation.py index e2da39b..15e87c7 100644 --- a/example/02-pki-management/service_implementation.py +++ b/example/02-pki-management/service_implementation.py @@ -12,7 +12,7 @@ class Cert(microesb.ClassHandler, metaclass=abc.ABCMeta): def __init__(self): super().__init__() - self._register_property( + self.register_property( 'generation_timestamp', { 'type': 'str', @@ -22,7 +22,7 @@ def __init__(self): } ) - self._register_property( + self.register_property( 'cert_data', { 'type': 'str', @@ -152,7 +152,7 @@ class Smartcard(microesb.ClassHandler): def __init__(self): super().__init__() - self._register_property( + self.register_property( 'gen_status', { 'type': bool, From 5472784dc226d1a7a0c1cdbe4dc7ed6911038ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 12:43:13 +0100 Subject: [PATCH 90/91] Fix multiple bugs --- src/microesb.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index b247f24..1384d9d 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -124,8 +124,8 @@ def _add_sys_default_properties(self, properties): } return properties - def _register_property(self, property_id, property_item): - """ _register_property() method. + def register_property(self, property_id, property_item): + """ register_property() method. :param str property_id: property id (internal class attribute name) :param dict property_item: property item to be registered for internal processing only @@ -134,8 +134,8 @@ def _register_property(self, property_id, property_item): additional properties not defined in Service-Properties, e.g. generated data, time-stamps or similar. - Use this private method for this purpose. - """ + Service-Implementation classes should call this method to register such + additional internal properties for processing by the Micro ESB framework. """ self._SYSPropertiesRegister[property_id] = property_item def _set_property(self, property_id, value): @@ -697,11 +697,10 @@ def execute_get_hierarchy(self, class_mapper, service_data): rlist = [] for item in service_data['data']: class_mapper_copy = copy.deepcopy(class_mapper) - sm_ref = ServiceMapper( + ServiceMapper( class_mapper=class_mapper_copy, service_call_data=item ) - rlist.append( self._connect_hierarchy(class_mapper_copy) ) @@ -864,10 +863,10 @@ class ChildCounter(): def __init__(self): """ - :ivar int _children_occurences: counter for children node occurrences + :ivar int _children_occurrences: counter for children node occurrences :ivar classref logger: logging logger reference """ - self._children_occurences = 0 + self._children_occurrences = 0 self.logger = logging.getLogger(__name__) def get_sum_child_count(self, reference_dict): @@ -884,10 +883,10 @@ def get_sum_child_count(self, reference_dict): for class_name, class_properties in reference_dict.items(): if 'children' in class_properties: - self._children_occurences += 1 + self._children_occurrences += 1 self.get_sum_child_count(class_properties['children']) - return self._children_occurences + return self._children_occurrences # import classes into current namespace From cf0de505fbf27f5dd0df74c5d924f0bcc947f27b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Tue, 13 Jan 2026 12:44:03 +0100 Subject: [PATCH 91/91] Update src/microesb.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/microesb.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/microesb.py b/src/microesb.py index 1384d9d..a622cfd 100644 --- a/src/microesb.py +++ b/src/microesb.py @@ -303,10 +303,8 @@ def set_json_dict(self): self.logger.debug('processing property:{}'.format(property_id)) self.json_dict[property_id] = getattr(self, property_id) - try: - del self.json_dict['SYSServiceMethod'] - except KeyError as e: - pass + # Remove optional service method key if present; ignore if absent. + self.json_dict.pop('SYSServiceMethod', None) self.logger.debug('self._SYSProperties:{}'.format(self._SYSProperties))