From 0d60a9439aa4d0b3e6ebcff9d5609bbfcd49de3e Mon Sep 17 00:00:00 2001 From: Alexander Sutcliffe Date: Mon, 7 Nov 2022 12:08:55 +0100 Subject: [PATCH 1/2] feat: add basic login config file type --- src/tm1filetools/files/text/other.py | 79 ++++++++++++++++++++++++ tests/conftest.py | 44 +++++++++++++ tests/test_files/test_text/test_other.py | 69 +++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 src/tm1filetools/files/text/other.py create mode 100644 tests/test_files/test_text/test_other.py diff --git a/src/tm1filetools/files/text/other.py b/src/tm1filetools/files/text/other.py new file mode 100644 index 0000000..80eb7a4 --- /dev/null +++ b/src/tm1filetools/files/text/other.py @@ -0,0 +1,79 @@ +import configparser +from pathlib import Path + +from .text import TM1TextFile + + +class TM1LoginCfgFile(TM1TextFile): + """ + A class representation of a config.in file often used in TM1py examples + + """ + + def __init__(self, path: Path, section: str): + + # list of valid params + + self._valid_params = [ + "address", + "port", + "user", + "password", + "ssl", + ] + + super().__init__(path) + + self.config = configparser.ConfigParser() + self.config.read(path, encoding=self.encoding) + + self._section = section + + self._params = {} + self._set_params() + + def get_parameter(self, param: str) -> str: + + return self.config.get(section=self._section, option=param, fallback=None) + + def set_parameter(self, param: str, value: str) -> None: + + # if we have a list of valid options, we could warn when an invalid option set + # do I need to care about the section in this file? + self.config[self._section][param] = value + + with open(self._path, "w") as f: + self.config.write(f) + + def is_valid(self): + # check file has the necessary sections and mandatory params + + return ( + self.config.has_section(self._section) + and self._params.get("address") is not None # noqa + and self._params.get("port") is not None # noqa + and self._params.get("user") is not None # noqa + and self._params.get("password") is not None # noqa + ) + + def _set_params(self): + + for p in self._valid_params: + self._params[p] = self.get_parameter(param=p) + + def get_login_kwargs(self): + """ + Return a dict object suitable for as keyword arguments to create a TM1py service object + """ + + # this is very naive for now and is really only meant for local testing with + # username/password authentication + # note it will return all valid params found + # it doesn't do much validation + + if self.is_valid(): + kwargs = {} + for p, v in self._params.items(): + if v is not None: + kwargs[p] = v + return kwargs diff --git a/tests/conftest.py b/tests/conftest.py index 07ee249..1b848e3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -226,3 +226,47 @@ def test_folder(tmp_path_factory): # return the path return d + + +@pytest.fixture(scope="function") +def login_config_folder(tmp_path_factory): + """ + Create a folder with with a sample login config file + """ + + d = tmp_path_factory.mktemp("login") + + f = d / "good_login.ini" + + cfg = r"""[local] + address = 192.168.0.111 + port = 18081 + user = admin + password = apple + """ + + f.write_text(cfg) + + f = d / "bad_login.ini" + + cfg = r"""[local] + address = 192.168.0.111 + user = admin + password = apple + """ + + f.write_text(cfg) + + f = d / "messy_login.ini" + + cfg = r"""[messy] + address = 192.168.0.111 + port = 18081 + user = admin + password = apple + irrelevant = koala + """ + + f.write_text(cfg) + + return d diff --git a/tests/test_files/test_text/test_other.py b/tests/test_files/test_text/test_other.py new file mode 100644 index 0000000..224ae09 --- /dev/null +++ b/tests/test_files/test_text/test_other.py @@ -0,0 +1,69 @@ +from pathlib import Path + +from tm1filetools.files.text.other import TM1LoginCfgFile + + +def test_read_and_write(test_folder): + + f = TM1LoginCfgFile(Path.joinpath(test_folder, "auth.cfg"), section="local") + + # need to create the section + f.config.add_section(f._section) + + param = "address" + + assert f.get_parameter(param) is None + + value = "192.168.0.111" + + f.set_parameter(param, value) + + assert f.get_parameter(param) == value + + # also re-open file to check it's been written to disk + + f2 = TM1LoginCfgFile(Path.joinpath(test_folder, "auth.cfg"), section="local") + + assert f2.get_parameter(param) == value + + +def test_is_valid(login_config_folder): + + f = TM1LoginCfgFile(Path.joinpath(login_config_folder, "good_login.ini"), section="local") + + assert f.is_valid() + + f = TM1LoginCfgFile(Path.joinpath(login_config_folder, "vad_login.ini"), section="local") + + assert not f.is_valid() + + +def test_get_kwargs(login_config_folder): + + f = TM1LoginCfgFile(Path.joinpath(login_config_folder, "good_login.ini"), section="local") + + login_kwargs = f.get_login_kwargs() + + assert login_kwargs["address"] == "192.168.0.111" + assert login_kwargs["port"] == "18081" + assert login_kwargs["user"] == "admin" + assert login_kwargs["password"] == "apple" + + assert login_kwargs.get("ssl") is None + assert login_kwargs.get("irrelevant") is None + + f = TM1LoginCfgFile(Path.joinpath(login_config_folder, "messy_login.ini"), section="messy") + + login_kwargs = f.get_login_kwargs() + + assert login_kwargs["address"] == "192.168.0.111" + assert login_kwargs["port"] == "18081" + assert login_kwargs["user"] == "admin" + assert login_kwargs["password"] == "apple" + + assert login_kwargs.get("ssl") is None + assert login_kwargs.get("irrelevant") is None + + f = TM1LoginCfgFile(Path.joinpath(login_config_folder, "bad_login.ini"), section="local") + + assert f.get_login_kwargs() is None From 31b60fb695ce8dab004d89493352d3e40c341344 Mon Sep 17 00:00:00 2001 From: Alexander Sutcliffe Date: Mon, 7 Nov 2022 12:09:23 +0100 Subject: [PATCH 2/2] chore: bump version --- docs/source/conf.py | 2 +- docs/source/history.rst | 6 ++++++ src/tm1filetools/__init__.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index e72ae09..f91404b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -9,7 +9,7 @@ project = "TM1 File Tools" copyright = "2022, Alexander Sutcliffe" author = "Alexander Sutcliffe" -release = "0.3.2" +release = "0.3.3" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/docs/source/history.rst b/docs/source/history.rst index e3d17a8..b473ac0 100644 --- a/docs/source/history.rst +++ b/docs/source/history.rst @@ -1,6 +1,12 @@ Release history =============== +Version 0.3.3 +------------- + +- Added experimental support for login credentials saved as config files +- Better docs + Version 0.3.2 ------------- diff --git a/src/tm1filetools/__init__.py b/src/tm1filetools/__init__.py index a45cd64..39a3d20 100644 --- a/src/tm1filetools/__init__.py +++ b/src/tm1filetools/__init__.py @@ -2,4 +2,4 @@ from .tools import TM1FileTool # noqa -__version__ = "0.3.2" # noqa +__version__ = "0.3.3" # noqa