diff --git a/backend/SC4SNMP_UI_backend/common/backend_ui_conversions.py b/backend/SC4SNMP_UI_backend/common/backend_ui_conversions.py index ced9a20..cad9d05 100644 --- a/backend/SC4SNMP_UI_backend/common/backend_ui_conversions.py +++ b/backend/SC4SNMP_UI_backend/common/backend_ui_conversions.py @@ -277,6 +277,8 @@ def ui2backend(self, document: dict, **kwargs): raise ValueError("No delete provided") def backend2ui(self, document: dict, **kwargs): + if "inventory_type" not in kwargs.keys(): + raise ValueError("No inventory_type provided") profiles_mongo = document['profiles'] if len(profiles_mongo) > 0: profiles = profiles_mongo.split(";") @@ -284,6 +286,7 @@ def backend2ui(self, document: dict, **kwargs): profiles = [] result = { '_id': str(document["_id"]), + 'inventoryType': kwargs['inventory_type'], 'address': document['address'], 'port': str(document['port']), 'version': document['version'], diff --git a/backend/SC4SNMP_UI_backend/common/inventory_utils.py b/backend/SC4SNMP_UI_backend/common/inventory_utils.py index 2f7e882..f0df231 100644 --- a/backend/SC4SNMP_UI_backend/common/inventory_utils.py +++ b/backend/SC4SNMP_UI_backend/common/inventory_utils.py @@ -25,7 +25,7 @@ def update_profiles_in_inventory(profile_to_search: str, process_record: Callabl inventory_records = list(mongo_inventory.find({"profiles": {"$regex": f'.*{profile_to_search}.*'}})) for record in inventory_records: record_id = record["_id"] - record_updated = inventory_conversion.backend2ui(record) + record_updated = inventory_conversion.backend2ui(record, inventory_type=None) # inventory_type isn't used index_to_update = record_updated["profiles"].index(profile_to_search) record_updated = process_record(index_to_update, record_updated, kwargs) record_updated = inventory_conversion.ui2backend(record_updated, delete=False) @@ -191,12 +191,9 @@ def add_group_to_inventory(self, group_name: str, group_port: str, group_object= existing_inventory_record = list(self._mongo_inventory.find({'address': group_name, "delete": False})) deleted_inventory_record = list(self._mongo_inventory.find({'address': group_name, "delete": True})) group = list(self._mongo_groups.find({group_name: {"$exists": 1}})) - if len(group) == 0 and len(existing_inventory_record) == 0: - group_added = True - message = f"Group {group_name} doesn't exist in the configuration. Treating {group_name} as a hostname." - elif len(group) == 0 and len(existing_inventory_record) > 0: + if len(group) == 0: group_added = False - message = f"{group_name} has already been configured. Record was not added." + message = f"Group {group_name} doesn't exist in the configuration. Record was not added." elif len(existing_inventory_record) > 0: group_added = False message = f"Group {group_name} has already been added to the inventory. Record was not added." @@ -229,8 +226,11 @@ def edit_group_in_inventory(self, group_name: str, group_id: str, group_object=N group_id = ObjectId(group_id) existing_inventory_record = list(self._mongo_inventory.find({'address': group_name, "delete": False})) deleted_inventory_record = list(self._mongo_inventory.find({'address': group_name, "delete": True})) - - if len(existing_inventory_record) == 0 or (len(existing_inventory_record) > 0 and existing_inventory_record[0]["_id"] == group_id): + group = list(self._mongo_groups.find({group_name: {"$exists": 1}})) + if len(group) == 0: + group_edited = False + message = f"Group {group_name} doesn't exist in the configuration. Record was not edited." + elif len(existing_inventory_record) == 0 or (len(existing_inventory_record) > 0 and existing_inventory_record[0]["_id"] == group_id): message = "success" group_edited = True if edit and group_object is not None: @@ -249,7 +249,7 @@ def edit_group_in_inventory(self, group_name: str, group_id: str, group_object=N if len(deleted_inventory_record) > 0: self._mongo_inventory.delete_one({"_id": deleted_inventory_record[0]["_id"]}) else: - message = f"Group wit name {group_name} already exists. Record was not edited." + message = f"Group with name {group_name} already exists. Record was not edited." group_edited = False return group_edited, message diff --git a/backend/SC4SNMP_UI_backend/inventory/routes.py b/backend/SC4SNMP_UI_backend/inventory/routes.py index 959e021..9bec328 100644 --- a/backend/SC4SNMP_UI_backend/inventory/routes.py +++ b/backend/SC4SNMP_UI_backend/inventory/routes.py @@ -11,6 +11,16 @@ mongo_groups = mongo_client.sc4snmp.groups_ui mongo_inventory = mongo_client.sc4snmp.inventory_ui +def get_inventory_type(document): + if not document["address"][0].isdigit(): + groups = list(mongo_groups.find({document["address"]: {"$exists": 1}})) + if groups: + result = "Group" + else: + result = "Host" + else: + result = "Host" + return result @inventory_blueprint.route('/inventory//') @cross_origin() @@ -22,7 +32,8 @@ def get_inventory_list(page_num, dev_per_page): inventory = list(mongo_inventory.find({"delete": False}).skip(skips).limit(dev_per_page)) inventory_list = [] for inv in inventory: - inventory_list.append(inventory_conversion.backend2ui(inv)) + inventory_type = get_inventory_type(inv) + inventory_list.append(inventory_conversion.backend2ui(inv, inventory_type=inventory_type)) return jsonify(inventory_list) @@ -37,9 +48,10 @@ def get_inventory_count(): @cross_origin() def add_inventory_record(): inventory_obj = request.json + inventory_type = inventory_obj["inventoryType"] inventory_obj = inventory_conversion.ui2backend(inventory_obj, delete=False) handler = HandleNewDevice(mongo_groups, mongo_inventory) - if inventory_obj["address"][0].isdigit(): + if inventory_type == "Host": record_added, message = handler.add_single_host(inventory_obj["address"], str(inventory_obj["port"]), inventory_obj, True) else: @@ -68,23 +80,22 @@ def delete_inventory_record(inventory_id): @cross_origin() def update_inventory_record(inventory_id): inventory_obj = request.json + inventory_type = inventory_obj["inventoryType"] inventory_obj = inventory_conversion.ui2backend(inventory_obj, delete=False) current_inventory = list(mongo_inventory.find({"_id": ObjectId(inventory_id)}))[0] + current_inventory_type = get_inventory_type(current_inventory) handler = HandleNewDevice(mongo_groups, mongo_inventory) - is_current_a_single_host = current_inventory["address"][0].isdigit() - is_new_a_single_host = inventory_obj["address"][0].isdigit() - if is_current_a_single_host != is_new_a_single_host: + if inventory_type != current_inventory_type: result = jsonify({"message": "Can't edit single host to the group or group to the single host"}), 400 else: - if is_new_a_single_host: + if inventory_type == "Host": record_edited, message = handler.edit_single_host(inventory_obj["address"], str(inventory_obj["port"]), str(inventory_id), inventory_obj, True) else: record_edited, message = handler.edit_group_in_inventory(inventory_obj["address"], str(inventory_id), inventory_obj, True) if record_edited: if message == "success" or message is None: - print(message) result = jsonify("success"), 200 else: result = jsonify({"message": message}), 200 diff --git a/backend/tests/common/test_backend_ui_conversions.py b/backend/tests/common/test_backend_ui_conversions.py index 33d790d..0a41f41 100644 --- a/backend/tests/common/test_backend_ui_conversions.py +++ b/backend/tests/common/test_backend_ui_conversions.py @@ -207,6 +207,7 @@ def setUpClass(cls): cls.ui_inventory_1 = { "_id": common_id, + "inventoryType": "Host", "address": "11.0.78.114", "port": "161", "version": "3", @@ -234,6 +235,7 @@ def setUpClass(cls): cls.ui_inventory_2 = { "_id": common_id, + "inventoryType": "Group", "address": "group_1", "port": "1161", "version": "2c", @@ -320,8 +322,8 @@ def test_group_device_ui_to_backend(self): self.assertDictEqual(group_device_conversion.ui2backend(self.ui_group_device_4), device) def test_inventory_backend_to_ui(self): - self.assertDictEqual(inventory_conversion.backend2ui(self.backend_inventory_1), self.ui_inventory_1) - self.assertDictEqual(inventory_conversion.backend2ui(self.backend_inventory_2), self.ui_inventory_2) + self.assertDictEqual(inventory_conversion.backend2ui(self.backend_inventory_1, inventory_type="Host"), self.ui_inventory_1) + self.assertDictEqual(inventory_conversion.backend2ui(self.backend_inventory_2, inventory_type="Group"), self.ui_inventory_2) def test_inventory_ui_to_backend(self): back_inv = self.backend_inventory_1 diff --git a/backend/tests/ui_handling/get_endpoints/test_get_endpoints.py b/backend/tests/ui_handling/get_endpoints/test_get_endpoints.py index cc7b53b..b91591f 100644 --- a/backend/tests/ui_handling/get_endpoints/test_get_endpoints.py +++ b/backend/tests/ui_handling/get_endpoints/test_get_endpoints.py @@ -1,7 +1,6 @@ from unittest import mock from bson import ObjectId - @mock.patch("pymongo.collection.Collection.find") def test_get_profile_names(m_client, client): m_client.return_value = [{ @@ -264,8 +263,9 @@ def test_get_devices_of_group(m_client, client): assert response.json == third_result +@mock.patch("SC4SNMP_UI_backend.inventory.routes.get_inventory_type") @mock.patch("pymongo.cursor.Cursor.limit") -def test_get_inventory_list(m_cursor, client): +def test_get_inventory_list(m_cursor, m_get_inventory_type, client): common_id = "635916b2c8cb7a15f28af40a" m_cursor.side_effect = [ @@ -314,9 +314,12 @@ def test_get_inventory_list(m_cursor, client): ] ] + m_get_inventory_type.side_effect = ["Host", "Group", "Group"] + first_result = [ { "_id": common_id, + "inventoryType": "Host", "address": "11.0.78.114", "port": "161", "version": "3", @@ -329,6 +332,7 @@ def test_get_inventory_list(m_cursor, client): }, { "_id": common_id, + "inventoryType": "Group", "address": "group_1", "port": "1161", "version": "2c", @@ -344,6 +348,7 @@ def test_get_inventory_list(m_cursor, client): second_result = [ { "_id": common_id, + "inventoryType": "Group", "address": "group_2", "port": "161", "version": "3", diff --git a/backend/tests/ui_handling/post_endpoints/test_post_inventory.py b/backend/tests/ui_handling/post_endpoints/test_post_inventory.py index 7c164e2..d5ad0c1 100644 --- a/backend/tests/ui_handling/post_endpoints/test_post_inventory.py +++ b/backend/tests/ui_handling/post_endpoints/test_post_inventory.py @@ -7,6 +7,7 @@ # TEST ADDING A SINGLE HOST ui_inventory_new = lambda : { + "inventoryType": "Host", "address": "11.0.78.114", "port": "161", "version": "3", @@ -178,6 +179,7 @@ def test_edit_single_host_address_and_port_success(m_find, m_insert, m_update, m m_update.return_value = None m_delete.return_value = None ui_inventory_new_address_port = { + "inventoryType": "Host", "address": "1.0.0.0", "port": "1111", "version": "3", @@ -267,6 +269,7 @@ def test_edit_single_host_failed(m_find, m_insert, m_update, m_delete, client): existing_id = "035916b2c8cb7a15f28af40b" ui_inventory_new = { + "inventoryType": "Host", "address": "0.0.0.0", "port": "1161", "version": "3", @@ -319,6 +322,7 @@ def test_edit_single_host_failed(m_find, m_insert, m_update, m_delete, client): # TEST ADDING A GROUP new_group_ui_inventory = lambda : { + "inventoryType": "Group", "address": "group_1", "port": "161", "version": "3", @@ -467,6 +471,7 @@ def test_add_group_with_hosts_configured_failure(m_find, m_insert, m_delete, cli m_delete.return_value = None new_group_ui_failure = { + "inventoryType": "Group", "address": "group_1", "port": "161", "version": "3", @@ -519,6 +524,7 @@ def test_add_group_with_host_configured_multiple_times_failure(m_find, m_insert, m_delete.return_value = None new_group_ui_failure = { + "inventoryType": "Group", "address": "group_1", "port": "161", "version": "3", @@ -604,10 +610,10 @@ def test_add_group_without_configuration(m_find, m_insert, m_delete, client): response = client.post(f"/inventory/add", json=new_group_ui_inventory()) m_find.assert_has_calls(calls_find) - assert m_insert.call_args == call(new_group_backend_inventory()) + assert not m_insert.called assert not m_delete.called assert response.json == {"message": "Group group_1 doesn't exist in the configuration. " - "Treating group_1 as a hostname."} + "Record was not added."} @mock.patch("pymongo.collection.Collection.delete_one") @@ -633,13 +639,14 @@ def test_add_group_without_configuration_failure(m_find, m_insert, m_delete, cli m_find.assert_has_calls(calls_find) assert not m_insert.called assert not m_delete.called - assert response.json == {"message": "group_1 has already been configured. Record was not added."} + assert response.json == {"message": "Group group_1 doesn't exist in the configuration. Record was not added."} # TEST UPDATING A GROUP -ui_edited_group = lambda : { +ui_edited_inventory_group = lambda : { + "inventoryType": "Group", "address": "group_1", "port": "161", "version": "3", @@ -651,7 +658,7 @@ def test_add_group_without_configuration_failure(m_find, m_insert, m_delete, cli "smartProfiles": False } -edited_group = lambda : { +edited_inventory_group = lambda : { "address": "group_1", "port": 161, "version": "3", @@ -664,7 +671,7 @@ def test_add_group_without_configuration_failure(m_find, m_insert, m_delete, cli "delete": False } -backend_existing_edit_group = lambda : { +backend_inventory_existing_edit_group = lambda : { "_id": ObjectId(common_id), "address": "group_1", "port": 161, @@ -678,6 +685,11 @@ def test_add_group_without_configuration_failure(m_find, m_insert, m_delete, cli "delete": False } +backend_existing_edit_group = lambda : { + "_id": ObjectId(common_id), + "group_1": [{"address": "1.1.1.1"}] +} + @mock.patch("pymongo.collection.Collection.delete_one") @mock.patch("pymongo.collection.Collection.update_one") @mock.patch("pymongo.collection.Collection.insert_one") @@ -689,22 +701,26 @@ def test_update_group_without_changing_name_success(m_find, m_insert, m_update, m_delete.return_value = None m_find.side_effect = [ - [backend_existing_edit_group()], # call from HandleNewDevice.update_inventory_record - [backend_existing_edit_group()], # call from HandleNewDevice.edit_group_in_inventory + [backend_inventory_existing_edit_group()], # call from inventory/routes/update_inventory_record + [backend_existing_edit_group()], # call from inventory/routes/get_inventory_type + [backend_inventory_existing_edit_group()], # call from HandleNewDevice.edit_group_in_inventory [], # call from HandleNewDevice.edit_group_in_inventory - [backend_existing_edit_group()] # call from HandleNewDevice.edit_group_in_inventory + [{"group_1": []}], # call from HandleNewDevice.edit_group_in_inventory + [backend_inventory_existing_edit_group()] # call from HandleNewDevice.edit_group_in_inventory ] calls_find = [ - call({"_id": ObjectId(common_id)}), # call from HandleNewDevice.update_inventory_record + call({"_id": ObjectId(common_id)}), # call from inventory/routes/update_inventory_record + call({"group_1": {"$exists": 1}}), # call from inventory/routes/get_inventory_type call({'address': "group_1", "delete": False}), # call from HandleNewDevice.edit_group_in_inventory call({'address': "group_1", "delete": True}), # call from HandleNewDevice.edit_group_in_inventory + call({"group_1": {"$exists": 1}}), # call from HandleNewDevice.edit_group_in_inventory call({"_id": ObjectId(common_id)}) # call from HandleNewDevice.edit_group_in_inventory ] - response = client.post(f"/inventory/update/{common_id}", json=ui_edited_group()) + response = client.post(f"/inventory/update/{common_id}", json=ui_edited_inventory_group()) m_find.assert_has_calls(calls_find) - assert m_update.call_args == call({"_id": ObjectId(common_id)}, {"$set": edited_group()}) + assert m_update.call_args == call({"_id": ObjectId(common_id)}, {"$set": edited_inventory_group()}) assert not m_insert.called assert not m_delete.called assert response.json == "success" @@ -721,6 +737,7 @@ def test_update_group_with_changing_name_success(m_find, m_insert, m_update, m_d m_delete.return_value = None new_name_group_ui = { + "inventoryType": "Group", "address": "group_2", "port": "161", "version": "3", @@ -751,19 +768,23 @@ def test_update_group_with_changing_name_success(m_find, m_insert, m_update, m_d } m_find.side_effect = [ - [backend_existing_edit_group()], # call from HandleNewDevice.update_inventory_record + [backend_inventory_existing_edit_group()], # call from inventory/routes/update_inventory_record + [backend_existing_edit_group()], # call from inventory/routes/get_inventory_type [], # call from HandleNewDevice.edit_group_in_inventory [], # call from HandleNewDevice.edit_group_in_inventory - [backend_existing_edit_group()], # call from HandleNewDevice.edit_group_in_inventory + [{"group_2": []}], # call from HandleNewDevice.edit_group_in_inventory + [backend_inventory_existing_edit_group()], # call from HandleNewDevice.edit_group_in_inventory [], # call from HandleNewDevice.add_group_to_inventory [], # call from HandleNewDevice.add_group_to_inventory [second_group_backend] # call from HandleNewDevice.add_group_to_inventory ] calls_find = [ - call({"_id": ObjectId(common_id)}), # call from HandleNewDevice.update_inventory_record + call({"_id": ObjectId(common_id)}), # call from inventory/routes/update_inventory_record + call({"group_1": {"$exists": 1}}), # call from inventory/routes/get_inventory_type call({'address': "group_2", "delete": False}), # call from HandleNewDevice.edit_group_in_inventory call({'address': "group_2", "delete": True}), # call from HandleNewDevice.edit_group_in_inventory + call({"group_2": {"$exists": 1}}), # call from HandleNewDevice.edit_group_in_inventory call({"_id": ObjectId(common_id)}), # call from HandleNewDevice.edit_group_in_inventory call({'address': "group_2", "delete": False}), # call from HandleNewDevice.add_group_to_inventory call({'address': "group_2", "delete": True}), # call from HandleNewDevice.add_group_to_inventory @@ -788,6 +809,7 @@ def test_update_group_to_already_configured_failure(m_find, m_insert, m_update, m_delete.return_value = None new_name_group_ui = { + "inventoryType": "Group", "address": "group_2", "port": "161", "version": "3", @@ -814,15 +836,19 @@ def test_update_group_to_already_configured_failure(m_find, m_insert, m_update, } m_find.side_effect = [ - [backend_existing_edit_group()], # call from HandleNewDevice.update_inventory_record + [backend_inventory_existing_edit_group()], # call from inventory/routes/update_inventory_record + [backend_existing_edit_group()], # call from inventory/routes/get_inventory_type [inventory_existing_other_group], # call from HandleNewDevice.edit_group_in_inventory - [] # call from HandleNewDevice.edit_group_in_inventory + [], # call from HandleNewDevice.edit_group_in_inventory + [{"group_2": []}], # call from HandleNewDevice.edit_group_in_inventory ] calls_find = [ - call({"_id": ObjectId(common_id)}), # call from HandleNewDevice.update_inventory_record + call({"_id": ObjectId(common_id)}), # call from inventory/routes/update_inventory_record + call({"group_1": {"$exists": 1}}), # call from inventory/routes/get_inventory_type call({'address': "group_2", "delete": False}), # call from HandleNewDevice.edit_group_in_inventory call({'address': "group_2", "delete": True}), # call from HandleNewDevice.edit_group_in_inventory + call({"group_2": {"$exists": 1}}), # call from HandleNewDevice.edit_group_in_inventory ] response = client.post(f"/inventory/update/{common_id}", json=new_name_group_ui) @@ -830,7 +856,7 @@ def test_update_group_to_already_configured_failure(m_find, m_insert, m_update, assert not m_update.called assert not m_insert.called assert not m_delete.called - assert response.json == {"message": "Group wit name group_2 already exists. Record was not edited."} + assert response.json == {"message": "Group with name group_2 already exists. Record was not edited."} @mock.patch("pymongo.collection.Collection.delete_one") @mock.patch("pymongo.collection.Collection.update_one") @@ -842,6 +868,7 @@ def test_update_group_to_other_group_with_host_already_configured_failure(m_find m_delete.return_value = None new_group_ui_failure = { + "inventoryType": "Group", "address": "group_3", "port": "161", "version": "3", @@ -859,10 +886,12 @@ def test_update_group_to_other_group_with_host_already_configured_failure(m_find } m_find.side_effect = [ - [backend_existing_edit_group()], # call from HandleNewDevice.update_inventory_record + [backend_inventory_existing_edit_group()], # call from inventory/routes/update_inventory_record + [backend_existing_edit_group()], # call from inventory/routes/get_inventory_type [], # call from HandleNewDevice.edit_group_in_inventory [], # call from HandleNewDevice.edit_group_in_inventory - [backend_existing_edit_group()], # call from HandleNewDevice.edit_group_in_inventory + [{"group_3": []}], # call from HandleNewDevice.edit_group_in_inventory + [backend_inventory_existing_edit_group()], # call from HandleNewDevice.edit_group_in_inventory [], # call from HandleNewDevice.add_group_to_inventory [], # call from HandleNewDevice.add_group_to_inventory @@ -874,9 +903,11 @@ def test_update_group_to_other_group_with_host_already_configured_failure(m_find ] calls_find = [ - call({"_id": ObjectId(common_id)}), # call from HandleNewDevice.update_inventory_record + call({"_id": ObjectId(common_id)}), # call from inventory/routes/update_inventory_record + call({"group_1": {"$exists": 1}}), # call from inventory/routes/get_inventory_type call({'address': "group_3", "delete": False}), # call from HandleNewDevice.edit_group_in_inventory call({'address': "group_3", "delete": True}), # call from HandleNewDevice.edit_group_in_inventory + call({"group_3": {"$exists": 1}}), # call from HandleNewDevice.edit_group_in_inventory call({"_id": ObjectId(common_id)}), # call from HandleNewDevice.edit_group_in_inventory call({'address': "group_3", "delete": False}), # call from HandleNewDevice.add_group_to_inventory @@ -906,6 +937,7 @@ def test_update_group_host_or_host_to_group_failure(m_find, m_insert, m_update, m_delete.return_value = None ui_edit_group_to_host = { + "inventoryType": "Host", "address": "1.1.1.1", "port": "161", "version": "3", @@ -918,11 +950,13 @@ def test_update_group_host_or_host_to_group_failure(m_find, m_insert, m_update, } m_find.side_effect = [ - [backend_existing_edit_group()], # call from HandleNewDevice.update_inventory_record + [backend_inventory_existing_edit_group()], # call from inventory/routes/update_inventory_record + [backend_existing_edit_group()], # call from inventory/routes/get_inventory_type ] calls_find = [ - call({"_id": ObjectId(common_id)}), # call from HandleNewDevice.update_inventory_record + call({"_id": ObjectId(common_id)}), # call from inventory/routes/update_inventory_record + call({"group_1": {"$exists": 1}}), # call from inventory/routes/get_inventory_type ] response = client.post(f"/inventory/update/{common_id}", json=ui_edit_group_to_host) @@ -947,6 +981,7 @@ def test_update_group_host_or_host_to_group_failure(m_find, m_insert, m_update, } ui_edit_group_to_host2 = { + "inventoryType": "Group", "address": "group_1", "port": "161", "version": "3", diff --git a/frontend/packages/manager/src/components/ErrorsModal.jsx b/frontend/packages/manager/src/components/ErrorsModal.jsx index 17b7055..e8af727 100644 --- a/frontend/packages/manager/src/components/ErrorsModal.jsx +++ b/frontend/packages/manager/src/components/ErrorsModal.jsx @@ -1,15 +1,16 @@ -import React, { useState, useRef, useContext } from 'react'; +import React, { useContext } from 'react'; import Button from '@splunk/react-ui/Button'; import Modal from '@splunk/react-ui/Modal'; import P from '@splunk/react-ui/Paragraph'; +import Message from "@splunk/react-ui/Message"; import ErrorsModalContext from "../store/errors-modal-contxt"; function ErrorsModal() { const ErrCtx = useContext(ErrorsModalContext); - const modalToggle = useRef(null); const handleRequestClose = () => { ErrCtx.setOpen(false); + ErrCtx.setErrorType("info"); ErrCtx.setMessage(""); }; @@ -17,7 +18,21 @@ function ErrorsModal() {
-

{ErrCtx.message}

+ { + ErrCtx.errorType === "info" ?

{ErrCtx.message}

: null + } + { + ErrCtx.errorType === "warning" ? + + {ErrCtx.message} + : null + } + { + ErrCtx.errorType === "error" ? + + {ErrCtx.message} + : null + }