Skip to content

Commit 8a030f1

Browse files
authored
set_handlers: heartbeat_handler/init_handler params rework (#226)
Signed-off-by: Alexander Piskun <[email protected]>
1 parent ee4b739 commit 8a030f1

File tree

20 files changed

+168
-201
lines changed

20 files changed

+168
-201
lines changed

CHANGELOG.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
## [0.10.0 - 2024-02-0x]
5+
## [0.10.0 - 2024-02-14]
66

77
### Added
88

9-
- set_handlers: `models_to_fetch` can now accept direct links to a files to download. #217
10-
- DeclarativeSettings API for Nextcloud 29. #222
9+
- NextcloudApp: `set_handlers`: `models_to_fetch` can now accept direct links to a files to download. #217
10+
- NextcloudApp: DeclarativeSettings UI API for Nextcloud `29`. #222
1111

1212
### Changed
1313

14-
- adjusted code related to changes in AppAPI `2.0.3` #216
14+
- NextcloudApp: adjusted code related to changes in AppAPI `2.0.3` #216
15+
- NextcloudApp: `set_handlers` **rework of optional parameters** see PR for information. #226
1516

1617
## [0.9.0 - 2024-01-25]
1718

docs/NextcloudApp.rst

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,36 @@ Since this is a simple skeleton application, we only define the ``/enable`` endp
1919
When the application receives a request at the endpoint ``/enable``,
2020
it should register all its functionalities in the cloud and wait for requests from Nextcloud.
2121

22-
So, calling:
22+
So, defining:
2323

2424
.. code-block:: python
2525
26-
set_handlers(APP, enabled_handler)
26+
@asynccontextmanager
27+
async def lifespan(app: FastAPI):
28+
set_handlers(app, enabled_handler)
29+
yield
2730
2831
will register an **enabled_handler** that will be called **both when the application is enabled and disabled**.
2932

3033
During the enablement process, you should register all the functionalities that your application offers
3134
in the **enabled_handler** and remove them during the disablement process.
3235

33-
The API is designed so that you don't have to check whether an endpoint is already registered
36+
The AppAPI APIs is designed so that you don't have to check whether an endpoint is already registered
3437
(e.g., in case of a malfunction or if the administrator manually altered something in the Nextcloud database).
35-
The API will not fail, and in such cases, it will simply re-register without error.
38+
The AppAPI APIs will not fail, and in such cases, it will simply re-register without error.
3639

3740
If any error prevents your application from functioning, you should provide a brief description in the return instead
3841
of an empty string, and log comprehensive information that will assist the administrator in addressing the issue.
3942

43+
.. code-block:: python
44+
45+
APP = FastAPI(lifespan=lifespan)
46+
APP.add_middleware(AppAPIAuthMiddleware)
47+
48+
With help of ``AppAPIAuthMiddleware`` you can add **global** AppAPI authentication for all future endpoints you will define.
49+
50+
.. note:: ``AppAPIAuthMiddleware`` supports **disable_for** optional argument, where you can list all routes for which authentication should be skipped.
51+
4052
Dockerfile
4153
----------
4254

@@ -86,7 +98,7 @@ After launching your application, execute the following command in the Nextcloud
8698
.. code-block:: shell
8799
88100
php occ app_api:app:register YOUR_APP_ID manual_install --json-info \
89-
"{\"appid\":\"YOUR_APP_ID\",\"name\":\"YOUR_APP_DISPLAY_NAME\",\"daemon_config_name\":\"manual_install\",\"version\":\"YOU_APP_VERSION\",\"secret\":\"YOUR_APP_SECRET\",\"scopes\":{\"required\":[\"ALL\"],\"optional\":[]},\"port\":SELECTED_PORT,\"system_app\":0}" \
101+
"{\"appid\":\"YOUR_APP_ID\",\"name\":\"YOUR_APP_DISPLAY_NAME\",\"daemon_config_name\":\"manual_install\",\"version\":\"YOU_APP_VERSION\",\"secret\":\"YOUR_APP_SECRET\",\"scopes\":[\"ALL\"],\"port\":SELECTED_PORT,\"system_app\":0}" \
90102
--force-scopes --wait-finish
91103
92104
You can see how **nc_py_api** registers in ``scripts/dev_register.sh``.
@@ -178,29 +190,12 @@ After that, let's define the **"/video_to_gif"** endpoint that we had registered
178190
@APP.post("/video_to_gif")
179191
async def video_to_gif(
180192
file: UiFileActionHandlerInfo,
181-
nc: Annotated[NextcloudApp, Depends(nc_app)],
182193
background_tasks: BackgroundTasks,
183194
):
184195
background_tasks.add_task(convert_video_to_gif, file.actionFile.to_fs_node(), nc)
185196
return Response()
186197
187-
And this step should be discussed in more detail, since it demonstrates most of the process of working External applications.
188-
189-
Here we see: **nc: Annotated[NextcloudApp, Depends(nc_app)]**
190-
191-
For those who already know how FastAPI works, everything should be clear by now,
192-
and for those who have not, it is very important to understand that:
193-
194-
It is a declaration of FastAPI `dependency <https://fastapi.tiangolo.com/tutorial/dependencies/#dependencies>`_ to be executed
195-
before the code of **video_to_gif** starts execution.
196-
197-
And this required dependency handles authentication and returns an instance of the :py:class:`~nc_py_api.nextcloud.NextcloudApp`
198-
class that allows you to make requests to Nextcloud.
199-
200-
.. note:: Every endpoint in your application should be protected with such method, this will ensure that only Nextcloud instance
201-
will be able to perform requests to the application.
202-
203-
Finally, we are left with two much less interesting parameters, let's start with the last one, with **BackgroundTasks**:
198+
We see two parameters ``file`` and ``BackgroundTasks``, let's start with the last one, with **BackgroundTasks**:
204199

205200
FastAPI `BackgroundTasks <https://fastapi.tiangolo.com/tutorial/background-tasks/?h=backgroundtasks#background-tasks>`_ documentation.
206201

@@ -219,28 +214,36 @@ and since this is not directly related to working with NextCloud, we will skip t
219214

220215
**ToGif** example `full source <https://github.com/cloud-py-api/nc_py_api/blob/main/examples/as_app/to_gif/lib/main.py>`_ code.
221216

222-
Using AppAPIAuthMiddleware
223-
--------------------------
217+
Life wo AppAPIAuthMiddleware
218+
----------------------------
224219

225-
If in your application in most cases you don't really need the ``NextcloudApp`` class returned after standard authentication using `Depends`:
220+
If for some reason you do not want to use global AppAPI authentication **nc_py_api** provides a FastAPI Dependency for authentication your endpoints.
226221

227-
.. code-block:: python
222+
This is a modified endpoint from ``to_gif`` example:
228223

229-
nc: Annotated[NextcloudApp, Depends(nc_app)]
224+
.. code-block:: python
230225
231-
In this case, you can use global authentication. It's quite simple, just add this line of code:
226+
@APP.post("/video_to_gif")
227+
async def video_to_gif(
228+
file: UiFileActionHandlerInfo,
229+
nc: Annotated[NextcloudApp, Depends(nc_app)],
230+
background_tasks: BackgroundTasks,
231+
):
232+
background_tasks.add_task(convert_video_to_gif, file.actionFile.to_fs_node(), nc)
233+
return Response()
232234
233-
.. code-block:: python
234235
235-
from nc_py_api.ex_app import AppAPIAuthMiddleware
236+
Here we see: **nc: Annotated[NextcloudApp, Depends(nc_app)]**
236237

237-
APP = FastAPI(lifespan=lifespan)
238-
APP.add_middleware(AppAPIAuthMiddleware)
238+
For those who already know how FastAPI works, everything should be clear by now,
239+
and for those who have not, it is very important to understand that:
239240

240-
and it will be called for all your endpoints and check the validity of the connection itself.
241+
It is a declaration of FastAPI `dependency <https://fastapi.tiangolo.com/tutorial/dependencies/#dependencies>`_ to be executed
242+
before the code of **video_to_gif** starts execution.
241243

242-
``AppAPIAuthMiddleware`` supports **disable_for** optional argument, where you can list all routes for which authentication should be skipped.
244+
And this required dependency handles authentication and returns an instance of the :py:class:`~nc_py_api.nextcloud.NextcloudApp`
245+
class that allows you to make requests to Nextcloud.
243246

244-
You can still use at the same time the *AppAPIAuthMiddleware* and *Depends(nc_app)*, it is clever enough and they won't interfere with each other.
247+
.. note:: NcPyAPI is clever enough to detect whether global authentication handler is enabled, and not perform authentication twice for performance reasons.
245248

246249
This chapter ends here, but the next topics are even more intriguing.

docs/NextcloudTalkBot.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ Afterward, using FastAPI, you can define endpoints that will be invoked by Talk:
3636
3737
@APP.post("/currency_talk_bot")
3838
async def currency_talk_bot(
39-
_nc: Annotated[NextcloudApp, Depends(nc_app)],
4039
message: Annotated[talk_bot.TalkBotMessage, Depends(atalk_bot_msg)],
4140
background_tasks: BackgroundTasks,
4241
):

examples/as_app/skeleton/lib/main.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@
55
from fastapi import FastAPI
66

77
from nc_py_api import NextcloudApp
8-
from nc_py_api.ex_app import LogLvl, run_app, set_handlers
8+
from nc_py_api.ex_app import AppAPIAuthMiddleware, LogLvl, run_app, set_handlers
99

1010

1111
@asynccontextmanager
12-
async def lifespan(_app: FastAPI):
13-
set_handlers(APP, enabled_handler)
12+
async def lifespan(app: FastAPI):
13+
set_handlers(app, enabled_handler)
1414
yield
1515

1616

1717
APP = FastAPI(lifespan=lifespan)
18+
APP.add_middleware(AppAPIAuthMiddleware) # set global AppAPI authentication middleware
1819

1920

2021
def enabled_handler(enabled: bool, nc: NextcloudApp) -> str:

examples/as_app/talk_bot/lib/main.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@
88
from fastapi import BackgroundTasks, Depends, FastAPI, Response
99

1010
from nc_py_api import NextcloudApp, talk_bot
11-
from nc_py_api.ex_app import atalk_bot_msg, nc_app, run_app, set_handlers
11+
from nc_py_api.ex_app import AppAPIAuthMiddleware, atalk_bot_msg, run_app, set_handlers
1212

1313

1414
# The same stuff as for usual External Applications
1515
@asynccontextmanager
16-
async def lifespan(_app: FastAPI):
17-
set_handlers(APP, enabled_handler)
16+
async def lifespan(app: FastAPI):
17+
set_handlers(app, enabled_handler)
1818
yield
1919

2020

2121
APP = FastAPI(lifespan=lifespan)
22+
APP.add_middleware(AppAPIAuthMiddleware)
23+
24+
2225
# We define bot globally, so if no `multiprocessing` module is used, it can be reused by calls.
2326
# All stuff in it works only with local variables, so in the case of multithreading, there should not be problems.
2427
CURRENCY_BOT = talk_bot.TalkBot("/currency_talk_bot", "Currency convertor", "Usage: `@currency convert 100 EUR to USD`")
@@ -66,7 +69,6 @@ def currency_talk_bot_process_request(message: talk_bot.TalkBotMessage):
6669

6770
@APP.post("/currency_talk_bot")
6871
async def currency_talk_bot(
69-
_nc: Annotated[NextcloudApp, Depends(nc_app)],
7072
message: Annotated[talk_bot.TalkBotMessage, Depends(atalk_bot_msg)],
7173
background_tasks: BackgroundTasks,
7274
):
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
nc_py_api[app]>=0.5.0
1+
nc_py_api[app]>=0.10.0

examples/as_app/talk_bot_ai/lib/main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@
1010

1111
from nc_py_api import NextcloudApp, talk_bot
1212
from nc_py_api.ex_app import (
13+
AppAPIAuthMiddleware,
1314
atalk_bot_msg,
1415
get_model_path,
15-
nc_app,
1616
run_app,
1717
set_handlers,
1818
)
1919

2020

2121
@asynccontextmanager
22-
async def lifespan(_app: FastAPI):
23-
set_handlers(APP, enabled_handler, models_to_fetch={MODEL_NAME: {}})
22+
async def lifespan(app: FastAPI):
23+
set_handlers(app, enabled_handler, models_to_fetch={MODEL_NAME: {}})
2424
yield
2525

2626

2727
APP = FastAPI(lifespan=lifespan)
28+
APP.add_middleware(AppAPIAuthMiddleware)
2829
AI_BOT = talk_bot.TalkBot("/ai_talk_bot", "AI talk bot", "Usage: `@assistant What sounds do cats make?`")
2930
MODEL_NAME = "MBZUAI/LaMini-Flan-T5-783M"
3031

@@ -40,7 +41,6 @@ def ai_talk_bot_process_request(message: talk_bot.TalkBotMessage):
4041

4142
@APP.post("/ai_talk_bot")
4243
async def ai_talk_bot(
43-
_nc: Annotated[NextcloudApp, Depends(nc_app)],
4444
message: Annotated[talk_bot.TalkBotMessage, Depends(atalk_bot_msg)],
4545
background_tasks: BackgroundTasks,
4646
):

examples/as_app/talk_bot_ai/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
nc_py_api[app]>=0.7.0
1+
nc_py_api[app]>=0.10.0
22
transformers>=4.33
33
torch
44
torchvision

examples/as_app/to_gif/lib/main.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,36 @@
33
import tempfile
44
from contextlib import asynccontextmanager
55
from os import path
6-
from typing import Annotated
76

87
import cv2
98
import imageio
109
import numpy
11-
from fastapi import BackgroundTasks, Depends, FastAPI
10+
from fastapi import BackgroundTasks, FastAPI
1211
from pygifsicle import optimize
1312
from requests import Response
1413

1514
from nc_py_api import FsNode, NextcloudApp
16-
from nc_py_api.ex_app import LogLvl, UiActionFileInfo, nc_app, run_app, set_handlers
15+
from nc_py_api.ex_app import (
16+
AppAPIAuthMiddleware,
17+
LogLvl,
18+
UiActionFileInfo,
19+
run_app,
20+
set_handlers,
21+
)
1722

1823

1924
@asynccontextmanager
20-
async def lifespan(_app: FastAPI):
21-
set_handlers(APP, enabled_handler)
25+
async def lifespan(app: FastAPI):
26+
set_handlers(app, enabled_handler)
2227
yield
2328

2429

2530
APP = FastAPI(lifespan=lifespan)
31+
APP.add_middleware(AppAPIAuthMiddleware)
2632

2733

28-
def convert_video_to_gif(input_file: FsNode, nc: NextcloudApp):
34+
def convert_video_to_gif(input_file: FsNode):
35+
nc = NextcloudApp()
2936
save_path = path.splitext(input_file.user_path)[0] + ".gif"
3037
nc.log(LogLvl.WARNING, f"Processing:{input_file.user_path} -> {save_path}")
3138
try:
@@ -70,10 +77,9 @@ def convert_video_to_gif(input_file: FsNode, nc: NextcloudApp):
7077
@APP.post("/video_to_gif")
7178
async def video_to_gif(
7279
file: UiActionFileInfo,
73-
nc: Annotated[NextcloudApp, Depends(nc_app)],
7480
background_tasks: BackgroundTasks,
7581
):
76-
background_tasks.add_task(convert_video_to_gif, file.to_fs_node(), nc)
82+
background_tasks.add_task(convert_video_to_gif, file.to_fs_node())
7783
return Response()
7884

7985

examples/as_app/to_gif/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
nc_py_api[app]>=0.7.0
1+
nc_py_api[app]>=0.10.0
22
pygifsicle
33
imageio
44
opencv-python

0 commit comments

Comments
 (0)