Skip to content
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

Major/410 session update #395

Merged
merged 44 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1d35875
WIP
Feb 1, 2024
93bdde6
Add test_parser and update session delete
Treesarj Feb 2, 2024
2f9b8c5
Updated the dataresource. Removed instantiation of the download and p…
Feb 7, 2024
608b2d5
fix parser API to fetch from session
Treesarj Feb 8, 2024
c8ebc22
add checks and fix initialize in dataresource strategy
Treesarj Feb 8, 2024
203eccc
modify parser API's
Treesarj Feb 8, 2024
d50f461
Add initialize endpoint for parser
Treesarj Feb 12, 2024
f691179
rename endpoint
Treesarj Feb 19, 2024
65b03cf
fix pre-commit errors
Treesarj Feb 19, 2024
74a737d
Merge branch 'master' into major/410-session-update
Treesarj Feb 20, 2024
55cd80e
fix pre-commit
Treesarj Feb 20, 2024
89863f2
Merge branch 'master' into major/410-session-update
CasperWA Feb 21, 2024
454e17b
updated tests and new test for dataresource
Treesarj Feb 22, 2024
8ecc76a
Update app/models/session.py
Treesarj Feb 22, 2024
cedc8ef
Update app/routers/session.py
Treesarj Feb 22, 2024
9d52802
resolve comments
Treesarj Feb 22, 2024
344645f
Merge branch 'major/410-session-update' of github.com:EMMC-ASBL/oteap…
Treesarj Feb 22, 2024
1a45ab7
fix test configuration
Treesarj Feb 23, 2024
d6d6a88
fix tests
Treesarj Feb 23, 2024
ba5c771
requested changes
Treesarj Feb 23, 2024
65f9eff
Update tests/static/test_strategies/resource.py
Treesarj Feb 23, 2024
5541e93
remove test ttl file and debug.py from requirements.txt
Treesarj Feb 26, 2024
aee3cf5
requested changes
Treesarj Feb 26, 2024
e0786d7
move cache key validation
Treesarj Feb 26, 2024
5a7bc1e
requested changes
Treesarj Feb 26, 2024
759aebd
redo fetch cache key
Treesarj Feb 26, 2024
f4aa22a
requested changes
Treesarj Feb 27, 2024
24d0237
fix pylint errors
Treesarj Feb 27, 2024
94cf49c
fix pylint errors
Treesarj Feb 27, 2024
968b0f3
Remove unused imports
CasperWA Feb 27, 2024
2ae97b0
Satisfy pre-commit hooks
CasperWA Feb 27, 2024
bc04d82
Temporarily use the latest developments in oteapi-core
CasperWA Feb 28, 2024
25e8deb
Do not show coverage on failed test runs
CasperWA Feb 28, 2024
d3eb4c8
Add session merging tests to all router tests
CasperWA Feb 28, 2024
7aceaa7
Update dumping dataresouce config in test
CasperWA Feb 28, 2024
a25ae0f
Merge remote-tracking branch 'origin/master' into major/410-session-u…
CasperWA Feb 28, 2024
7181f95
Use v0.7.0.dev0 of oteapi-core
CasperWA Feb 29, 2024
3cef560
Update docker-compose_ci_plugin.yml
CasperWA Feb 29, 2024
bfb6ff0
Try avoiding running debugpy
CasperWA Feb 29, 2024
6b57b1d
Revert changed sleep in CI
CasperWA Feb 29, 2024
306c0ed
Remove logging setup in tests
CasperWA Feb 29, 2024
0e1213a
Update app/routers/session.py
Treesarj Mar 1, 2024
0ad16f0
requested changes
Treesarj Mar 1, 2024
257fe9f
Update README with important notice
CasperWA Mar 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dev/requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dulwich~=0.21.7
debugpy>=1.8.0
fakeredis~=2.21
httpx~=0.26.0
invoke~=2.2
Expand Down
20 changes: 20 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Remote Attach",
"type": "python",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}
]
}
]
}
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ COPY .git .git/
COPY .dev/requirements_dev.txt .pre-commit-config.yaml pyproject.toml ./

# Run static security check, linters, and pytest with code coverage
RUN pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org -r requirements_dev.txt \
RUN pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org -r requirements_dev.txt
Treesarj marked this conversation as resolved.
Show resolved Hide resolved
# Ignore ID 44715 for now.
# See this NumPy issue for more information: https://github.com/numpy/numpy/issues/19038
&& pre-commit run --all-files \
&& safety check -r requirements.txt -r requirements_dev.txt --ignore 44715 \
&& pytest --cov app
# && pre-commit run --all-files
# && safety check -r requirements.txt -r requirements_dev.txt --ignore 44715
# && pytest --cov app

