Skip to content

Commit

Permalink
Fix status code for missing IVA ID (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cito authored Jun 17, 2024
1 parent 00c7ec4 commit 1785920
Show file tree
Hide file tree
Showing 19 changed files with 1,421 additions and 1,018 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/devcontainers/python:1-3.12-bullseye
FROM mcr.microsoft.com/devcontainers/python:1-3.12-bookworm

ENV PYTHONUNBUFFERED 1

Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ repos:
- id: no-commit-to-branch
args: [--branch, dev, --branch, int, --branch, main]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.2
rev: v0.4.9
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
8 changes: 4 additions & 4 deletions .pyproject_generation/pyproject_custom.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[project]
name = "ars"
version = "2.0.1"
version = "2.0.2"
description = "Access Request Service"
dependencies = [
"ghga-event-schemas~=3.1.0",
"ghga-service-commons[api,auth]>=3.1.3",
"hexkit[mongodb,akafka]>=3.0.0",
"ghga-event-schemas~=3.3.0",
"ghga-service-commons[api,auth]>=3.1.5",
"hexkit[mongodb,akafka]>=3.2.2",
"httpx>=0.27",
"typer>=0.12",
]
Expand Down
2 changes: 2 additions & 0 deletions .template/static_files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

scripts/script_utils/__init__.py
scripts/script_utils/cli.py
scripts/script_utils/deps.py
scripts/script_utils/lock_deps.py

scripts/__init__.py
scripts/update_all.py
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ We recommend using the provided Docker container.

A pre-build version is available at [docker hub](https://hub.docker.com/repository/docker/ghga/access-request-service):
```bash
docker pull ghga/access-request-service:2.0.1
docker pull ghga/access-request-service:2.0.2
```

Or you can build the container yourself from the [`./Dockerfile`](./Dockerfile):
```bash
# Execute in the repo's root dir:
docker build -t ghga/access-request-service:2.0.1 .
docker build -t ghga/access-request-service:2.0.2 .
```

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/access-request-service:2.0.1 --help
docker run -p 8080:8080 ghga/access-request-service:2.0.2 --help
```

If you prefer not to use containers, you may install the service from source:
Expand Down
2 changes: 1 addition & 1 deletion lock/requirements-dev-template.in
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ setuptools>=69.5
# required since switch to pyproject.toml and pip-tools
tomli_w>=1.0

uv>=0.1.39
uv>=0.1.44
1,276 changes: 742 additions & 534 deletions lock/requirements-dev.txt

Large diffs are not rendered by default.

1,002 changes: 605 additions & 397 deletions lock/requirements.txt

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ components:
info:
description: A service managing access requests for the GHGA Data Portal
title: Access Request Service
version: 2.0.1
version: 2.0.2
openapi: 3.1.0
paths:
/access-requests:
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ classifiers = [
"Intended Audience :: Developers",
]
name = "ars"
version = "2.0.1"
version = "2.0.2"
description = "Access Request Service"
dependencies = [
"ghga-event-schemas~=3.1.0",
"ghga-service-commons[api,auth]>=3.1.3",
"hexkit[mongodb,akafka]>=3.0.0",
"ghga-event-schemas~=3.3.0",
"ghga-service-commons[api,auth]>=3.1.5",
"hexkit[mongodb,akafka]>=3.2.2",
"httpx>=0.27",
"typer>=0.12",
]
Expand Down
60 changes: 28 additions & 32 deletions scripts/check_license.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
".git",
".github",
".flake8",
".gitignore" ".pre-commit-config.yaml",
".gitignore",
".mypy_cache",
".mypy.ini",
".pylintrc",
Expand Down Expand Up @@ -115,8 +115,8 @@
See the License for the specific language governing permissions and
limitations under the License."""

# A list of all chars that may be used to introduce a comment:
COMMENT_CHARS = ["#"]
# A list of strings that may be used to introduce a line comment:
LINE_COMMENTS = ["#"]

AUTHOR = """Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
for the German Human Genome-Phenome Archive (GHGA)"""
Expand Down Expand Up @@ -202,24 +202,25 @@ def get_target_files(
file_
for file_ in all_files
if not (
any([file_.is_relative_to(excl) for excl in exclude_normalized])
or any([str(file_).endswith(ending) for ending in exclude_endings])
or any([re.match(pattern, str(file_)) for pattern in exclude_pattern])
any(file_.is_relative_to(excl) for excl in exclude_normalized)
or any(str(file_).endswith(ending) for ending in exclude_endings)
or any(re.match(pattern, str(file_)) for pattern in exclude_pattern)
)
]
return target_files


def normalized_line(line: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str:
norm_line = line.strip()
def normalized_line(line: str, line_comments: list[str] = LINE_COMMENTS) -> str:
line = line.strip()
for line_comment in line_comments:
line_without_comment = line.removeprefix(line_comment)
if line_without_comment != line:
line = line_without_comment.lstrip()
break
return line

for char in chars_to_trim:
norm_line = norm_line.strip(char)

return norm_line.strip("\n").strip("\t").strip()


def normalized_text(text: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str:
def normalized_text(text: str, line_comments: list[str] = LINE_COMMENTS) -> str:
"Normalize a license header text."
lines = text.split("\n")

Expand All @@ -231,7 +232,7 @@ def normalized_text(text: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str:
if stripped_line.startswith("#!"):
continue

norm_line = normalized_line(stripped_line)
norm_line = normalized_line(stripped_line, line_comments=line_comments)

# exclude empty lines:
if norm_line == "":
Expand All @@ -249,30 +250,25 @@ def format_copyright_template(copyright_template: str, author: str) -> str:
return normalized_text(copyright_template.replace("{author}", author))


def is_commented_line(line: str, comment_chars: list[str] = COMMENT_CHARS) -> bool:
def is_commented_line(line: str, line_comments: list[str] = LINE_COMMENTS) -> bool:
"""Checks whether a line is a comment."""
line_stripped = line.strip()
for comment_char in comment_chars:
if line_stripped.startswith(comment_char):
return True

return False
return line.lstrip().startswith(tuple(line_comments))


def is_empty_line(line: str) -> bool:
"""Checks whether a line is empty."""
return line.strip("\n").strip("\t").strip() == ""
return not line.strip()


def get_header(file_path: Path, comment_chars: list[str] = COMMENT_CHARS):
def get_header(file_path: Path, line_comments: list[str] = LINE_COMMENTS):
"""Extracts the header from a file and normalizes it."""
header_lines: list[str] = []

try:
with open(file_path) as file:
for line in file:
if is_commented_line(
line, comment_chars=comment_chars
line, line_comments=line_comments
) or is_empty_line(line):
header_lines.append(line)
else:
Expand All @@ -282,7 +278,7 @@ def get_header(file_path: Path, comment_chars: list[str] = COMMENT_CHARS):

# normalize the lines:
header = "".join(header_lines)
return normalized_text(header, chars_to_trim=comment_chars)
return normalized_text(header, line_comments=line_comments)


def validate_year_string(year_string: str, min_year: int = MIN_YEAR) -> bool:
Expand Down Expand Up @@ -317,7 +313,7 @@ def check_copyright_notice(
global_copyright: GlobalCopyrightNotice,
copyright_template: str = COPYRIGHT_TEMPLATE,
author: str = AUTHOR,
comment_chars: list[str] = COMMENT_CHARS,
line_comments: list[str] = LINE_COMMENTS,
min_year: int = MIN_YEAR,
) -> bool:
"""Checks the specified copyright text against a template.
Expand Down Expand Up @@ -385,7 +381,7 @@ def check_file_headers(
exclude: list[str] = EXCLUDE,
exclude_endings: list[str] = EXCLUDE_ENDINGS,
exclude_pattern: list[str] = EXCLUDE_PATTERN,
comment_chars: list[str] = COMMENT_CHARS,
line_comments: list[str] = LINE_COMMENTS,
min_year: int = MIN_YEAR,
) -> tuple[list[Path], list[Path]]:
"""Check files for presence of a license header and verify that
Expand Down Expand Up @@ -429,13 +425,13 @@ def check_file_headers(

for target_file in target_files:
try:
header = get_header(target_file, comment_chars=comment_chars)
header = get_header(target_file, line_comments=line_comments)
if check_copyright_notice(
copyright=header,
global_copyright=global_copyright,
copyright_template=copyright_template,
author=author,
comment_chars=comment_chars,
line_comments=line_comments,
min_year=min_year,
):
passed_files.append(target_file)
Expand All @@ -453,7 +449,7 @@ def check_license_file(
global_copyright: GlobalCopyrightNotice,
copyright_template: str = COPYRIGHT_TEMPLATE,
author: str = AUTHOR,
comment_chars: list[str] = COMMENT_CHARS,
line_comments: list[str] = LINE_COMMENTS,
min_year: int = MIN_YEAR,
) -> bool:
"""Currently only checks if the copyright notice in the
Expand Down Expand Up @@ -495,7 +491,7 @@ def check_license_file(
global_copyright=global_copyright,
copyright_template=copyright_template,
author=author,
comment_chars=comment_chars,
line_comments=line_comments,
min_year=min_year,
)

Expand Down
4 changes: 2 additions & 2 deletions scripts/script_utils/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
#
"""Contains utils for working with dependencies, lock files, etc."""

import tomllib
from copy import deepcopy
from pathlib import Path
from typing import Any

import stringcase
import tomli


def exclude_from_dependency_list(*, package_name: str, dependencies: list) -> list:
Expand Down Expand Up @@ -69,7 +69,7 @@ def remove_self_dependencies(pyproject: dict) -> dict:
def get_modified_pyproject(pyproject_toml_path: Path) -> dict[str, Any]:
"""Get a copy of pyproject.toml with any self-referencing dependencies removed."""
with open(pyproject_toml_path, "rb") as pyproject_toml:
pyproject = tomli.load(pyproject_toml)
pyproject = tomllib.load(pyproject_toml)

modified_pyproject = remove_self_dependencies(pyproject)
return modified_pyproject
5 changes: 4 additions & 1 deletion src/ars/adapters/inbound/fastapi_/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ async def patch_access_request(
raise HTTPException(status_code=403, detail=str(exc)) from exc
except repository.AccessRequestNotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
except repository.AccessRequestInvalidState as exc:
except (
repository.AccessRequestInvalidState,
repository.AccessRequestMissingIva,
) as exc:
raise HTTPException(status_code=422, detail=str(exc)) from exc
except Exception as exc:
log.error("Could not modify access request: %s", exc)
Expand Down
29 changes: 10 additions & 19 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,13 @@

"""Setup for testing the access request service."""

import pytest
from hexkit.providers.akafka.testutils import get_kafka_fixture
from hexkit.providers.mongodb.testutils import get_mongodb_fixture

from .fixtures import JointFixture, get_joint_fixture


@pytest.fixture(autouse=True)
def reset_state(joint_fixture: JointFixture):
"""Clear joint_fixture state before tests.
This is a function-level fixture because it needs to run in each test.
"""
joint_fixture.mongodb.empty_collections()


kafka_fixture = get_kafka_fixture("session")
mongodb_fixture = get_mongodb_fixture("session")
joint_fixture = get_joint_fixture("session")
from hexkit.providers.akafka.testutils import ( # noqa: F401
kafka_container_fixture,
kafka_fixture,
)
from hexkit.providers.mongodb.testutils import ( # noqa: F401
mongodb_container_fixture,
mongodb_fixture,
)

from .fixtures import joint_fixture # noqa: F401
21 changes: 8 additions & 13 deletions tests/fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
generate_jwk,
sign_and_serialize_token,
)
from hexkit.custom_types import PytestScope
from hexkit.providers.akafka.testutils import KafkaFixture
from hexkit.providers.mongodb.testutils import MongoDbFixture
from pytest import fixture
Expand All @@ -38,7 +37,7 @@
"AUTH_CLAIMS_STEWARD",
"fixture_auth_headers_doe",
"fixture_auth_headers_steward",
"get_joint_fixture",
"joint_fixture",
"JointFixture",
"headers_for_token",
]
Expand Down Expand Up @@ -91,8 +90,9 @@ class JointFixture(NamedTuple):
rest_client: AsyncTestClient


async def joint_fixture_function(
mongodb_fixture: MongoDbFixture, kafka_fixture: KafkaFixture
@pytest_asyncio.fixture()
async def joint_fixture(
mongodb: MongoDbFixture, kafka: KafkaFixture
) -> AsyncGenerator[JointFixture, None]:
"""A fixture that embeds all other fixtures for API-level integration testing
Expand All @@ -101,8 +101,8 @@ async def joint_fixture_function(
config = Config(
auth_key=AUTH_KEY_PAIR.export_public(), # pyright: ignore
download_access_url="http://access",
**kafka_fixture.config.model_dump(),
**mongodb_fixture.config.model_dump(),
**kafka.config.model_dump(),
**mongodb.config.model_dump(),
)
async with prepare_core(config=config) as core:
async with (
Expand All @@ -111,12 +111,7 @@ async def joint_fixture_function(
async with AsyncTestClient(app=app) as rest_client:
yield JointFixture(
config=config,
kafka=kafka_fixture,
mongodb=mongodb_fixture,
kafka=kafka,
mongodb=mongodb,
rest_client=rest_client,
)


def get_joint_fixture(scope: PytestScope = "function"):
"""Produce a joint fixture with desired scope"""
return pytest_asyncio.fixture(joint_fixture_function, scope=scope)
2 changes: 1 addition & 1 deletion tests/test_access_grants.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

from ars.adapters.outbound.http import AccessGrantsAdapter, AccessGrantsConfig

pytestmark = pytest.mark.asyncio(scope="session")
pytestmark = pytest.mark.asyncio()


DOWNLOAD_ACCESS_URL = "http://test-access:1234"
Expand Down
Loading

0 comments on commit 1785920

Please sign in to comment.