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

feat: add base classes and docs #191

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/code_quality.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ jobs:
with:
os: ${{ job.os }}
python-version: '3.11'
poetry-install-options: "--only=types --no-root"
poetry-export-options: "--only=types"
poetry-install-options: "--only=main --only=types --no-root"
uniqueg marked this conversation as resolved.
Show resolved Hide resolved
poetry-export-options: "--only=main --only=types"

- name: Check types
run: poetry run mypy tesk/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/endpoint_test.yaml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and other trivial changes may be required by linters, but they do not really fit in this PR. Open another style: PR to address these issues in bulk.

Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ jobs:
done
echo "API failed to start in time"
exit 1
...
...
8 changes: 5 additions & 3 deletions deployment/config.yaml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: Include in a style: PR

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
# FOCA configuration
# Available in app context as attributes of `current_app.config.foca`
# Automatically validated via FOCA
Expand Down Expand Up @@ -71,9 +72,10 @@ log:
# Exception configuration
# Cf. https://foca.readthedocs.io/en/latest/modules/foca.models.html#foca.models.config.ExceptionConfig
exceptions:
required_members: [['detail'], ['status'], ['title']]
status_member: ['status']
exceptions: tesk.exceptions.exceptions
required_members: [['detail'], ['status'], ['title']]
status_member: ['status']
exceptions: tesk.exceptions.exceptions

storeLogs:
execution_trace: True
...
6 changes: 3 additions & 3 deletions docs/source/index.rst
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: Include in a style: PR

Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ TESK
.. toctree::
:maxdepth: 1
:caption: TESK

.. mdinclude:: ../../README.md
.. mdinclude:: ../tesintro.md

.. Not adding a heading to separate deployment docs because
.. Not adding a heading to separate deployment docs because
.. these md files already have heading.
.. mdinclude:: ../../deployment/documentation/deployment.md
.. mdinclude:: ../../deployment/documentation/integrated_wes_tes.md
Expand All @@ -19,7 +19,7 @@ Package contents

.. toctree::
:maxdepth: 2
:caption: package
:caption: API reference
uniqueg marked this conversation as resolved.
Show resolved Hide resolved

pages/tesk/modules

Expand Down
11 changes: 9 additions & 2 deletions docs/source/pages/tesk/modules.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
tesk
TESK
====

.. toctree::
:maxdepth: 4
:caption: Services (filer and taskmaster)

tesk
tesk.services

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have we altered this? I see the parent model is still tesk?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

segregation in the sidebar of documentation.

.. toctree::
:maxdepth: 4
:caption: API

tesk.api
18 changes: 18 additions & 0 deletions docs/source/pages/tesk/tesk.api.ga4gh.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
tesk.api.ga4gh package
======================

Subpackages

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the difference between submodules and subpackages? Any reason why we have used subpackages here and submodules at all other places?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

autogenerated

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the docs are auto-generated - why do we version control them, then? Does this require you to build the docs before committing (and if so, what if you forget)? Couldn't you just auto-generate them as part of your docs deployment and leave them out of the CI? For packages I wrote and documented their APIs via Sphinx and RtD, I only kept the index.rst and configuration - the pages were then generated by RtD and were not under version control (see FOCA, for example).

Admittedly, the API docs I generated with Sphinx and RtD are pretty shitty 🤣 Still, do we really need these files here?

-----------

.. toctree::
:maxdepth: 4

tesk.api.ga4gh.tes

Module contents
---------------

.. automodule:: tesk.api.ga4gh
:members:
:undoc-members:
:show-inheritance:
21 changes: 21 additions & 0 deletions docs/source/pages/tesk/tesk.api.ga4gh.tes.base.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
tesk.api.ga4gh.tes.base package
===============================

Submodules
----------

tesk.api.ga4gh.tes.base.base\_tesk\_request module
--------------------------------------------------

.. automodule:: tesk.api.ga4gh.tes.base.base_tesk_request
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

.. automodule:: tesk.api.ga4gh.tes.base
:members:
:undoc-members:
:show-inheritance:
29 changes: 29 additions & 0 deletions docs/source/pages/tesk/tesk.api.ga4gh.tes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
tesk.api.ga4gh.tes package
==========================

Subpackages
-----------

.. toctree::
:maxdepth: 4

tesk.api.ga4gh.tes.base

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not include tesk.api.ga4gh.models here?

Copy link
Author

@JaeAeich JaeAeich Jun 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

autogenerated


Submodules
----------

tesk.api.ga4gh.tes.controllers module
-------------------------------------

.. automodule:: tesk.api.ga4gh.tes.controllers
JaeAeich marked this conversation as resolved.
Show resolved Hide resolved
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

.. automodule:: tesk.api.ga4gh.tes
:members:
:undoc-members:
:show-inheritance:
18 changes: 18 additions & 0 deletions docs/source/pages/tesk/tesk.api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
tesk.api package
================

Subpackages
-----------

.. toctree::
:maxdepth: 4

tesk.api.ga4gh

Module contents
---------------

.. automodule:: tesk.api
:members:
:undoc-members:
:show-inheritance:
28 changes: 28 additions & 0 deletions docs/source/pages/tesk/tesk.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,36 @@ Subpackages
.. toctree::
:maxdepth: 4

tesk.api
tesk.services

Submodules
----------

tesk.app module
---------------

.. automodule:: tesk.app
:members:
:undoc-members:
:show-inheritance:

tesk.exceptions module
----------------------

.. automodule:: tesk.exceptions
:members:
:undoc-members:
:show-inheritance:

tesk.tesk\_app module
---------------------

.. automodule:: tesk.tesk_app
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

Expand Down
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ ignore_missing_imports = True
[mypy-connexion.*]
ignore_missing_imports = True