# Run app with reload option
EXPOSE 8080
Expand Down
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,74 @@ networks:
otenet:
```

## Debugging OTEAPI Service in Visual Studio

### Prerequisites
* Ensure you have `docker` and `docker-compose` installed on your system.
* Install Visual Studio Code along with the Python extension and the Remote - Containers extension.
* Have the docker-compose_dev.yml file configured for your OTEAPI Service.
* Ensure `debugpy` is installed in your virtual environment

### Configuring Visual Studio Code:

* Open your project in Visual Studio Code.
* Go to the Run and Debug view (Ctrl+Shift+D or ⌘+Shift+D on macOS).
* Create a launch.json file in the .vscode folder at the root of your project (if not already present). This file should contain something like this:

```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Remote Attach",
"type": "python",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}
]
}
]
}
```

### Update the entrypoint.sh
In order to enable remote debugging, update the file `entrypoint.sh` such that the oteapi is started using the following command:

```
python -m debugpy --wait-for-client --listen 0.0.0.0:5678 -m hypercorn --bind 0.0.0.0:8080 --reload asgi:app "$@"
```

This will run the OTEAPI service in remote debug mode. The debugpy debugger will wait until a remote debuggin client is attached to port 5678. This happens when activating the "Run and Debug | Python: Remote Attach" from Visual Studio Code. For other IDEs please follow the relevant documentation.



### Starting the docker-compose
* Open a terminal and navigate to the directory containing your docker-compose_dev.yml file.
* Start the service using the command:

```sh
docker-compose -f docker-compose_dev.yml up -d
```

The `-d` option starts the OTEAPI sevice in detached mode. In order to access and follow the logs output you can run `docker-compose logs -f`.

### Attaching to the Remote Process:

* With the `launch.json` configured, go to the Run and Debug view in Visual Studio Code.
* Select the "Python: Remote Attach" configuration from the dropdown menu.* Click the green play button or press F5 to start the debugging session.
* Visual Studio Code will attach to the remote debugging session running inside the Docker container.

### Debugging the Application:

Set breakpoints in your code as needed. Interact with your FastAPI application as you normally would. Visual Studio Code will pause execution when a breakpoint is hit, allowing you to inspect variables, step through code, and debug your application.

## Acknowledgment

OTEAPI Core has been supported by the following projects:
Expand Down
4 changes: 3 additions & 1 deletion app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
dataresource,
function,
mapping,
parser,
redisadmin,
session,
transformation,
Expand Down Expand Up @@ -82,9 +83,10 @@ def create_app() -> FastAPI:
available_routers = [
session,
dataresource,
parser,
mapping,
datafilter,
function,
mapping,
transformation,
triplestore,
]
Expand Down
2 changes: 1 addition & 1 deletion app/models/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def httpexception_422_resource_id_is_unprocessable(resource_id: str) -> HTTPExce
detail=[
{
"loc": ["resource_id"],
"msg": "Missing downloadUrl/mediaType or "
"msg": "Missing resourceType or downloadUrl/mediaType or "
f"accessUrl/accessService identifier in {resource_id=}",
"type": "Error",
}
Expand Down
67 changes: 67 additions & 0 deletions app/models/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Parser specific pydantic response models."""

from typing import Annotated
from uuid import uuid4

from oteapi.models import AttrDict
from pydantic import Field

from app.models.response import CreateResponse, GetResponse, InitializeResponse, Session

IDPREFIX = "parser"


class CreateParserResponse(CreateResponse):
"""Create a response resource

Router: `POST /parser`
"""

parser_id: Annotated[
str,
Field(
default_factory=lambda: f"{IDPREFIX}-{uuid4()}",
description="The parser id.",
pattern=(
rf"^{IDPREFIX}-[0-9a-f]{{8}}-[0-9a-f]{{4}}-[0-9a-f]{{4}}-[0-9a-f]{{4}}-"
r"[0-9a-f]{12}$"
),
),
]


class GetParserResponse(GetResponse):
"""Get a parser resource

Router: `GET /parser/{parser_id}`
"""


class InitializeParserResponse(InitializeResponse):
"""Initialize a parser resource

Router: `POST /parser/{parser_id}/initialize`
"""


class DeleteAllParsersResponse(AttrDict):
"""### Delete all sessions

Router: `DELETE /parser`
"""

number_of_deleted_parsers: Annotated[
int, Field(description="The number of deleted parsers in the Redis cache.")
]


class ListParsersResponse(Session):
"""List all parser ids

Router: `GET /parser`
"""

keys_: Annotated[
list[str],
Field(description="List of all parser ids in the cache.", alias="keys"),
]
6 changes: 5 additions & 1 deletion app/models/session.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Session-specific pydantic response models."""

from typing import Annotated
from typing import Annotated, Optional
from uuid import uuid4

from pydantic import Field
Expand Down Expand Up @@ -57,6 +57,10 @@ class DeleteAllSessionsResponse(Session):
number_of_deleted_sessions: Annotated[
int, Field(description="The number of deleted sessions in the Redis cache.")
]
message: Optional[str] = Field(
"All session keys deleted.",
description="Optional message indicating the result of the operation.",
)
Treesarj marked this conversation as resolved.
Show resolved Hide resolved


class DeleteSessionResponse(Session):
Expand Down
3 changes: 3 additions & 0 deletions app/routers/datafilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
responses={
status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError},
},
tags=["datafilter"],
)
async def create_filter(
cache: TRedisPlugin,
Expand Down Expand Up @@ -61,6 +62,7 @@ async def create_filter(
responses={
status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError},
},
tags=["datafilter"],
)
async def get_filter(
cache: TRedisPlugin,
Expand Down Expand Up @@ -109,6 +111,7 @@ async def get_filter(
responses={
status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError},
},
tags=["datafilter"],
)
async def initialize_filter(
cache: TRedisPlugin,
Expand Down
70 changes: 22 additions & 48 deletions app/routers/dataresource.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@
responses={
status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError},
},
tags=["dataresource"],
)
async def create_dataresource(
cache: TRedisPlugin,
config: ResourceConfig,
request: Request,
session_id: Optional[str] = None,
) -> CreateResourceResponse:
"""### Register an external data resource.
Expand All @@ -56,7 +56,7 @@ async def create_dataresource(
"""
new_resource = CreateResourceResponse()

config.token = request.headers.get("Authorization") or config.token
# config.token = request.headers.get("Authorization") or config.token
Treesarj marked this conversation as resolved.
Show resolved Hide resolved

resource_config = config.model_dump_json()

Expand All @@ -81,6 +81,7 @@ async def create_dataresource(
responses={
status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError},
},
tags=["dataresource"],
)
async def info_dataresource(
cache: TRedisPlugin,
Expand All @@ -107,6 +108,7 @@ async def info_dataresource(
status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError},
status.HTTP_422_UNPROCESSABLE_ENTITY: {"model": HTTPValidationError},
},
tags=["dataresource"],
)
async def read_dataresource(
cache: TRedisPlugin,
Expand Down Expand Up @@ -137,27 +139,18 @@ async def read_dataresource(
f"Expected cache value of {session_id} to be a string or bytes, "
f"found it to be of type {type(cache_value)!r}."
Treesarj marked this conversation as resolved.
Show resolved Hide resolved
)
session_data: "Optional[dict[str, Any]]" = (
None if not session_id else json.loads(cache_value)
)

if config.downloadUrl and config.mediaType:
# Download strategy
session_update = create_strategy("download", config).get(session=session_data)
if session_update and session_id:
session_data = await _update_session(session_id, session_update, cache)

# Parse strategy
session_update = create_strategy("parse", config).get(session=session_data)
if session_update and session_id:
await _update_session(session_id, session_update, cache)
if not config.resourceType:
raise httpexception_422_resource_id_is_unprocessable(resource_id)

elif config.accessUrl and config.accessService:
# Resource strategy
session_update = create_strategy("resource", config).get(session=session_data)
if (config.downloadUrl and config.mediaType) or (
config.accessUrl and config.accessService
):
session_update = create_strategy("resource", config).get()
if session_update and session_id:
await _update_session(session_id, session_update, cache)

await _update_session(
session_id=session_id, updated_session=session_update, redis=cache
)
else:
raise httpexception_422_resource_id_is_unprocessable(resource_id)
Treesarj marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -171,6 +164,7 @@ async def read_dataresource(
status.HTTP_404_NOT_FOUND: {"model": HTTPNotFoundError},
status.HTTP_422_UNPROCESSABLE_ENTITY: {"model": HTTPValidationError},
},
tags=["dataresource"],
)
async def initialize_dataresource(
cache: TRedisPlugin,
Expand Down Expand Up @@ -198,39 +192,19 @@ async def initialize_dataresource(
f"Expected cache value of {session_id} to be a string or bytes, "
f"found it to be of type {type(cache_value)!r}."
)
session_data: "Optional[dict[str, Any]]" = (
None if not session_id else json.loads(cache_value)
)

if config.downloadUrl and config.mediaType:
# Download strategy
session_update = create_strategy("download", config).initialize(
session=session_data
)
if session_update and session_id:
session_data = await _update_session(
session_id=session_id, updated_session=session_update, redis=cache
)

# Parse strategy
session_update = create_strategy("parse", config).initialize(
session=session_data
)
if session_update and session_id:
session_data = await _update_session(
session_id=session_id, updated_session=session_update, redis=cache
)
if not config.resourceType:
raise httpexception_422_resource_id_is_unprocessable(resource_id)

elif config.accessUrl and config.accessService:
# Resource strategy
session_update = create_strategy("resource", config).initialize(
session=session_data
)
if (config.downloadUrl and config.mediaType) or (
config.accessUrl and config.accessService
):
# Download strategy
session_update = create_strategy("resource", config).initialize()
if session_update and session_id:
session_data = await _update_session(
await _update_session(
session_id=session_id, updated_session=session_update, redis=cache
)

else:
raise httpexception_422_resource_id_is_unprocessable(resource_id)

Expand Down
Loading
Loading