From 327d02db766c957ec6bce3676af5b4e99e1fb533 Mon Sep 17 00:00:00 2001 From: bobslee Date: Thu, 21 Sep 2023 19:11:05 +0200 Subject: [PATCH] Add component_class_mapping feature (argument) for the Builder instantiation Map a custom component type to an implemented component class, which is then loaded. Example, also added in README and unitest: component_class_mapping = {'custom_editgrid': editgridComponent} builder = Builder( schema_json, component_class_mapping=component_class_mapping, ) Also refactored the Builder constructor, from some `kwargs` to keyword arguments. --- CHANGELOG.md | 9 +++++ README.md | 26 +++++++++++++++ formiodata/builder.py | 48 +++++++++++++++++++-------- formiodata/form.py | 6 ++-- pyproject.toml | 2 +- tests/test_component_class_mapping.py | 41 +++++++++++++++++++++++ 6 files changed, 116 insertions(+), 16 deletions(-) create mode 100644 tests/test_component_class_mapping.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a444f4..7ca298a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.2.0 + +New "component class mapping feature" for the Builder instantiation:\ +Map a custom component type to an implemented component class, which is then loaded. + +An example is available in the unittests of file: `tests/test_component_class_mapping.py` + +Also refactored the Builder constructor, from some `kwargs` to keyword arguments. + ## 1.1.0 Put component classes as files in the new `components` directory.\ diff --git a/README.md b/README.md index 96c119a..a0408dd 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,32 @@ Datetime: datetime.datetime(2021, 5, 8, 11, 41, 5, 919943), Fahrenheit: 131 {'firstName': , 'lastName: }, {'email': , 'companyName: } ] + +########################## +# components class mapping +########################## + +# Below an example which verbosely shows the feature: +# - First set a custom component type 'custom_editgrid' in the Builder JSON schema. +# - Check (assert) whether the component object is an instance of the mapped editgridComponent. +# This code is also present in the unittest (file): tests/test_component_class_mapping.py + +schema_dict = json.loads(self.builder_json) + +# change 'editgrid' type to 'custom_editgrid' +for comp in schema_dict['components']: + if comp['key'] == 'editGrid': + comp['type'] = 'custom_editgrid' + +component_class_mapping = {'custom_editgrid': editgridComponent} +builder = Builder( + schema_json, + component_class_mapping=component_class_mapping, +) + +custom_editgrid = builder.components['editGrid'] +self.assertIsInstance(custom_editgrid, editgridComponent) +self.assertEqual(custom_editgrid.type, 'custom_editgrid') ``` ## Unit tests diff --git a/formiodata/builder.py b/formiodata/builder.py index 06e07ea..3d8da6c 100644 --- a/formiodata/builder.py +++ b/formiodata/builder.py @@ -14,10 +14,24 @@ class Builder: - def __init__(self, schema_json, **kwargs): + def __init__( + self, + schema_json, + language='en', + i18n={}, + resources={}, + load_path_objects=True, + component_class_mapping={}, + **kwargs + ): """ @param schema_json - @param lang + @param language + @param i18n + @param resources + @param resources + @param load_path_objects + @param component_class_mapping """ if isinstance(schema_json, dict): @@ -25,13 +39,11 @@ def __init__(self, schema_json, **kwargs): else: self.schema = json.loads(schema_json) - self.language = kwargs.get('language', 'en') - # i18n (translations) - self.i18n = kwargs.get('i18n', {}) - self.resources = kwargs.get('resources', {}) - - # options from kwargs - self.load_path_objects = kwargs.get('load_path_objects', True) + self.language = language + self.i18n = i18n + self.resources = resources + self.load_path_objects = load_path_objects + self.component_class_mapping = component_class_mapping # Raw components from the schema self._raw_components = [] @@ -79,10 +91,20 @@ def get_component_object(self, component): component_type = component.get('type') if component_type: try: - cls_name = '%sComponent' % component_type - import_path = 'formiodata.components.%s' % component_type - module = __import__(import_path, fromlist=[cls_name]) - cls = getattr(module, cls_name) + try: + mapping_value = self.component_class_mapping[component_type] + if isinstance(mapping_value, str): + cls_name = '%sComponent' % mapping_value + import_path = 'formiodata.components.%s' % mapping_value + module = __import__(import_path, fromlist=[cls_name]) + cls = getattr(module, cls_name) + else: + cls = self.component_class_mapping[component_type] + except KeyError: + cls_name = '%sComponent' % component_type + import_path = 'formiodata.components.%s' % component_type + module = __import__(import_path, fromlist=[cls_name]) + cls = getattr(module, cls_name) component_obj = cls(component, self, language=self.language, i18n=self.i18n, resources=self.resources) return component_obj except AttributeError as e: diff --git a/formiodata/form.py b/formiodata/form.py index d1a1bff..61feaf5 100644 --- a/formiodata/form.py +++ b/formiodata/form.py @@ -12,11 +12,13 @@ class Form: - def __init__(self, form_json, builder=None, builder_schema_json=None, lang='en', **kwargs): + def __init__( + self, form_json, builder=None, builder_schema_json=None, lang="en", **kwargs + ): """ @param form_json @param builder Builder - @param builder_schema + @param builder_schema_json @param lang """ if isinstance(form_json, dict): diff --git a/pyproject.toml b/pyproject.toml index 5296747..5a093a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "formio-data" -version = "1.1.0" +version = "1.2.0" homepage = "https://github.com/novacode-nl/python-formio-data" description = "formio.js JSON-data API" readme = "README.md" diff --git a/tests/test_component_class_mapping.py b/tests/test_component_class_mapping.py new file mode 100644 index 0000000..aabeff9 --- /dev/null +++ b/tests/test_component_class_mapping.py @@ -0,0 +1,41 @@ +# Copyright Nova Code (http://www.novacode.nl) +# See LICENSE file for full licensing details. + +import json + +from test_common import CommonTestCase +from formiodata.builder import Builder +from formiodata.components.editgrid import editgridComponent + + +class ComponentClassMappingTestCase(CommonTestCase): + + def setUp(self): + super().setUp() + + schema_dict = json.loads(self.builder_json) + for comp in schema_dict['components']: + if comp['key'] == 'editGrid': + comp['type'] = 'custom_editgrid' + + self.schema_json_component_class_mapping = json.dumps(schema_dict) + + def test_component_class_mapping_with_class(self): + component_class_mapping = {'custom_editgrid': editgridComponent} + builder = Builder( + self.schema_json_component_class_mapping, + component_class_mapping=component_class_mapping, + ) + custom_editgrid = builder.components['editGrid'] + self.assertIsInstance(custom_editgrid, editgridComponent) + self.assertEqual(custom_editgrid.type, 'custom_editgrid') + + def test_component_class_mapping_with_string(self): + component_class_mapping = {'custom_editgrid': 'editgrid'} + builder = Builder( + self.schema_json_component_class_mapping, + component_class_mapping=component_class_mapping, + ) + custom_editgrid = builder.components['editGrid'] + self.assertIsInstance(custom_editgrid, editgridComponent) + self.assertEqual(custom_editgrid.type, 'custom_editgrid')