nc-py-api 0.14.0__tar.gz → 0.15.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/CHANGELOG.md +18 -1
  2. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/PKG-INFO +6 -6
  3. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/README.md +5 -5
  4. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/_session.py +5 -3
  5. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/_version.py +1 -1
  6. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/apps.py +2 -2
  7. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/integration_fastapi.py +17 -7
  8. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/misc.py +1 -1
  9. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/providers/task_processing.py +11 -3
  10. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/files/__init__.py +1 -3
  11. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/nextcloud.py +11 -4
  12. nc_py_api-0.15.1/nc_py_api/webhooks.py +210 -0
  13. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/pyproject.toml +1 -1
  14. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/.gitignore +0 -0
  15. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/AUTHORS +0 -0
  16. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/LICENSE.txt +0 -0
  17. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/__init__.py +0 -0
  18. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/_deffered_error.py +0 -0
  19. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/_exceptions.py +0 -0
  20. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/_misc.py +0 -0
  21. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/_preferences.py +0 -0
  22. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/_preferences_ex.py +0 -0
  23. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/_talk_api.py +0 -0
  24. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/_theming.py +0 -0
  25. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/activity.py +0 -0
  26. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/calendar.py +0 -0
  27. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/__init__.py +0 -0
  28. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/defs.py +0 -0
  29. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/events_listener.py +0 -0
  30. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/occ_commands.py +0 -0
  31. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/persist_transformers_cache.py +0 -0
  32. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/providers/__init__.py +0 -0
  33. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/providers/providers.py +0 -0
  34. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/providers/speech_to_text.py +0 -0
  35. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/providers/text_processing.py +0 -0
  36. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/providers/translations.py +0 -0
  37. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/ui/__init__.py +0 -0
  38. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/ui/files_actions.py +0 -0
  39. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/ui/resources.py +0 -0
  40. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/ui/settings.py +0 -0
  41. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/ui/top_menu.py +0 -0
  42. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/ui/ui.py +0 -0
  43. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/ex_app/uvicorn_fastapi.py +0 -0
  44. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/files/_files.py +0 -0
  45. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/files/files.py +0 -0
  46. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/files/files_async.py +0 -0
  47. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/files/sharing.py +0 -0
  48. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/loginflow_v2.py +0 -0
  49. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/notes.py +0 -0
  50. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/notifications.py +0 -0
  51. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/options.py +0 -0
  52. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/talk.py +0 -0
  53. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/talk_bot.py +0 -0
  54. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/user_status.py +0 -0
  55. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/users.py +0 -0
  56. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/users_groups.py +0 -0
  57. {nc_py_api-0.14.0 → nc_py_api-0.15.1}/nc_py_api/weather_status.py +0 -0
@@ -2,7 +2,24 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [0.14.0 - 2024-07-0X]
5
+ ## [0.15.1 - 2024-07-30]
6
+
7
+ ### Fixed
8
+
9
+ - Corrected behaviour of `ocs` function for `Group Folders` app routes(they are not fully OCS API). #279
10
+ - NextcloudApp: `get_computation_device` function now correctly returns result in upper_case. #278
11
+
12
+ ## [0.15.0 - 2024-07-19]
13
+
14
+ ### Added
15
+
16
+ - Initial Webhooks API support for the upcoming Nextcloud 30. #272
17
+
18
+ ### Changed
19
+
20
+ - NextcloudApp: `fetch_models_task` function now saves paths to downloaded models. #274 Thanks to @kyteinsky
21
+
22
+ ## [0.14.0 - 2024-07-09]
6
23
 
7
24
  ### Added
8
25
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nc-py-api
3
- Version: 0.14.0
3
+ Version: 0.15.1
4
4
  Summary: Nextcloud Python Framework
5
5
  Project-URL: Changelog, https://github.com/cloud-py-api/nc_py_api/blob/main/CHANGELOG.md
6
6
  Project-URL: Documentation, https://cloud-py-api.github.io/nc_py_api/
