diff --git a/README.rst b/README.rst index 49d8f458..2f127e24 100644 --- a/README.rst +++ b/README.rst @@ -105,4 +105,4 @@ Python Requirements | Python 2.7 or 3.5-7 supported, though other versions may work. *We will shortly stop supporting Python2* | Tested on AL2 and OSX 10.14.x - Windows automated testing not attempted -| Outside of the standard Python modules, we make use of lxml, DeepDiff and ruamel.yaml in processing, and Docker for demo/tests. +| Outside of the standard Python modules, we make use of lxml, DeepDiff, ruamel.yaml and xmltodict in processing, and Docker for demo/tests. diff --git a/docs/contributing.rst b/docs/contributing.rst index 3ff83ed5..4fc845b4 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -79,7 +79,15 @@ Ready to contribute? Here's how to set up `nipyapi` for local development. 5. You may want to leverage the provided Docker configuration for testing and development - Install the latest version of Docker - - Use the provided Docker Compose configuration in ./test_env_config/docker_compose_full_test + - Use the provided Docker Compose configuration in ./resources/docker/latest and run the tests:: + + $ cd resources/docker/latest + $ docker-compose up -d + $ cd ../../../ + $ tox + $ cd resources/docker/latest + $ docker-compose stop + 6. You may also want to interactively test your code leveraging the convenience console in the demo package:: diff --git a/nipyapi/templates.py b/nipyapi/templates.py index e949e25b..e408ccb3 100644 --- a/nipyapi/templates.py +++ b/nipyapi/templates.py @@ -5,10 +5,13 @@ """ from __future__ import absolute_import + +import json from os import access, R_OK, W_OK from os.path import isfile, dirname import logging import six +import xmltodict from lxml import etree import nipyapi @@ -17,7 +20,9 @@ __all__ = [ "list_all_templates", "get_template_by_name", "deploy_template", "upload_template", "create_pg_snippet", "create_template", - "delete_template", "export_template", 'get_template' + "delete_template", "export_template", 'get_template', + "load_template_from_xml_file_path", "load_template_from_xml_file_stream", + "load_template_from_xml_string" ] @@ -262,3 +267,60 @@ def list_all_templates(native=True): return templates.templates return None return templates + + +def load_template_from_xml_file_path(file_path): + """ + Loads a TemplateEntity from an xml file for a + given path + + + Args: + file_path (str): path to the xml file + + Returns: + TemplateEntity + """ + assert isfile(file_path) and access(file_path, R_OK), \ + SystemError("File {0} invalid or unreadable".format(file_path)) + with open(file_path, "r") as template_file: + return load_template_from_xml_file_stream(template_file) + + +def load_template_from_xml_file_stream(file_stream): + """ + Loads a TemplateEntity from a template xml file + + Args: + file_stream (io stream): the xml file stream as returned by open + + Returns: + TemplateEntity + """ + return load_template_from_xml_string(file_stream.read()) + + +def load_template_from_xml_string(xml_string): + """ + Loads a TemplateEntity from xml string, as if + you had read in the xml file to string + + Args: + xml_string (str): string of xml + + Returns: + TemplateEntity + """ + assert isinstance(xml_string, six.string_types) + + json_string = json.dumps(xmltodict.parse(xml_string)) + unset = False + if nipyapi.config.nifi_config.api_client is None: + unset = True + nipyapi.config.nifi_config.api_client = nipyapi.nifi.ApiClient() + + template_entity = nipyapi.utils.load(json_string, + ('nifi', 'TemplateEntity')) + if unset: + nipyapi.config.nifi_config.api_client = None + return template_entity diff --git a/requirements.txt b/requirements.txt index 574e6d9f..2c27a10f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,6 @@ ruamel.yaml==0.16.10 # Demo deployment automation docker>=2.5.1 + +# xml to json parsing +xmltodict>=0.12.0 diff --git a/tests/test_templates.py b/tests/test_templates.py index 3100e228..0b90f54d 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -171,3 +171,48 @@ def test_delete_template(regress_nifi, fix_templates): assert isinstance(r, nipyapi.nifi.TemplateEntity) with pytest.raises(ValueError): _ = nipyapi.templates.delete_template('invalid') + + +def test_load_template_from_file_path(fix_templates): + template_entity = nipyapi.templates.load_template_from_xml_file_path(fix_templates.c_file) + assert isinstance(template_entity, nipyapi.nifi.TemplateEntity) + # we should be able to DeepDiff these + # but the template _from_ NIFI does not have the inner + # snipit + # pg = fix_templates.pg.generate() + # _ = nipyapi.templates.upload_template( + # pg.id, + # fix_templates.c_file + # ) + # nifi_template = nipyapi.templates.get_template(fix_templates.c_name) + # assert nifi_template is not None + # assert isinstance(nifi_template, nipyapi.nifi.TemplateEntity) + # from deepdiff import DeepDiff + # diff_output = DeepDiff(template_entity, nifi_template, ignore_order=True) + # assert len(diff_output['type_changes']) == 6 + + +def test_load_template_from_file_path_bad_path(): + with pytest.raises(AssertionError): + nipyapi.templates.load_template_from_xml_file_path('nothing-to-see-here.nope') + with pytest.raises(TypeError): + nipyapi.templates.load_template_from_xml_file_path(None) + + +def test_load_template_from_xml_file(fix_templates): + with open(fix_templates.c_file, "r") as template_file: + template_entity = nipyapi.templates.load_template_from_xml_file_stream(template_file) + assert isinstance(template_entity, nipyapi.nifi.TemplateEntity) + + +def test_load_template_from_xml_string(fix_templates): + with open(fix_templates.c_file, "r") as template_file: + data = template_file.read() + template_entity = nipyapi.templates.load_template_from_xml_string(data) + assert isinstance(template_entity, nipyapi.nifi.TemplateEntity) + + +def test_load_template_from_xml_really_json_string(): + from pyexpat import ExpatError + with pytest.raises(ExpatError): + nipyapi.templates.load_template_from_xml_string("{'foo':'bar'}")