-
Notifications
You must be signed in to change notification settings - Fork 19
WebDav support #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
WebDav support #51
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
5ce2fe2
ServerContext: add hostname, base_url props, webdav_path, webdav_clie…
labkey-alan 4b07dd1
Add unit tests for ServerContext
labkey-alan 16338a1
Add webdav docs
labkey-alan 065f6c0
Bump version, update CHANGE.txt
labkey-alan fcc1a40
Fix markdown urls
labkey-alan 3042928
Fix typo
labkey-alan b66a466
Update CHANGE.txt
labkey-alan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 [webdavclient3](https://github.com/ezhov-evgeny/webdav-client-python-3) 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 [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. | ||
|
|
||
|
|
||
| ### 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 two 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)) | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,23 +81,81 @@ def __init__( | |
| def __repr__(self): | ||
| return f"<ServerContext [ {self._domain} | {self._context_path} | {self._container_path} ]>" | ||
|
|
||
| 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: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. |
||
| 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 | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" | ||
| ) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is rad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's rare that his feature is useful, but it's powerful when used!