@@ -85,7 +85,7 @@ Description-Content-Type: text/markdown
85
85
  [![Docs](https://github.com/cloud-py-api/nc_py_api/actions/workflows/docs.yml/badge.svg)](https://cloud-py-api.github.io/nc_py_api/)
86
86
  [![codecov](https://codecov.io/github/cloud-py-api/nc_py_api/branch/main/graph/badge.svg?token=C91PL3FYDQ)](https://codecov.io/github/cloud-py-api/nc_py_api)
87
87
 
88
- ![NextcloudVersion](https://img.shields.io/badge/Nextcloud-26%20%7C%2027%20%7C%2028-blue)
88
+ ![NextcloudVersion](https://img.shields.io/badge/Nextcloud-27%20%7C%2028%20%7C%2029%20%7C%2030-blue)
89
89
  ![PythonVersion](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)
90
90
  ![impl](https://img.shields.io/pypi/implementation/nc_py_api)
91
91
  ![pypi](https://img.shields.io/pypi/v/nc_py_api.svg)
@@ -101,7 +101,7 @@ Python library that provides a robust and well-documented API that allows develo
101
101
  * **Sync + Async**: Provides both sync and async APIs.
102
102
 
103
103
  ### Capabilities
104
- | **_Capability_** | Nextcloud 26 | Nextcloud 27 | Nextcloud 28 | Nextcloud 29 |
104
+ | **_Capability_** | Nextcloud 27 | Nextcloud 28 | Nextcloud 29 | Nextcloud 30 |
105
105
  |-----------------------|:------------:|:------------:|:------------:|:------------:|
106
106
  | Calendar | ✅ | ✅ | ✅ | ✅ |
107
107
  | File System & Tags | ✅ | ✅ | ✅ | ✅ |
@@ -111,9 +111,9 @@ Python library that provides a robust and well-documented API that allows develo
111
111
  | Users & Groups | ✅ | ✅ | ✅ | ✅ |
112
112
  | User & Weather status | ✅ | ✅ | ✅ | ✅ |
113
113
  | Other APIs*** | ✅ | ✅ | ✅ | ✅ |
114
- | Talk Bot API* | N/A | ✅ | ✅ | ✅ |
115
- | Settings UI API* | N/A | N/A | N/A | ✅ |
116
- | AI Providers API** | N/A | N/A | N/A | ✅ |
114
+ | Talk Bot API* | | ✅ | ✅ | ✅ |
115
+ | Settings UI API* | N/A | N/A | | ✅ |
116
+ | AI Providers API** | N/A | N/A | | ✅ |
117
117
 
118
118
  &ast;_available only for **NextcloudApp**_<br>
119
119
  &ast;&ast;_available only for **NextcloudApp**: SpeechToText, TextProcessing, Translation_<br>
@@ -8,7 +8,7 @@
8
8
  [![Docs](https://github.com/cloud-py-api/nc_py_api/actions/workflows/docs.yml/badge.svg)](https://cloud-py-api.github.io/nc_py_api/)
9
9
  [![codecov](https://codecov.io/github/cloud-py-api/nc_py_api/branch/main/graph/badge.svg?token=C91PL3FYDQ)](https://codecov.io/github/cloud-py-api/nc_py_api)
10
10
 
11
- ![NextcloudVersion](https://img.shields.io/badge/Nextcloud-26%20%7C%2027%20%7C%2028-blue)
11
+ ![NextcloudVersion](https://img.shields.io/badge/Nextcloud-27%20%7C%2028%20%7C%2029%20%7C%2030-blue)
12
12
  ![PythonVersion](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)
13
13
  ![impl](https://img.shields.io/pypi/implementation/nc_py_api)
14
14
  ![pypi](https://img.shields.io/pypi/v/nc_py_api.svg)
@@ -24,7 +24,7 @@ Python library that provides a robust and well-documented API that allows develo
24
24
  * **Sync + Async**: Provides both sync and async APIs.
25
25
 
26
26
  ### Capabilities
27
- | **_Capability_** | Nextcloud 26 | Nextcloud 27 | Nextcloud 28 | Nextcloud 29 |
27
+ | **_Capability_** | Nextcloud 27 | Nextcloud 28 | Nextcloud 29 | Nextcloud 30 |
28
28
  |-----------------------|:------------:|:------------:|:------------:|:------------:|
29
29
  | Calendar | ✅ | ✅ | ✅ | ✅ |
30
30
  | File System & Tags | ✅ | ✅ | ✅ | ✅ |
@@ -34,9 +34,9 @@ Python library that provides a robust and well-documented API that allows develo
34
34
  | Users & Groups | ✅ | ✅ | ✅ | ✅ |
35
35
  | User & Weather status | ✅ | ✅ | ✅ | ✅ |
36
36
  | Other APIs*** | ✅ | ✅ | ✅ | ✅ |
37
- | Talk Bot API* | N/A | ✅ | ✅ | ✅ |
38
- | Settings UI API* | N/A | N/A | N/A | ✅ |
39
- | AI Providers API** | N/A | N/A | N/A | ✅ |
37
+ | Talk Bot API* | | ✅ | ✅ | ✅ |
38
+ | Settings UI API* | N/A | N/A | | ✅ |
39
+ | AI Providers API** | N/A | N/A | | ✅ |
40
40
 
41
41
  &ast;_available only for **NextcloudApp**_<br>
42
42
  &ast;&ast;_available only for **NextcloudApp**: SpeechToText, TextProcessing, Translation_<br>
@@ -150,7 +150,7 @@ class NcSessionBase(ABC):
150
150
  self.init_adapter()
151
151
  self.init_adapter_dav()
152
152
  self.response_headers = Headers()
153
- self._ocs_regexp = re.compile(r"/ocs/v[12]\.php/")
153
+ self._ocs_regexp = re.compile(r"/ocs/v[12]\.php/|/apps/groupfolders/")
154
154
 
155
155
  def init_adapter(self, restart=False) -> None:
156
156
  if getattr(self, "adapter", None) is None or restart:
@@ -176,12 +176,12 @@ class NcSessionBase(ABC):
176
176
 
177
177
  @property
178
178
  def ae_url(self) -> str:
179
- """Return base url for the App Ecosystem endpoints."""
179
+ """Return base url for the AppAPI endpoints."""
180
180
  return "/ocs/v1.php/apps/app_api/api/v1"
181
181
 
182
182
  @property
183
183
  def ae_url_v2(self) -> str:
184
- """Return base url for the App Ecosystem endpoints(version 2)."""
184
+ """Return base url for the AppAPI endpoints(version 2)."""
185
185
  return "/ocs/v1.php/apps/app_api/api/v2"
186
186
 
187
187
 
@@ -289,6 +289,7 @@ class NcSessionBasic(NcSessionBase, ABC):
289
289
  str_url = str(request.url)
290
290
  if re.search(self._ocs_regexp, str_url) is not None: # this is OCS call
291
291
  request.url = request.url.copy_merge_params({"format": "json"})
292
+ request.headers["Accept"] = "application/json"
292
293
 
293
294
  def _response_event(self, response: Response) -> None:
294
295
  str_url = str(response.request.url)
@@ -412,6 +413,7 @@ class AsyncNcSessionBasic(NcSessionBase, ABC):
412
413
  str_url = str(request.url)
413
414
  if re.search(self._ocs_regexp, str_url) is not None: # this is OCS call
414
415
  request.url = request.url.copy_merge_params({"format": "json"})
416
+ request.headers["Accept"] = "application/json"
415
417
 
416
418
  async def _response_event(self, response: Response) -> None:
417
419
  str_url = str(response.request.url)
@@ -1,3 +1,3 @@
1
1
  """Version of nc_py_api."""
2
2
 
3
- __version__ = "0.14.0"
3
+ __version__ = "0.15.1"
@@ -43,8 +43,8 @@ class ExAppInfo:
43
43
 
44
44
  @property
45
45
  def system(self) -> bool:
46
- """Flag indicating if the application is a system application."""
47
- return bool(self._raw_data["system"])
46
+ """**DEPRECATED** Flag indicating if the application is a system application."""
47
+ return True
48
48
 
49
49
  def __repr__(self):
50
50
  return f"<{self.__class__.__name__} id={self.app_id}, ver={self.version}>"
@@ -127,22 +127,26 @@ def fetch_models_task(nc: NextcloudApp, models: dict[str, dict], progress_init_s
127
127
  percent_for_each = min(int((100 - progress_init_start_value) / len(models)), 99)
128
128
  for model in models:
129
129
  if model.startswith(("http://", "https://")):
130
- __fetch_model_as_file(current_progress, percent_for_each, nc, model, models[model])
130
+ models[model]["path"] = __fetch_model_as_file(
131
+ current_progress, percent_for_each, nc, model, models[model]
132
+ )
131
133
  else:
132
- __fetch_model_as_snapshot(current_progress, percent_for_each, nc, model, models[model])
134
+ models[model]["path"] = __fetch_model_as_snapshot(
135
+ current_progress, percent_for_each, nc, model, models[model]
136
+ )
133
137
  current_progress += percent_for_each
134
138
  nc.set_init_status(100)
135
139
 
136
140
 
137
141
  def __fetch_model_as_file(
138
142
  current_progress: int, progress_for_task: int, nc: NextcloudApp, model_path: str, download_options: dict
139
- ) -> None:
143
+ ) -> str | None:
140
144
  result_path = download_options.pop("save_path", urlparse(model_path).path.split("/")[-1])
141
145
  try:
142
146
  with httpx.stream("GET", model_path, follow_redirects=True) as response:
143
147
  if not response.is_success:
144
148
  nc.log(LogLvl.ERROR, f"Downloading of '{model_path}' returned {response.status_code} status.")
145
- return
149
+ return None
146
150
  downloaded_size = 0
147
151
  linked_etag = ""
148
152
  for each_history in response.history:
@@ -163,7 +167,7 @@ def __fetch_model_as_file(
163
167
  sha256_hash.update(byte_block)
164
168
  if f'"{sha256_hash.hexdigest()}"' == linked_etag:
165
169
  nc.set_init_status(min(current_progress + progress_for_task, 99))
166
- return
170
+ return None
167
171
 
168
172
  with builtins.open(result_path, "wb") as file:
169
173
  last_progress = current_progress
@@ -174,13 +178,17 @@ def __fetch_model_as_file(
174
178
  if new_progress != last_progress:
175
179
  nc.set_init_status(new_progress)
176
180
  last_progress = new_progress
181
+
182
+ return result_path
177
183
  except Exception as e: # noqa pylint: disable=broad-exception-caught
178
184
  nc.log(LogLvl.ERROR, f"Downloading of '{model_path}' raised an exception: {e}")
179
185
 
186
+ return None
187
+
180
188
 
181
189
  def __fetch_model_as_snapshot(
182
190
  current_progress: int, progress_for_task, nc: NextcloudApp, mode_name: str, download_options: dict
183
- ) -> None:
191
+ ) -> str:
184
192
  from huggingface_hub import snapshot_download # noqa isort:skip pylint: disable=C0415 disable=E0401
185
193
  from tqdm import tqdm # noqa isort:skip pylint: disable=C0415 disable=E0401
186
194
 
@@ -191,7 +199,9 @@ def __fetch_model_as_snapshot(
191
199
 
192
200
  workers = download_options.pop("max_workers", 2)
193
201
  cache = download_options.pop("cache_dir", persistent_storage())
194
- snapshot_download(mode_name, tqdm_class=TqdmProgress, **download_options, max_workers=workers, cache_dir=cache)
202
+ return snapshot_download(
203
+ mode_name, tqdm_class=TqdmProgress, **download_options, max_workers=workers, cache_dir=cache
204
+ )
195
205
 
196
206
 
197
207
  def __nc_app(request: HTTPConnection) -> dict:
@@ -54,4 +54,4 @@ def get_model_path(model_name: str) -> str:
54
54
 
55
55
  def get_computation_device() -> str:
56
56
  """Returns computation device(`ROCM` or `CUDA`) if it is defined in the environment variable."""
57
- return os.environ.get("COMPUTE_DEVICE", "")
57
+ return str(os.environ.get("COMPUTE_DEVICE", "")).upper()
@@ -5,7 +5,7 @@ import dataclasses
5
5
  import typing
6
6
 
7
7
  from ..._exceptions import NextcloudException, NextcloudExceptionNotFound
8
- from ..._misc import require_capabilities
8
+ from ..._misc import clear_from_params_empty, require_capabilities
9
9
  from ..._session import AsyncNcSessionApp, NcSessionApp
10
10
 
11
11
  _EP_SUFFIX: str = "ai_provider/task_processing"
@@ -43,14 +43,18 @@ class _TaskProcessingProviderAPI:
43
43
  def __init__(self, session: NcSessionApp):
44
44
  self._session = session
45
45
 
46
- def register(self, name: str, display_name: str, task_type: str) -> None:
46
+ def register(
47
+ self, name: str, display_name: str, task_type: str, custom_task_type: dict[str, typing.Any] | None = None
48
+ ) -> None:
47
49
  """Registers or edit the TaskProcessing provider."""
48
50
  require_capabilities("app_api", self._session.capabilities)
49
51
  params = {
50
52
  "name": name,
51
53
  "displayName": display_name,
52
54
  "taskType": task_type,
55
+ "customTaskType": custom_task_type,
53
56
  }
57
+ clear_from_params_empty(["customTaskType"], params)
54
58
  self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=params)
55
59
 
56
60
  def unregister(self, name: str, not_fail=True) -> None:
@@ -118,14 +122,18 @@ class _AsyncTaskProcessingProviderAPI:
118
122
  def __init__(self, session: AsyncNcSessionApp):
119
123
  self._session = session
120
124
 
121
- async def register(self, name: str, display_name: str, task_type: str) -> None:
125
+ async def register(
126
+ self, name: str, display_name: str, task_type: str, custom_task_type: dict[str, typing.Any] | None = None
127
+ ) -> None:
122
128
  """Registers or edit the TaskProcessing provider."""
123
129
  require_capabilities("app_api", await self._session.capabilities)
124
130
  params = {
125
131
  "name": name,
126
132
  "displayName": display_name,
127
133
  "taskType": task_type,
134
+ "customTaskType": custom_task_type,
128
135
  }
136
+ clear_from_params_empty(["customTaskType"], params)
129
137
  await self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=params)
130
138
 
131
139
  async def unregister(self, name: str, not_fail=True) -> None:
@@ -203,9 +203,7 @@ class FsNode:
203
203
  )
204
204
 
205
205
  def __eq__(self, other):
206
- if self.file_id and self.file_id == other.file_id:
207
- return True
208
- return False
206
+ return bool(self.file_id and self.file_id == other.file_id)
209
207
 
210
208
  @property
211
209
  def has_extra(self) -> bool:
@@ -44,6 +44,7 @@ from .user_status import _AsyncUserStatusAPI, _UserStatusAPI
44
44
  from .users import _AsyncUsersAPI, _UsersAPI
45
45
  from .users_groups import _AsyncUsersGroupsAPI, _UsersGroupsAPI
46
46
  from .weather_status import _AsyncWeatherStatusAPI, _WeatherStatusAPI
47
+ from .webhooks import _AsyncWebhooksAPI, _WebhooksAPI
47
48
 
48
49
 
49
50
  class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
@@ -71,6 +72,8 @@ class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
71
72
  """Nextcloud API for managing users statuses"""
72
73
  weather_status: _WeatherStatusAPI
73
74
  """Nextcloud API for managing user weather statuses"""
75
+ webhooks: _WebhooksAPI
76
+ """Nextcloud API for managing webhooks"""
74
77
  _session: NcSessionBasic
75
78
 
76
79
  def __init__(self, session: NcSessionBasic):
@@ -86,6 +89,7 @@ class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
86
89
  self.users_groups = _UsersGroupsAPI(session)
87
90
  self.user_status = _UserStatusAPI(session)
88
91
  self.weather_status = _WeatherStatusAPI(session)
92
+ self.webhooks = _WebhooksAPI(session)
89
93
 
90
94
  @property
91
95
  def capabilities(self) -> dict:
@@ -169,6 +173,8 @@ class _AsyncNextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
169
173
  """Nextcloud API for managing users statuses"""
170
174
  weather_status: _AsyncWeatherStatusAPI
171
175
  """Nextcloud API for managing user weather statuses"""
176
+ webhooks: _AsyncWebhooksAPI
177
+ """Nextcloud API for managing webhooks"""
172
178
  _session: AsyncNcSessionBasic
173
179
 
174
180
  def __init__(self, session: AsyncNcSessionBasic):
@@ -184,6 +190,7 @@ class _AsyncNextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
184
190
  self.users_groups = _AsyncUsersGroupsAPI(session)
185
191
  self.user_status = _AsyncUserStatusAPI(session)
186
192
  self.weather_status = _AsyncWeatherStatusAPI(session)
193
+ self.webhooks = _AsyncWebhooksAPI(session)
187
194
 
188
195
  @property
189
196
  async def capabilities(self) -> dict:
@@ -350,14 +357,14 @@ class NextcloudApp(_NextcloudBasic):
350
357
  self._session.ocs("POST", f"{self._session.ae_url}/log", json={"level": int(log_lvl), "message": content})
351
358
 
352
359
  def users_list(self) -> list[str]:
353
- """Returns list of users on the Nextcloud instance. **Available** only for ``System`` applications."""
360
+ """Returns list of users on the Nextcloud instance."""
354
361
  return self._session.ocs("GET", f"{self._session.ae_url}/users")
355
362
 
356
363
  @property
357
364
  def user(self) -> str:
358
365
  """Property containing the current user ID.
359
366
 
360
- **System Applications** can change user ID they impersonate with **set_user** method.
367
+ **ExApps** can change user ID they impersonate with **set_user** method.
361
368
  """
362
369
  return self._session.user
363
370
 
@@ -480,14 +487,14 @@ class AsyncNextcloudApp(_AsyncNextcloudBasic):
480
487
  await self._session.ocs("POST", f"{self._session.ae_url}/log", json={"level": int(log_lvl), "message": content})
481
488
 
482
489
  async def users_list(self) -> list[str]:
483
- """Returns list of users on the Nextcloud instance. **Available** only for ``System`` applications."""
490
+ """Returns list of users on the Nextcloud instance."""
484
491
  return await self._session.ocs("GET", f"{self._session.ae_url}/users")
485
492
 
486
493
  @property
487
494
  async def user(self) -> str:
488
495
  """Property containing the current user ID.
489
496
 
490
- **System Applications** can change user ID they impersonate with **set_user** method.
497
+ **ExApps** can change user ID they impersonate with **set_user** method.
491
498
  """
492
499
  return await self._session.user
493
500
 
@@ -0,0 +1,210 @@
1
+ """Nextcloud Webhooks API."""
2
+
3
+ import dataclasses
4
+
5
+ from ._misc import clear_from_params_empty # , require_capabilities
6
+ from ._session import AsyncNcSessionBasic, NcSessionBasic
7
+
8
+
9
+ @dataclasses.dataclass
10
+ class WebhookInfo:
11
+ """Information about the Webhook."""
12
+
13
+ def __init__(self, raw_data: dict):
14
+ self._raw_data = raw_data
15
+
16
+ @property
17
+ def webhook_id(self) -> int:
18
+ """`ID` of the webhook."""
19
+ return self._raw_data["id"]
20
+
21
+ @property
22
+ def app_id(self) -> str:
23
+ """`ID` of the ExApp that registered webhook."""
24
+ return self._raw_data["appId"] if self._raw_data["appId"] else ""
25
+
26
+ @property
27
+ def user_id(self) -> str:
28
+ """`UserID` if webhook was registered in user context."""
29
+ return self._raw_data["userId"] if self._raw_data["userId"] else ""
30
+
31
+ @property
32
+ def http_method(self) -> str:
33
+ """HTTP method used to call webhook."""
34
+ return self._raw_data["httpMethod"]
35
+
36
+ @property
37
+ def uri(self) -> str:
38
+ """URL address that will be called for this webhook."""
39
+ return self._raw_data["uri"]
40
+
41
+ @property
42
+ def event(self) -> str:
43
+ """Nextcloud PHP event that triggers this webhook."""
44
+ return self._raw_data["event"]
45
+
46
+ @property
47
+ def event_filter(self):
48
+ """Mongo filter to apply to the serialized data to decide if firing."""
49
+ return self._raw_data["eventFilter"]
50
+
51
+ @property
52
+ def user_id_filter(self) -> str:
53
+ """Currently unknown."""
54
+ return self._raw_data["userIdFilter"]
55
+
56
+ @property
57
+ def headers(self) -> dict:
58
+ """Headers that should be added to request when calling webhook."""
59
+ return self._raw_data["headers"] if self._raw_data["headers"] else {}
60
+
61
+ @property
62
+ def auth_method(self) -> str:
63
+ """Currently unknown."""
64
+ return self._raw_data["authMethod"]
65
+
66
+ @property
67
+ def auth_data(self) -> dict:
68
+ """Currently unknown."""
69
+ return self._raw_data["authData"] if self._raw_data["authData"] else {}
70
+
71
+ def __repr__(self):
72
+ return f"<{self.__class__.__name__} id={self.webhook_id}, event={self.event}>"
73
+
74
+
75
+ class _WebhooksAPI:
76
+ """The class provides the application management API on the Nextcloud server."""
77
+
78
+ _ep_base: str = "/ocs/v1.php/apps/webhook_listeners/api/v1/webhooks"
79
+
80
+ def __init__(self, session: NcSessionBasic):
81
+ self._session = session
82
+
83
+ def get_list(self, uri_filter: str = "") -> list[WebhookInfo]:
84
+ params = {"uri": uri_filter} if uri_filter else {}
85
+ return [WebhookInfo(i) for i in self._session.ocs("GET", f"{self._ep_base}", params=params)]
86
+
87
+ def get_entry(self, webhook_id: int) -> WebhookInfo:
88
+ return WebhookInfo(self._session.ocs("GET", f"{self._ep_base}/{webhook_id}"))
89
+
90
+ def register(
91
+ self,
92
+ http_method: str,
93
+ uri: str,
94
+ event: str,
95
+ event_filter: dict | None = None,
96
+ user_id_filter: str = "",
97
+ headers: dict | None = None,
98
+ auth_method: str = "none",
99
+ auth_data: dict | None = None,
100
+ ):
101
+ params = {
102
+ "httpMethod": http_method,
103
+ "uri": uri,
104
+ "event": event,
105
+ "eventFilter": event_filter,
106
+ "userIdFilter": user_id_filter,
107
+ "headers": headers,
108
+ "authMethod": auth_method,
109
+ "authData": auth_data,
110
+ }
111
+ clear_from_params_empty(["eventFilter", "userIdFilter", "headers", "authMethod", "authData"], params)
112
+ return WebhookInfo(self._session.ocs("POST", f"{self._ep_base}", json=params))
113
+
114
+ def update(
115
+ self,
116
+ webhook_id: int,
117
+ http_method: str,
118
+ uri: str,
119
+ event: str,
120
+ event_filter: dict | None = None,
121
+ user_id_filter: str = "",
122
+ headers: dict | None = None,
123
+ auth_method: str = "none",
124
+ auth_data: dict | None = None,
125
+ ):
126
+ params = {
127
+ "id": webhook_id,
128
+ "httpMethod": http_method,
129
+ "uri": uri,
130
+ "event": event,
131
+ "eventFilter": event_filter,
132
+ "userIdFilter": user_id_filter,
133
+ "headers": headers,
134
+ "authMethod": auth_method,
135
+ "authData": auth_data,
136
+ }
137
+ clear_from_params_empty(["eventFilter", "userIdFilter", "headers", "authMethod", "authData"], params)
138
+ return WebhookInfo(self._session.ocs("POST", f"{self._ep_base}/{webhook_id}", json=params))
139
+
140
+ def unregister(self, webhook_id: int) -> bool:
141
+ return self._session.ocs("DELETE", f"{self._ep_base}/{webhook_id}")
142
+
143
+
144
+ class _AsyncWebhooksAPI:
145
+ """The class provides the async application management API on the Nextcloud server."""
146
+
147
+ _ep_base: str = "/ocs/v1.php/webhooks"
148
+
149
+ def __init__(self, session: AsyncNcSessionBasic):
150
+ self._session = session
151
+
152
+ async def get_list(self, uri_filter: str = "") -> list[WebhookInfo]:
153
+ params = {"uri": uri_filter} if uri_filter else {}
154
+ return [WebhookInfo(i) for i in await self._session.ocs("GET", f"{self._ep_base}", params=params)]
155
+
156
+ async def get_entry(self, webhook_id: int) -> WebhookInfo:
157
+ return WebhookInfo(await self._session.ocs("GET", f"{self._ep_base}/{webhook_id}"))
158
+
159
+ async def register(
160
+ self,
161
+ http_method: str,
162
+ uri: str,
163
+ event: str,
164
+ event_filter: dict | None = None,
165
+ user_id_filter: str = "",
166
+ headers: dict | None = None,
167
+ auth_method: str = "none",
168
+ auth_data: dict | None = None,
169
+ ):
170
+ params = {
171
+ "httpMethod": http_method,
172
+ "uri": uri,
173
+ "event": event,
174
+ "eventFilter": event_filter,
175
+ "userIdFilter": user_id_filter,
176
+ "headers": headers,
177
+ "authMethod": auth_method,
178
+ "authData": auth_data,
179
+ }
180
+ clear_from_params_empty(["eventFilter", "userIdFilter", "headers", "authMethod", "authData"], params)
181
+ return WebhookInfo(await self._session.ocs("POST", f"{self._ep_base}", json=params))
182
+
183
+ async def update(
184
+ self,
185
+ webhook_id: int,
186
+ http_method: str,
187
+ uri: str,
188
+ event: str,
189
+ event_filter: dict | None = None,
190
+ user_id_filter: str = "",
191
+ headers: dict | None = None,
192
+ auth_method: str = "none",
193
+ auth_data: dict | None = None,
194
+ ):
195
+ params = {
196
+ "id": webhook_id,
197
+ "httpMethod": http_method,
198
+ "uri": uri,
199
+ "event": event,
200
+ "eventFilter": event_filter,
201
+ "userIdFilter": user_id_filter,
202
+ "headers": headers,
203
+ "authMethod": auth_method,
204
+ "authData": auth_data,
205
+ }
206
+ clear_from_params_empty(["eventFilter", "userIdFilter", "headers", "authMethod", "authData"], params)
207
+ return WebhookInfo(await self._session.ocs("POST", f"{self._ep_base}/{webhook_id}", json=params))
208
+
209
+ async def unregister(self, webhook_id: int) -> bool:
210
+ return await self._session.ocs("DELETE", f"{self._ep_base}/{webhook_id}")
@@ -177,7 +177,7 @@ design.max-attributes = 8
177
177
  design.max-locals = 20
178
178
  design.max-branches = 16
179
179
  design.max-returns = 8
180
- design.max-args = 8
180
+ design.max-args = 10
181
181
  basic.good-names = [
182
182
  "a",
183
183
  "b",
File without changes
File without changes
File without changes
File without changes