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

Implement WOT endpoint caching (GSI-1238) #28

Merged
merged 5 commits into from
Jan 8, 2025
Merged
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 .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ repos:
- id: no-commit-to-branch
args: [--branch, dev, --branch, int, --branch, main]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.3
rev: v0.8.4
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
rev: v1.14.0
hooks:
- id: mypy
args: [--no-warn-unused-ignores]
2 changes: 1 addition & 1 deletion .pyproject_generation/pyproject_custom.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "wps"
version = "2.0.6"
version = "2.0.7"
description = "Work Package Service"
dependencies = [
"ghga-event-schemas~=3.3.1",
Expand Down
2 changes: 2 additions & 0 deletions .pyproject_generation/pyproject_template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ fixable = [
"UP", # e.g. List -> list
"I", # sort imports
"D", # pydocstyle
"RUF022", # sort items in __all__
]
ignore = [
"E111", # indentation with invalid multiple (for formatter)
Expand All @@ -59,6 +60,7 @@ ignore = [
"D206", # indent-with-spaces (for formatter)
"D300", # triple-single-quotes (for formatter)
"UP040", # type statement (not yet supported by mypy)
"PLC0206", # Extracting value from dictionary without calling `.items()`
]
select = [
"C90", # McCabe Complexity
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,21 @@ We recommend using the provided Docker container.

A pre-build version is available at [docker hub](https://hub.docker.com/repository/docker/ghga/work-package-service):
```bash
docker pull ghga/work-package-service:2.0.6
docker pull ghga/work-package-service:2.0.7
```

Or you can build the container yourself from the [`./Dockerfile`](./Dockerfile):
```bash
# Execute in the repo's root dir:
docker build -t ghga/work-package-service:2.0.6 .
docker build -t ghga/work-package-service:2.0.7 .
```

For production-ready deployment, we recommend using Kubernetes, however,
for simple use cases, you could execute the service using docker
on a single server:
```bash
# The entrypoint is preconfigured:
docker run -p 8080:8080 ghga/work-package-service:2.0.6 --help
docker run -p 8080:8080 ghga/work-package-service:2.0.7 --help
```

If you prefer not to use containers, you may install the service from source:
Expand Down
1,346 changes: 675 additions & 671 deletions lock/requirements-dev.txt

Large diffs are not rendered by default.

866 changes: 438 additions & 428 deletions lock/requirements.txt

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ components:
info:
description: A service managing work packages for the GHGA CLI
title: Work Package Service
version: 2.0.6
version: 2.0.7
openapi: 3.1.0
paths:
/health:
Expand Down Expand Up @@ -262,9 +262,7 @@ paths:
'201':
content:
application/json:
schema:
title: Response Create Work Order Token
type: string
schema: {}
description: Work order token has been created.
'403':
description: Not authorized to create the work order token.
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ classifiers = [
"Intended Audience :: Developers",
]
name = "wps"
version = "2.0.6"
version = "2.0.7"
description = "Work Package Service"
dependencies = [
"ghga-event-schemas~=3.3.1",
Expand Down Expand Up @@ -67,6 +67,7 @@ fixable = [
"UP",
"I",
"D",
"RUF022",
]
ignore = [
"E111",
Expand All @@ -86,6 +87,7 @@ ignore = [
"D206",
"D300",
"UP040",
"PLC0206",
]
select = [
"C90",
Expand Down
2 changes: 1 addition & 1 deletion src/wps/adapters/inbound/event_sub.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from wps.core.models import Dataset, DatasetFile, WorkType
from wps.ports.inbound.repository import WorkPackageRepositoryPort

__all__ = ["EventSubTranslatorConfig", "EventSubTranslator"]
__all__ = ["EventSubTranslator", "EventSubTranslatorConfig"]

log = logging.getLogger(__name__)

Expand Down
4 changes: 2 additions & 2 deletions src/wps/adapters/inbound/fastapi_/dummies.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@
from wps.ports.inbound.repository import WorkPackageRepositoryPort

__all__ = [
"auth_provider",
"work_package_repo_port",
"AuthProviderDummy",
"WorkPackageRepositoryDummy",
"auth_provider",
"work_package_repo_port",
]

auth_provider = DependencyDummy("auth_provider")
Expand Down
12 changes: 10 additions & 2 deletions src/wps/adapters/inbound/fastapi_/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
import logging

from fastapi import APIRouter, HTTPException, status
from fastapi.responses import JSONResponse

from wps.adapters.inbound.fastapi_.auth import UserAuthContext, WorkPackageAccessToken
from wps.adapters.inbound.fastapi_.dummies import WorkPackageRepositoryDummy
from wps.constants import WORK_ORDER_TOKEN_VALID_SECONDS
from wps.core.models import (
Dataset,
WorkPackageCreationData,
Expand Down Expand Up @@ -137,17 +139,23 @@ async def create_work_order_token(
file_id: str,
repository: WorkPackageRepositoryDummy,
work_package_access_token: WorkPackageAccessToken,
) -> str:
) -> JSONResponse:
"""Get an encrypted work order token using a work package access token."""
try:
if not (work_package_id and file_id and work_package_access_token):
raise repository.WorkPackageAccessError
return await repository.work_order_token(

wot = await repository.work_order_token(
work_package_id=work_package_id,
file_id=file_id,
check_valid=True,
work_package_access_token=work_package_access_token,
)

cache_control_header = {
"Cache-Control": f"max-age={WORK_ORDER_TOKEN_VALID_SECONDS}, private"
}
return JSONResponse(content=wot, status_code=201, headers=cache_control_header)
except repository.WorkPackageAccessError as error:
raise HTTPException(status_code=403, detail=str(error)) from error

Expand Down
2 changes: 1 addition & 1 deletion src/wps/adapters/outbound/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from wps.ports.outbound.access import AccessCheckPort

__all__ = ["AccessCheckConfig", "AccessCheckAdapter"]
__all__ = ["AccessCheckAdapter", "AccessCheckConfig"]

TIMEOUT = 60

Expand Down
17 changes: 17 additions & 0 deletions src/wps/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2021 - 2024 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
# for the German Human Genome-Phenome Archive (GHGA)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Non-configurable values used in multiple modules"""

WORK_ORDER_TOKEN_VALID_SECONDS = 30
4 changes: 2 additions & 2 deletions src/wps/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@
from wps.core.crypt import validate_public_key

__all__ = [
"WorkType",
"WorkOrderToken",
"WorkPackage",
"WorkPackageCreationData",
"WorkPackageCreationResponse",
"WorkPackageData",
"WorkPackage",
"WorkType",
]


Expand Down
2 changes: 1 addition & 1 deletion src/wps/core/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from ghga_service_commons.utils.jwt_helpers import sign_and_serialize_token
from jwcrypto import jwk

from wps.constants import WORK_ORDER_TOKEN_VALID_SECONDS
from wps.core.models import WorkOrderToken

__all__ = [
Expand All @@ -34,7 +35,6 @@

ACCESS_TOKEN_CHARSET = string.ascii_letters + string.digits
ACCESS_TOKEN_LENGTH = 24
WORK_ORDER_TOKEN_VALID_SECONDS = 30


def generate_work_package_access_token() -> str:
Expand Down
2 changes: 1 addition & 1 deletion src/wps/inject.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from wps.core.repository import WorkPackageRepository
from wps.ports.inbound.repository import WorkPackageRepositoryPort

__all__ = ["prepare_core", "prepare_rest_app", "prepare_consumer", "Consumer"]
__all__ = ["Consumer", "prepare_consumer", "prepare_core", "prepare_rest_app"]


@asynccontextmanager
Expand Down
2 changes: 1 addition & 1 deletion src/wps/ports/outbound/dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from wps.core import models

__all__ = ["DatasetDaoPort", "WorkPackageDaoPort", "ResourceNotFoundError"]
__all__ = ["DatasetDaoPort", "ResourceNotFoundError", "WorkPackageDaoPort"]

# ports described by type aliases:
DatasetDaoPort = DaoNaturalId[models.Dataset]
Expand Down
4 changes: 2 additions & 2 deletions tests/fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@
"AUTH_CLAIMS",
"AUTH_KEY_PAIR",
"SIGNING_KEY_PAIR",
"Consumer",
"fixture_auth_context",
"fixture_auth_headers",
"fixture_bad_auth_headers",
"fixture_client",
"fixture_config",
"fixture_repository",
"fixture_client",
"headers_for_token",
"non_mocked_hosts",
"Consumer",
]

AUTH_KEY_PAIR = generate_jwk()
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from wps.core.models import Dataset, DatasetFile, WorkType

__all__ = ["DATASET", "DATASET_UPSERTION_EVENT", "DATASET_DELETION_EVENT"]
__all__ = ["DATASET", "DATASET_DELETION_EVENT", "DATASET_UPSERTION_EVENT"]


DATASET = Dataset(
Expand Down
5 changes: 5 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from hexkit.providers.mongodb.testutils import MongoDbFixture
from pytest_httpx import HTTPXMock

from wps.constants import WORK_ORDER_TOKEN_VALID_SECONDS

from .fixtures import ( # noqa: F401
SIGNING_KEY_PAIR,
fixture_auth_headers,
Expand Down Expand Up @@ -186,6 +188,9 @@ async def test_create_work_order_token(
headers=headers_for_token(token),
)
assert response.status_code == status.HTTP_201_CREATED
assert "Cache-Control" in response.headers
cache_control = response.headers["Cache-Control"]
assert cache_control == f"max-age={WORK_ORDER_TOKEN_VALID_SECONDS}, private"

wot = response.json()
assert isinstance(wot, str)
Expand Down
Loading