[mypy-foca]
[mypy-foca.*]
uniqueg marked this conversation as resolved.
Show resolved Hide resolved

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember we fixed a few things here? Can you list the issues faced here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from foca.config.config_parser import ConfigParser
This throws:

tesk/tesk_app.py:9: error: Skipping analyzing "foca.config.config_parser": module is installed, but missing library stubs or py.typed marker  [import-untyped]
tesk/tesk_app.py:9: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is an issue that should be solved in a different PR though. Or were these errors a result of code added in this PR exclusively? If so, keep it here.

ignore_missing_imports = True
13 changes: 12 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ boto3-stubs = "^1.34.108"
kubernetes-stubs = "^22.6.0.post1"
mypy = "^1.10.0"
types-botocore = "^1.0.2"
types-pyyaml = "^6.0.12.20240311"
types-requests = "^2.31.0.20240406"
types-urllib3 = "^1.26.25.14"
types-werkzeug = "^1.0.9"
Comment on lines 67 to 71
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel that all of those type packages should rather be added to FOCA, but I guess it's okay for now.

Expand Down
1 change: 1 addition & 0 deletions tesk/api/ga4gh/tes/base/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Package for base class for TESK API request."""
53 changes: 53 additions & 0 deletions tesk/api/ga4gh/tes/base/base_tesk_request.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose you are aware that Connexion validates requests and responses based on the OpenAPI schema without us having to write models? It's basically the main reason we use Connexion.

Admittedly, this predates the time of Pydantic - and I do like a good Pydantic model. However, this all may really not be necessary.

On another note, apart from your doc strings, I don't see anything TES-specific in here...

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Base class for the TES API request."""

from abc import ABC, abstractmethod
from typing import Any, final

from pydantic import BaseModel

from tesk.tesk_app import TeskApp


class BaseTeskRequest(ABC, TeskApp):
"""Base class for the TES API.

This class is an abstract class that defines the common properties and
methods needed by all of the TES API endpoint business logic.

Notes: This initializes the parent class `TeskApp` and will give
all the subclasses access to the configuration object `conf` and
config path.
"""

def __init__(self) -> None:
"""Initializes the BaseTeskRequest class."""
super().__init__()

@abstractmethod
def api_response(self) -> BaseModel:
"""Returns the response as Pydantic model.

Should be implemented by the child class as final
business logic for the specific endpoint.

Returns:
BaseModel: API response for the specific endpoint.

Raises:
Exception: If API response break due to something unhandled.
"""
return BaseModel()

@final
def response(self) -> dict[str, Any]:
"""Returns serialized response.

Should be invoked by controller.

Returns:
dict: Serialized response for the specific endpoint.
"""
_response = self.api_response()
if not isinstance(_response, BaseModel):
raise TypeError('API response must be a Pydantic model.')
return _response.dict()
1 change: 1 addition & 0 deletions tesk/api/ga4gh/tes/models/validators/base/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Package for base class for custom pydantic validators."""
79 changes: 79 additions & 0 deletions tesk/api/ga4gh/tes/models/validators/base/base_validator.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why we don't just use Pydantic's tools for writing validators? What are your concrete use cases here?

Also, note that this may change for the next FOCA version, which will use Pydantic v2 - and validators work quite differently from v1.

Again

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Base validator class, all custom validators must implement it."""

import logging
from abc import ABC, abstractmethod
from typing import Any, Generic, TypeVar

from pydantic import ValidationError

T = TypeVar('T')
logger = logging.getLogger(__name__)


class BaseValidator(ABC, Generic[T]):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need generic here? ABC should be sufficient IMO

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to give type annotations to the value of the field I am getting, because the validator in future can be for a complex datatype as well, in that case it would be good that we can have type hinting of what data str will the methods return.

"""Base custom validator class.

Base validator class for fields in Pydantic models,
if the field is empty, ie None, then it is returned as is without any validation.
otherwise the validation logic is applied to the field.
"""

@property
@abstractmethod
def error_message(self) -> str:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have this enforced and throw exception by default.

"""Returns the error message.

Returns:
str: The error message to be used when validation fails.
"""
pass

@abstractmethod
def validation_logic(self, value: T) -> bool:
"""Validation logic for the field.

Args:
value: The value of the field being validated.

Returns:
bool: True if the validation is successful, False otherwise.
"""
pass

def _raise_error(self, cls: Any, value: T) -> None:
"""Raise a validation error.

Args:
cls: The class being validated.
value: The value of the field being validated.

Raises:
ValidationError: Raised when the validation fails.
"""
logger.error(f'Validation failed for {value} in {cls.__name__}.')
raise ValidationError(
self.error_message,
model=cls,
)

def validate(self, cls: Any, value: T) -> T:
"""Validate the value.

If the value is None, that is the fields is optional
in the model, then it is returned as is without any validation.

Args:
cls: The class being validated.
value: The value of the field being validated.

Returns:
value: The validated value (if valid).

Raises:
ValidationError: If the value is not valid.
"""
if not value:
return value
elif not self.validation_logic(value):
self._raise_error(cls, value)
return value
4 changes: 2 additions & 2 deletions tesk/api/specs/task_execution_service.117cd92.openapi.yaml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do NOT change the OpenAPI file as it's an exact copy of the specs at the indicated commit hash, and we don't want to change it even for style reasons.

Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ paths:
?tag_key=foo1&tag_value=bar1&tag_key=foo2&tag_value=bar2
```
Should be constructed into the structure { "foo1" : "bar1", "foo2" : "bar2"}

```
?tag_key=foo1
```
Expand Down Expand Up @@ -884,4 +884,4 @@ components:
System logs are only included in the FULL task view.
items:
type: string
description: TaskLog describes logging information related to a Task.
description: TaskLog describes logging information related to a Task.
Loading
Loading