From 5ce2fe259d25b7e8f187d775c6d347a558b5bd8a Mon Sep 17 00:00:00 2001 From: Alan Vezina Date: Tue, 28 Jun 2022 10:21:12 -0500 Subject: [PATCH 1/7] ServerContext: add hostname, base_url props, webdav_path, webdav_client methods --- labkey/server_context.py | 72 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/labkey/server_context.py b/labkey/server_context.py index 74ed390..9cf351d 100644 --- a/labkey/server_context.py +++ b/labkey/server_context.py @@ -81,23 +81,81 @@ def __init__( def __repr__(self): return f"" - def build_url(self, controller: str, action: str, container_path: str = None) -> str: - sep = "/" + @property + def hostname(self) -> str: + return self._scheme + self._domain - url = self._scheme + self._domain + @property + def base_url(self) -> str: + base_url = self.hostname if self._context_path is not None: - url += sep + self._context_path + base_url += "/" + self._context_path + + return base_url + + def build_url(self, controller: str, action: str, container_path: str = None) -> str: + url = self.base_url if container_path is not None: - url += sep + container_path + url += "/" + container_path elif self._container_path is not None: - url += sep + self._container_path + url += "/" + self._container_path - url += sep + controller + "-" + action + url += "/" + controller + "-" + action return url + def webdav_path(self, container_path: str = None, file_name: str = None): + path = "/_webdav" + container_path = container_path or self._container_path + + if container_path is not None: + if container_path.endswith("/"): + # trim the slash + container_path = container_path[0:-1] + + if not container_path.startswith("/"): + path += "/" + + path += container_path + + path += "/@files" + + if file_name is not None: + if not file_name.startswith("/"): + path += "/" + + path += file_name + + return path + + def webdav_client(self, webdav_options: dict = None): + # We localize the import of webdav3 here so it is an optional dependency. Only users who want to use webdav will + # need to pip install webdavclient3 + from webdav3.client import Client + + options = { + "webdav_hostname": self.base_url, + } + + if self._api_key is not None: + options["webdav_login"] = "apikey" + options["webdav_password"] = f"apikey|{self._api_key}" + + if webdav_options is not None: + options = { + **options, + **webdav_options, + } + + client = Client(options) + + if self._verify_ssl is False: + client.verify = False # Set verify to false if using localhost without HTTPS + + return client + def handle_request_exception(self, exception): if type(exception) in [RequestAuthorizationError, QueryNotFoundError, ServerNotFoundError]: raise exception From 4b07dd1576f403ec3423912de1e3e60bfe78cef2 Mon Sep 17 00:00:00 2001 From: Alan Vezina Date: Tue, 28 Jun 2022 10:21:28 -0500 Subject: [PATCH 2/7] Add unit tests for ServerContext --- test/unit/test_server_context.py | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 test/unit/test_server_context.py diff --git a/test/unit/test_server_context.py b/test/unit/test_server_context.py new file mode 100644 index 0000000..dc26b60 --- /dev/null +++ b/test/unit/test_server_context.py @@ -0,0 +1,46 @@ +from labkey.server_context import ServerContext +import pytest + + +@pytest.fixture(scope="session") +def server_context(): + return ServerContext("example.com", "test_container", "test_context_path") + + +@pytest.fixture(scope="session") +def server_context_no_context_path(): + return ServerContext("example.com", "test_container") + + +@pytest.fixture(scope="session") +def server_context_no_ssl(): + return ServerContext("example.com", "test_container", "test_context_path", use_ssl=False) + + +def test_base_url(server_context, server_context_no_context_path, server_context_no_ssl): + assert server_context.base_url == "https://example.com/test_context_path" + assert server_context_no_context_path.base_url == "https://example.com" + assert server_context_no_ssl.base_url == "http://example.com/test_context_path" + + +def test_build_url(server_context): + assert ( + server_context.build_url("query", "getQuery.api") + == "https://example.com/test_context_path/test_container/query-getQuery.api" + ) + assert ( + server_context.build_url("query", "getQuery.api", "different_container") + == "https://example.com/test_context_path/different_container/query-getQuery.api" + ) + + +def test_webdav_path(server_context, server_context_no_context_path, server_context_no_ssl): + assert server_context.webdav_path() == "/_webdav/test_container/@files" + assert ( + server_context.webdav_path(file_name="test.jpg") + == "/_webdav/test_container/@files/test.jpg" + ) + assert ( + server_context.webdav_path("my_container/with_subfolder", "data.txt") + == "/_webdav/my_container/with_subfolder/@files/data.txt" + ) From 16338a160d94702712c241ab1b02f1e0937695c9 Mon Sep 17 00:00:00 2001 From: Alan Vezina Date: Tue, 28 Jun 2022 10:49:11 -0500 Subject: [PATCH 3/7] Add webdav docs --- README.md | 2 ++ docs/webdav.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 docs/webdav.md diff --git a/README.md b/README.md index a7be225..008dc93 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,8 @@ else: print('select_rows: Failed to load results from ' + schema + '.' + table) ``` +Documentation and example code for working with WebDav can be found [docs/webdav.md](here). + ## Supported Versions Python 3.7+ is fully supported. LabKey Server v15.1 and later. diff --git a/docs/webdav.md b/docs/webdav.md new file mode 100644 index 0000000..91d7649 --- /dev/null +++ b/docs/webdav.md @@ -0,0 +1,52 @@ +# WebDav Support + +Our Python API includes some convenience methods for creating "webdavclient3" clients, and building webdav file paths. + +### Creating a WebDav client +First, make sure you have the [https://github.com/ezhov-evgeny/webdav-client-python-3](webdavclient3) library installed: + +```bash +$ pip install webdavclient3 +``` + +Then you can use your `APIWrapper` to create a client: + +```python +from labkey.api_wrapper import APIWrapper + +domain = "localhost:8080" +container = "MyContainer" +api = APIWrapper(domain, container) +webdav_client = api.server_context.webdav_client() +``` + +The `webdav_client` method has a single optional argument, `webdav_options`, a dict that you can use to pass any options +that you would pass to the [https://github.com/ezhov-evgeny/webdav-client-python-3#webdav-api](webdavclient3) library. +If you are using API Key authentication with your APIWrapper we will automatically configure the WebDav Client to use +API Key authentication with your API Key. If you are using a `.netrc` file for authentication it should automatically +detect your `.netrc` file and authenticate using those credentials. + + +### The webdav_path utility method +If you are using the `webdavclient3` library you'll still need to know the appropriate WebDav path in order to access +your files. We provide a utility method, `webdav_path` to make it easier to construct LabKey WebDav paths. The method +takes to keyword arguments, `container_path`, and `file_name`. + +```python +from labkey.api_wrapper import APIWrapper + +domain = "localhost:8080" +container = "MyContainer" +api = APIWrapper(domain, container) +webdav_client = api.server_context.webdav_client() + +# Constructs a webdav path to "MyContainer" +path = api.server_context.webdav_path() +print(webdav_client.info(path)) +# Constructs a webdav path to the "data.txt" file in "MyContainer" +path = api.server_context.webdav_path(file_name='data.txt') +print(webdav_client.info(path)) +# Constructs a webdav path to the "data.txt" file in "other_container" +path = api.server_context.webdav_path(container_path="other_container", file_name="data.txt") +print(webdav_client.info(path)) +``` From 065f6c0bb06b8defabecbc294f169ccfdd78b29e Mon Sep 17 00:00:00 2001 From: Alan Vezina Date: Tue, 28 Jun 2022 10:56:40 -0500 Subject: [PATCH 4/7] Bump version, update CHANGE.txt --- CHANGE.txt | 13 +++++++++++++ labkey/__init__.py | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGE.txt b/CHANGE.txt index bdcb670..7dff55b 100644 --- a/CHANGE.txt +++ b/CHANGE.txt @@ -1,9 +1,22 @@ +++++++++++ LabKey Python Client API News +++++++++++ +What's New in the LabKey 2.3.0 package +============================== + +*Release date: 06/28/2022* +- Add "hostname" property to ServerContext +- Add "base_url" property to ServerContext +- Add "webdav_client" method to ServerContext + - This method returns a webdavclient3 Client instance +- Add "webdav_path" method to ServerContext +- Add docs for WebDav support +- Add unit tests for ServerContext What's New in the LabKey 2.2.0 package ============================== + +*Release date: 08/11/2021* - Add `domain.get_domain_details` API to domain module. - Support saving domain options via `domain.save`. - Fix `ConditionalFormat.to_json()` to match server response. diff --git a/labkey/__init__.py b/labkey/__init__.py index 3929445..d848eac 100644 --- a/labkey/__init__.py +++ b/labkey/__init__.py @@ -16,6 +16,6 @@ from labkey import domain, query, experiment, security, utils __title__ = "labkey" -__version__ = "2.2.0" +__version__ = "2.3.0" __author__ = "LabKey" __license__ = "Apache License 2.0" From fcc1a407fcb9183e0dbb430293a8519f8e725c1e Mon Sep 17 00:00:00 2001 From: Alan Vezina Date: Tue, 28 Jun 2022 10:59:54 -0500 Subject: [PATCH 5/7] Fix markdown urls --- README.md | 6 ++++-- docs/webdav.md | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 008dc93..5e9fa4d 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,10 @@ Security API - [sample code](samples/security_example.py) - Available for administrating and configuring user accounts and permissions. +WebDav + +- Documentation and example code can be found [here](docs/webdav.md). + ## Installation To install, simply use `pip`: @@ -99,8 +103,6 @@ else: print('select_rows: Failed to load results from ' + schema + '.' + table) ``` -Documentation and example code for working with WebDav can be found [docs/webdav.md](here). - ## Supported Versions Python 3.7+ is fully supported. LabKey Server v15.1 and later. diff --git a/docs/webdav.md b/docs/webdav.md index 91d7649..e59313c 100644 --- a/docs/webdav.md +++ b/docs/webdav.md @@ -3,7 +3,7 @@ Our Python API includes some convenience methods for creating "webdavclient3" clients, and building webdav file paths. ### Creating a WebDav client -First, make sure you have the [https://github.com/ezhov-evgeny/webdav-client-python-3](webdavclient3) library installed: +First, make sure you have the [webdavclient3](https://github.com/ezhov-evgeny/webdav-client-python-3) library installed: ```bash $ pip install webdavclient3 @@ -21,7 +21,7 @@ webdav_client = api.server_context.webdav_client() ``` The `webdav_client` method has a single optional argument, `webdav_options`, a dict that you can use to pass any options -that you would pass to the [https://github.com/ezhov-evgeny/webdav-client-python-3#webdav-api](webdavclient3) library. +that you would pass to the [webdavclient3](https://github.com/ezhov-evgeny/webdav-client-python-3#webdav-api) library. If you are using API Key authentication with your APIWrapper we will automatically configure the WebDav Client to use API Key authentication with your API Key. If you are using a `.netrc` file for authentication it should automatically detect your `.netrc` file and authenticate using those credentials. From 30429287607392375b1d29b5c3d01471fa999e71 Mon Sep 17 00:00:00 2001 From: Alan Vezina Date: Tue, 28 Jun 2022 11:01:22 -0500 Subject: [PATCH 6/7] Fix typo --- docs/webdav.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/webdav.md b/docs/webdav.md index e59313c..5a3c3ce 100644 --- a/docs/webdav.md +++ b/docs/webdav.md @@ -30,7 +30,7 @@ detect your `.netrc` file and authenticate using those credentials. ### The webdav_path utility method If you are using the `webdavclient3` library you'll still need to know the appropriate WebDav path in order to access your files. We provide a utility method, `webdav_path` to make it easier to construct LabKey WebDav paths. The method -takes to keyword arguments, `container_path`, and `file_name`. +takes two keyword arguments, `container_path`, and `file_name`. ```python from labkey.api_wrapper import APIWrapper From b66a46684779d4bd7a5a0ad913873773c71da083 Mon Sep 17 00:00:00 2001 From: Alan Vezina Date: Thu, 30 Jun 2022 17:39:23 -0500 Subject: [PATCH 7/7] Update CHANGE.txt --- CHANGE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGE.txt b/CHANGE.txt index 7dff55b..b7ef8ef 100644 --- a/CHANGE.txt +++ b/CHANGE.txt @@ -4,7 +4,7 @@ LabKey Python Client API News What's New in the LabKey 2.3.0 package ============================== -*Release date: 06/28/2022* +*Release date: 06/30/2022* - Add "hostname" property to ServerContext - Add "base_url" property to ServerContext - Add "webdav_client" method to ServerContext