From 334971158e038b9a26e30ba75bb5e264acad88ba Mon Sep 17 00:00:00 2001 From: "Lars Espen Strand Nordhus (Innleid)" Date: Mon, 18 Mar 2024 23:31:16 +0100 Subject: [PATCH 1/6] Added support for PAT in API constructor --- dhis2/api.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dhis2/api.py b/dhis2/api.py index 29e8354..bca681e 100644 --- a/dhis2/api.py +++ b/dhis2/api.py @@ -36,7 +36,8 @@ def __init__( self, server: str, username: str, - password: str, + password: str = None, + api_access_token: str = None, api_version: Union[int, str] = None, user_agent: str = None, ) -> None: @@ -45,6 +46,7 @@ def __init__( :param server: baseurl, e.g. 'play.dhis2.org/demo' :param username: DHIS2 username :param password: DHIS2 password + :param password: DHIS2 password :param api_version: optional, creates a url like /api/29/schemas :param user_agent: optional, add user-agent to header. otherwise it uses requests' user-agent. """ @@ -62,7 +64,12 @@ def __init__( self.session = requests.Session() self.username = username - self.session.auth = (self.username, password) + if password is not None: + self.session.auth = (self.username, password) + elif api_access_token is not None: + self.session.headers["Authorization"] = f"ApiToken {api_access_token}" + else: + raise ClientException("You need to define password or api_access_token in API construction.") if user_agent: self.session.headers["user-agent"] = user_agent From b607b9a70142db1516eb0d3c161349ac40c73cc8 Mon Sep 17 00:00:00 2001 From: "Lars Espen Strand Nordhus (Innleid)" Date: Mon, 18 Mar 2024 23:41:19 +0100 Subject: [PATCH 2/6] Added PAT support --- dhis2/api.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/dhis2/api.py b/dhis2/api.py index 29e8354..4d6f056 100644 --- a/dhis2/api.py +++ b/dhis2/api.py @@ -36,15 +36,17 @@ def __init__( self, server: str, username: str, - password: str, - api_version: Union[int, str] = None, - user_agent: str = None, + password: str = None, + api_access_token: str = None, + api_version: Union[int, str] = None, + user_agent: str = None, ) -> None: """ :param server: baseurl, e.g. 'play.dhis2.org/demo' :param username: DHIS2 username :param password: DHIS2 password + :param api_access_token: DHIS2 private api token :param api_version: optional, creates a url like /api/29/schemas :param user_agent: optional, add user-agent to header. otherwise it uses requests' user-agent. """ @@ -62,7 +64,12 @@ def __init__( self.session = requests.Session() self.username = username - self.session.auth = (self.username, password) + if password is not None: + self.session.auth = (self.username, password) + elif api_access_token is not None: + self.session.headers["Authorization"] = f"ApiToken {api_access_token}" + else: + raise ClientException("You need to define password or api_access_token in API construction.") if user_agent: self.session.headers["user-agent"] = user_agent From d73d21499d187f3ac0c68bd65b982f0ef99df333 Mon Sep 17 00:00:00 2001 From: "Lars Espen Strand Nordhus (Innleid)" Date: Mon, 18 Mar 2024 23:45:42 +0100 Subject: [PATCH 3/6] Added PAT support --- dhis2/api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dhis2/api.py b/dhis2/api.py index 4d6f056..0cfe8cb 100644 --- a/dhis2/api.py +++ b/dhis2/api.py @@ -36,10 +36,10 @@ def __init__( self, server: str, username: str, - password: str = None, - api_access_token: str = None, - api_version: Union[int, str] = None, - user_agent: str = None, + password: str = None, + api_access_token: str = None, + api_version: Union[int, str] = None, + user_agent: str = None, ) -> None: """ From 3f203de1c8ba066a9619a5f052a0e9a4176fb3a9 Mon Sep 17 00:00:00 2001 From: "Lars Espen Strand Nordhus (Innleid)" Date: Wed, 26 Jun 2024 11:37:50 +0200 Subject: [PATCH 4/6] Added support for PAT in API constructor --- dhis2/api.py | 12 +++++++++--- examples/1_update_dataelements.py | 3 ++- setup.py | 4 ++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/dhis2/api.py b/dhis2/api.py index bca681e..999689c 100644 --- a/dhis2/api.py +++ b/dhis2/api.py @@ -35,18 +35,19 @@ class Api(object): def __init__( self, server: str, - username: str, + username: str = None, password: str = None, api_access_token: str = None, api_version: Union[int, str] = None, user_agent: str = None, + validate: bool = False ) -> None: """ :param server: baseurl, e.g. 'play.dhis2.org/demo' :param username: DHIS2 username :param password: DHIS2 password - :param password: DHIS2 password + :param api_access_token: DHIS2 private api token :param api_version: optional, creates a url like /api/29/schemas :param user_agent: optional, add user-agent to header. otherwise it uses requests' user-agent. """ @@ -63,15 +64,20 @@ def __init__( self.api_version = api_version self.session = requests.Session() - self.username = username if password is not None: + self.username = username self.session.auth = (self.username, password) elif api_access_token is not None: self.session.headers["Authorization"] = f"ApiToken {api_access_token}" + self.username = "" else: raise ClientException("You need to define password or api_access_token in API construction.") if user_agent: self.session.headers["user-agent"] = user_agent + if validate: + self.username = self.get("me", params=[("fields","username")]).json()['username'] + print(self.username) + def get_base_url(self) -> Optional[str]: return self._base_url diff --git a/examples/1_update_dataelements.py b/examples/1_update_dataelements.py index a112da6..687d639 100644 --- a/examples/1_update_dataelements.py +++ b/examples/1_update_dataelements.py @@ -7,7 +7,8 @@ """ # Create a Api object -api = Api("play.dhis2.org/dev", "admin", "district") +api = Api("play.im.dhis2.org/dev", "admin", "district") +# api = Api("play.im.dhis2.org/dev", api_access_token="d2p_Y0WIFJZx9AFISuUS1EC8tCOlqUZy5S6QRwEiZZeFh3P33rl1wa", validate=True) # setup the logger setup_logger() diff --git a/setup.py b/setup.py index b44239d..6b95df1 100644 --- a/setup.py +++ b/setup.py @@ -71,7 +71,7 @@ def run(self): install_requires=requirements, license=about["__license__"], zip_safe=False, - classifiers=( # https://pypi.org/pypi?%3Aaction=list_classifiers + classifiers=[ # https://pypi.org/pypi?%3Aaction=list_classifiers "License :: OSI Approved :: MIT License", "Intended Audience :: Developers", "Development Status :: 5 - Production/Stable", @@ -80,6 +80,6 @@ def run(self): "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", - ), + ], cmdclass={"publish": PublishCommand}, ) From c2f7b3be371cafd0cd6687af4ea9423a7bdb3a15 Mon Sep 17 00:00:00 2001 From: August Mathisen Date: Mon, 8 Jul 2024 11:16:13 +0200 Subject: [PATCH 5/6] support for accepting self signed certificated added --- dhis2/api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dhis2/api.py b/dhis2/api.py index 999689c..6eec96b 100644 --- a/dhis2/api.py +++ b/dhis2/api.py @@ -40,7 +40,8 @@ def __init__( api_access_token: str = None, api_version: Union[int, str] = None, user_agent: str = None, - validate: bool = False + validate: bool = False, + deny_self_signed_certificate: bool = True ) -> None: """ @@ -64,6 +65,7 @@ def __init__( self.api_version = api_version self.session = requests.Session() + self.session.verify = deny_self_signed_certificate if password is not None: self.username = username self.session.auth = (self.username, password) From c6187cc43cb69ce761d9ef6994f085c9ac51f10f Mon Sep 17 00:00:00 2001 From: August Mathisen Date: Tue, 28 Jan 2025 14:08:53 +0100 Subject: [PATCH 6/6] fixed bug for patch requests --- dhis2/api.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dhis2/api.py b/dhis2/api.py index 6eec96b..f3a3326 100644 --- a/dhis2/api.py +++ b/dhis2/api.py @@ -251,9 +251,9 @@ def _validate_request( [isinstance(elem, tuple) for elem in params] ): raise ClientException("`params` list must all be tuples") - if data and not isinstance(data, dict): + if data and not isinstance(data, (dict, list)): raise ClientException( - "`data` must be a dict, not {}".format(data.__class__.__name__) # type: ignore + "`data` must be a dict or list, not {}".format(data.__class__.__name__) # type: ignore ) def _make_request( @@ -290,7 +290,11 @@ def _make_request( r = self.session.put(url=url, json=data, params=params, timeout=timeout) elif method == "patch": - r = self.session.patch(url=url, json=data, params=params, timeout=timeout) + self.session.headers["Content-Type"] = "application/json-patch+json" + try: + r = self.session.patch(url=url, json=data, params=params, timeout=timeout) + finally: + del self.session.headers["Content-Type"] elif method == "delete": r = self.session.delete(url=url, params=params, timeout=timeout)