This is an automated email from the ASF dual-hosted git repository.

bossenti pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 42b5c9e17 feature: retrieve credentials from SP environment variables 
(#1640)
42b5c9e17 is described below

commit 42b5c9e17486eaddf2b71e66d3c1e3567d251796
Author: Tim <[email protected]>
AuthorDate: Wed May 31 20:12:58 2023 +0200

    feature: retrieve credentials from SP environment variables (#1640)
---
 .../docs/getting-started/quickstart.md             |  5 +-
 streampipes-client-python/docs/index.md            |  3 +-
 ...introduction-to-streampipes-python-client.ipynb | 18 ++---
 ...cting-data-from-the-streampipes-data-lake.ipynb |  6 +-
 ...ive-data-from-the-streampipes-data-stream.ipynb |  6 +-
 ...ine-learning-on-a-streampipes-data-stream.ipynb |  6 +-
 streampipes-client-python/setup.py                 |  3 +-
 .../streampipes/client/credential_provider.py      | 85 +++++++++++++++++++---
 .../tests/client/test_credential_provider.py       | 81 +++++++++++++++++++--
 9 files changed, 167 insertions(+), 46 deletions(-)

diff --git a/streampipes-client-python/docs/getting-started/quickstart.md 
b/streampipes-client-python/docs/getting-started/quickstart.md
index cef53c1f9..84b9e5a0c 100644
--- a/streampipes-client-python/docs/getting-started/quickstart.md
+++ b/streampipes-client-python/docs/getting-started/quickstart.md
@@ -66,10 +66,11 @@ Alternatively, you can provide your credentials via 
environment variables.
 Simply define your credential provider as follows:
 
 ```python
->>> from streampipes.client.credential_provider import 
StreamPipesApiKeyCredentials
+from streampipes.client.credential_provider import StreamPipesApiKeyCredentials
 
-StreamPipesApiKeyCredentials.from_env(username_env="USER", 
api_key_env="API-KEY")
+StreamPipesApiKeyCredentials()
 ```
+This requires to set the following environment variables: `SP_API_KEY` and 
`SP_USERNAME`
 <br>
 
 `username` is always the username that is used to log in into StreamPipes. <br>
diff --git a/streampipes-client-python/docs/index.md 
b/streampipes-client-python/docs/index.md
index 4ccf4a0c0..8caeb6759 100644
--- a/streampipes-client-python/docs/index.md
+++ b/streampipes-client-python/docs/index.md
@@ -89,8 +89,9 @@ Simply define your credential provider as follows:
 ```python
 from streampipes.client.credential_provider import StreamPipesApiKeyCredentials
 
-StreamPipesApiKeyCredentials.from_env(username_env="USER", 
api_key_env="API-KEY")
+StreamPipesApiKeyCredentials()
 ```
+This requires to set the following environment variables: `SP_API_KEY` and 
`SP_USERNAME`
 <br>
 
 `username` is always the username that is used to log in into StreamPipes. <br>
diff --git 
a/streampipes-client-python/docs/tutorials/1-introduction-to-streampipes-python-client.ipynb
 
b/streampipes-client-python/docs/tutorials/1-introduction-to-streampipes-python-client.ipynb
index cf5ff5cff..9ec18392c 100644
--- 
a/streampipes-client-python/docs/tutorials/1-introduction-to-streampipes-python-client.ipynb
+++ 
b/streampipes-client-python/docs/tutorials/1-introduction-to-streampipes-python-client.ipynb
@@ -149,7 +149,7 @@
    "execution_count": null,
    "outputs": [],
    "source": [
-    "StreamPipesApiKeyCredentials.from_env(username_env=\"USER\", 
api_key_env=\"API-KEY\")"
+    "StreamPipesApiKeyCredentials()"
    ],
    "metadata": {
     "collapsed": false
@@ -158,9 +158,8 @@
   {
    "cell_type": "markdown",
    "source": [
-    "Please note that you pass the names of the environment variables.\n",
-    "To ensure that the above code works, you must set the environment 
variables with the same name you specified in `from_env`.\n",
-    "In this scenario this would look like the following:"
+    "To ensure that the above code works, you must set the environment 
variables as expected.\n",
+    "This can be done like following:"
    ],
    "metadata": {
     "collapsed": false
@@ -171,8 +170,8 @@
    "execution_count": null,
    "outputs": [],
    "source": [
-    "%export USER=\"<USERNAME>\"\n",
-    "%export API_KEY=\"<API-KEY>\""
+    "%export SP_USERNAME=\"<USERNAME>\"\n",
+    "%export SP_API_KEY=\"<API-KEY>\""
    ],
    "metadata": {
     "collapsed": false
@@ -256,13 +255,6 @@
    "metadata": {
     "collapsed": false
    }
-  },
-  {
-   "cell_type": "markdown",
-   "source": [],
-   "metadata": {
-    "collapsed": false
-   }
   }
  ],
  "metadata": {
diff --git 
a/streampipes-client-python/docs/tutorials/2-extracting-data-from-the-streampipes-data-lake.ipynb
 
b/streampipes-client-python/docs/tutorials/2-extracting-data-from-the-streampipes-data-lake.ipynb
index 66a71f08b..000f6fa96 100644
--- 
a/streampipes-client-python/docs/tutorials/2-extracting-data-from-the-streampipes-data-lake.ipynb
+++ 
b/streampipes-client-python/docs/tutorials/2-extracting-data-from-the-streampipes-data-lake.ipynb
@@ -46,8 +46,8 @@
    "outputs": [],
    "source": [
     "import os\n",
-    "os.environ[\"USER\"] = \"[email protected]\"\n",
-    "os.environ[\"API-KEY\"] = \"XXX\"\n"
+    "os.environ[\"SP_USERNAME\"] = \"[email protected]\"\n",
+    "os.environ[\"SP_API_KEY\"] = \"XXX\"\n"
    ],
    "metadata": {
     "collapsed": false
@@ -59,7 +59,7 @@
    "outputs": [],
    "source": [
     "config = StreamPipesClientConfig(\n",
-    "    
credential_provider=StreamPipesApiKeyCredentials.from_env(username_env=\"USER\",
 api_key_env=\"API-KEY\"),\n",
+    "    credential_provider=StreamPipesApiKeyCredentials(),\n",
     "    host_address=\"localhost\",\n",
     "    https_disabled=True,\n",
     "    port=80\n",
diff --git 
a/streampipes-client-python/docs/tutorials/3-getting-live-data-from-the-streampipes-data-stream.ipynb
 
b/streampipes-client-python/docs/tutorials/3-getting-live-data-from-the-streampipes-data-stream.ipynb
index 5d2dcef89..5b206df5f 100644
--- 
a/streampipes-client-python/docs/tutorials/3-getting-live-data-from-the-streampipes-data-stream.ipynb
+++ 
b/streampipes-client-python/docs/tutorials/3-getting-live-data-from-the-streampipes-data-stream.ipynb
@@ -51,8 +51,8 @@
    "source": [
     "import os\n",
     "\n",
-    "os.environ[\"USER\"] = \"[email protected]\"\n",
-    "os.environ[\"API-KEY\"] = \"XXX\"\n",
+    "os.environ[\"SP_USERNAME\"] = \"[email protected]\"\n",
+    "os.environ[\"SP_API_KEY\"] = \"XXX\"\n",
     "\n",
     "# Use this if you work locally:\n",
     "os.environ[\"BROKER-HOST\"] = \"localhost\"  \n",
@@ -74,7 +74,7 @@
    ],
    "source": [
     "client_config = StreamPipesClientConfig(\n",
-    "    
credential_provider=StreamPipesApiKeyCredentials.from_env(username_env=\"USER\",
 api_key_env=\"API-KEY\"),\n",
+    "    credential_provider=StreamPipesApiKeyCredentials(),\n",
     "    host_address=\"localhost\",\n",
     "    port=80,\n",
     "    https_disabled=True,\n",
diff --git 
a/streampipes-client-python/docs/tutorials/4-using-online-machine-learning-on-a-streampipes-data-stream.ipynb
 
b/streampipes-client-python/docs/tutorials/4-using-online-machine-learning-on-a-streampipes-data-stream.ipynb
index 780464d3d..a797f041b 100644
--- 
a/streampipes-client-python/docs/tutorials/4-using-online-machine-learning-on-a-streampipes-data-stream.ipynb
+++ 
b/streampipes-client-python/docs/tutorials/4-using-online-machine-learning-on-a-streampipes-data-stream.ipynb
@@ -40,8 +40,8 @@
    "source": [
     "import os\n",
     "\n",
-    "os.environ[\"USER\"] = \"[email protected]\"\n",
-    "os.environ[\"API-KEY\"] = \"XXX\"\n",
+    "os.environ[\"SP_USERNAME\"] = \"[email protected]\"\n",
+    "os.environ[\"SP_API_KEY\"] = \"XXX\"\n",
     "\n",
     "# Use this if you work locally:\n",
     "os.environ[\"BROKER-HOST\"] = \"localhost\"  \n",
@@ -63,7 +63,7 @@
    ],
    "source": [
     "client_config = StreamPipesClientConfig(\n",
-    "    
credential_provider=StreamPipesApiKeyCredentials.from_env(username_env=\"USER\",
 api_key_env=\"API-KEY\"),\n",
+    "    credential_provider=StreamPipesApiKeyCredentials(),\n",
     "    host_address=\"localhost\",\n",
     "    port=80,\n",
     "    https_disabled=True,\n",
diff --git a/streampipes-client-python/setup.py 
b/streampipes-client-python/setup.py
index ac26f49e0..7825ca403 100644
--- a/streampipes-client-python/setup.py
+++ b/streampipes-client-python/setup.py
@@ -41,7 +41,8 @@ base_packages = [
     "pydantic>=1.10.2",
     "requests>=2.28.1",
     "nats-py>=2.2.0",
-    "confluent-kafka>=2.0.2"
+    "confluent-kafka>=2.0.2",
+    "typing-extensions>=4.5.0"
 ]
 
 dev_packages = base_packages + [
diff --git 
a/streampipes-client-python/streampipes/client/credential_provider.py 
b/streampipes-client-python/streampipes/client/credential_provider.py
index a29598c9f..461088e8f 100644
--- a/streampipes-client-python/streampipes/client/credential_provider.py
+++ b/streampipes-client-python/streampipes/client/credential_provider.py
@@ -32,6 +32,8 @@ __all__ = [
     "StreamPipesApiKeyCredentials",
 ]
 
+from typing_extensions import deprecated
+
 
 class CredentialProvider(ABC):
     """Abstract implementation of a credential provider.
@@ -81,12 +83,20 @@ class StreamPipesApiKeyCredentials(CredentialProvider):
 
     The required token can be generated via the StreamPipes UI (see the 
description on our [start-page](../../../).
 
+    Both parameters can either be passed as arguments or remain unset.
+    If they are not passed, they are retrieved from environment variables:
+
+    * `SP_USERNAME` is expected to contain the username
+    * `SP_API_KEY` is expected to contain the API key
+
     Parameters
     ----------
-    username: str
-        The username to which the API token is granted, e.g., 
`[email protected]`.
-    api_key: str
-        The StreamPipes API key as it is displayed in the UI.
+    username: Optional[str]
+        The username to which the API token is granted, e.g., 
`[email protected]`.<br>
+        If not passed, the username is retrieved from environment variable 
`SP_USERNAME`.
+    api_key: Optional[str]
+        The StreamPipes API key as it is displayed in the UI.<br>
+        If not passed, the api key is retrieved from environment variable 
`SP_API_KEY`
 
     Examples
     --------
@@ -94,9 +104,15 @@ class StreamPipesApiKeyCredentials(CredentialProvider):
 
     """
 
+    _ENV_KEY_API = "SP_API_KEY"
+    _ENV_KEY_USERNAME = "SP_USERNAME"
+
     @classmethod
+    @deprecated("deprecated since 0.93.0; please use the class constructor 
instead.")
     def from_env(cls, username_env: str, api_key_env: str) -> 
StreamPipesApiKeyCredentials:
-        """Returns an api key provider parameterized via environment variables.
+        """DEPRECATED - use the class constructor instead
+
+        Returns an api key provider parameterized via environment variables.
 
         Parameters
         ----------
@@ -123,7 +139,7 @@ class StreamPipesApiKeyCredentials(CredentialProvider):
             raise KeyError(
                 f"Ups, the following environment variables have not been 
found: "
                 f"{'`' + username_env + '`,' if username is None else ''}"
-                f"{'`' + api_key_env +'`' if api_key is None else ''}. "  # 
noqa: W291
+                f"{'`' + api_key_env + '`' if api_key is None else ''}. "  # 
noqa: W291
                 "Please check them to be properly set."
             )
 
@@ -131,11 +147,52 @@ class StreamPipesApiKeyCredentials(CredentialProvider):
 
     def __init__(
         self,
-        username: str,
-        api_key: str,
+        username: Optional[str] = None,
+        api_key: Optional[str] = None,
     ):
-        self.username = username
-        self.api_key = api_key
+        # if both parameters are passed we can add them directly to the 
instance
+        if all({username, api_key}):
+            self.username = username
+            self.api_key = api_key
+
+        # otherwise we need to check if environment variables are properly set
+        else:
+            retrieved_api_key = os.environ.get(self._ENV_KEY_API, None)
+            retrieved_user_name = os.environ.get(self._ENV_KEY_USERNAME, None)
+
+            self.username = retrieved_user_name
+            self.api_key = retrieved_api_key
+
+            if username:
+                self.username = username
+
+                if retrieved_api_key is None:
+                    raise AttributeError(
+                        "API key not found in the environment variables - 
please provide the API key "
+                        "via the parameter `api_key` or ensure that it is 
passed to "
+                        f"the environment variable `{self._ENV_KEY_API}`."
+                    )
+                self.api_key = retrieved_api_key
+
+            if api_key:
+                self.api_key = api_key
+
+                if retrieved_user_name is None:
+                    raise AttributeError(
+                        "Username not found - please provide the username "
+                        "via the parameter `username` or ensure that it is 
passed to "
+                        f"the environment variable `{self._ENV_KEY_USERNAME}`."
+                    )
+                self.username = retrieved_user_name
+
+            if not all({self.username, self.api_key}):
+                if None in {retrieved_user_name, retrieved_api_key}:
+                    raise AttributeError(
+                        "Both parameters not found - please provide both api 
key and username "
+                        "either via the parameters (`api_key` and `username`) 
or via "
+                        f"the corresponding environment variables 
(`{self._ENV_KEY_API}` and "
+                        f"`{self._ENV_KEY_USERNAME}`)."
+                    )
 
     @property
     def _authentication_headers(self) -> Dict[str, str]:
@@ -146,7 +203,11 @@ class StreamPipesApiKeyCredentials(CredentialProvider):
         Dictionary with authentication headers as string key-value pairs.
 
         """
+
+        user: str = self.username  # type: ignore  # mypy mistakenly detects 
this attribute as optional
+        token: str = self.api_key  # type: ignore  # mypy mistakenly detects 
this attribute as optional
+
         return {
-            "X-API-User": self.username,
-            "X-API-Key": self.api_key,
+            "X-API-User": user,
+            "X-API-Key": token,
         }
diff --git a/streampipes-client-python/tests/client/test_credential_provider.py 
b/streampipes-client-python/tests/client/test_credential_provider.py
index 23ed251e6..57a7b975c 100644
--- a/streampipes-client-python/tests/client/test_credential_provider.py
+++ b/streampipes-client-python/tests/client/test_credential_provider.py
@@ -21,6 +21,16 @@ from streampipes.client.credential_provider import 
StreamPipesApiKeyCredentials
 
 
 class TestStreamPipesApiKeyCredentials(TestCase):
+
+    @staticmethod
+    def _clear_envs():
+
+        if StreamPipesApiKeyCredentials._ENV_KEY_API in os.environ.keys():
+            del os.environ[StreamPipesApiKeyCredentials._ENV_KEY_API]
+
+        if StreamPipesApiKeyCredentials._ENV_KEY_USERNAME in os.environ.keys():
+            del os.environ[StreamPipesApiKeyCredentials._ENV_KEY_USERNAME]
+
     def test_api_key_credentials(self):
         credentials = StreamPipesApiKeyCredentials(username="test-user", 
api_key="test-key")
         result = credentials.make_headers()
@@ -34,14 +44,69 @@ class TestStreamPipesApiKeyCredentials(TestCase):
 
         self.assertDictEqual(expected_extended, result_extended)
 
-    def test_api_key_from_env(self):
-        os.environ["USER"] = "user"
-        os.environ["KEY"] = "api-key"
-        credentials = 
StreamPipesApiKeyCredentials.from_env(username_env="USER", api_key_env="KEY")
+    def test_pass_credentials(self):
+        credentials = StreamPipesApiKeyCredentials(username="username", 
api_key="api-key")
+
+        self.assertEqual("username", credentials.username)
+        self.assertEqual("api-key", credentials.api_key)
+
+    def test_pass_credentials_envs_set(self):
+
+        os.environ[StreamPipesApiKeyCredentials._ENV_KEY_API] = 
"another-api-key"
+        os.environ[StreamPipesApiKeyCredentials._ENV_KEY_USERNAME] = 
"another-user-name"
+
+        credentials = StreamPipesApiKeyCredentials(username="username", 
api_key="api-key")
+
+        self.assertEqual("username", credentials.username)
+        self.assertEqual("api-key", credentials.api_key)
+
+    def test_pass_username(self):
+
+        self._clear_envs()
+        os.environ[StreamPipesApiKeyCredentials._ENV_KEY_API] = 
"another-api-key"
+
+        credentials = StreamPipesApiKeyCredentials(username="username")
+
+        self.assertEqual("username", credentials.username)
+        self.assertEqual("another-api-key", credentials.api_key)
 
-        self.assertEqual("user", credentials.username)
+    def test_pass_username_api_key_not_set(self):
+
+        self._clear_envs()
+
+        with self.assertRaises(AttributeError):
+            StreamPipesApiKeyCredentials(username="username")
+
+    def test_pass_api_key(self):
+
+        self._clear_envs()
+        os.environ[StreamPipesApiKeyCredentials._ENV_KEY_USERNAME] = 
"another-username"
+
+        credentials = StreamPipesApiKeyCredentials(api_key="api-key")
+
+        self.assertEqual("another-username", credentials.username)
         self.assertEqual("api-key", credentials.api_key)
 
-    def test_api_key_from_env_not_set(self):
-        with self.assertRaises(KeyError):
-            StreamPipesApiKeyCredentials.from_env(username_env="test", 
api_key_env="key")
+    def test_pass_api_key_username_not_set(self):
+
+        self._clear_envs()
+
+        with self.assertRaises(AttributeError):
+            StreamPipesApiKeyCredentials(api_key="api-key")
+
+    def test_nothing_set(self):
+
+        self._clear_envs()
+
+        with self.assertRaises(AttributeError):
+            StreamPipesApiKeyCredentials()
+
+    def test_all_from_envs(self):
+
+        os.environ[StreamPipesApiKeyCredentials._ENV_KEY_API] = 
"another-api-key"
+        os.environ[StreamPipesApiKeyCredentials._ENV_KEY_USERNAME] = 
"another-username"
+
+        credentials = StreamPipesApiKeyCredentials()
+
+        self.assertEqual("another-username", credentials.username)
+        self.assertEqual("another-api-key", credentials.api_key)

Reply via email to