diff --git a/.github/codecov.yml b/.github/codecov.yml index f9218315ed..a19a62a034 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -15,9 +15,6 @@ coverage: flags: - sdk flags: - dibbs: - paths: - - "containers/dibbs" ecr-viewer: paths: - "containers/ecr-viewer" diff --git a/.github/workflows/container-dibbs.yaml b/.github/workflows/container-dibbs.yaml deleted file mode 100644 index ce91e9b6a1..0000000000 --- a/.github/workflows/container-dibbs.yaml +++ /dev/null @@ -1,83 +0,0 @@ -name: DIBBs Container Workflow - -on: - pull_request: - branches: - - "**" - paths: - - containers/dibbs/** - merge_group: - types: - - checks_requested - push: - branches: - - main - paths-ignore: - - pyproject.toml - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - TEST_RUNNER_PYTHON_VERSION: 3.11 - CONTAINER: dibbs - -jobs: - python-linting: - uses: ./.github/workflows/linting-python.yaml - with: - python_runner_version: 3.11 # must be hardcoded; cannot pass in from env - - unit-test-python-containers: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup python ${{env.TEST_RUNNER_PYTHON_VERSION}} - uses: actions/setup-python@v5 - with: - python-version: ${{env.TEST_RUNNER_PYTHON_VERSION}} - cache: pip - - - name: Install pytest and pytest-cov - run: pip install pytest pytest-cov - - - name: Install dependencies - working-directory: ./containers/${{env.CONTAINER}} - run: | - pip install . - pip install -r requirements.txt - if [ -f dev-requirements.txt ]; then - pip install -r dev-requirements.txt - fi - - - name: Run unit tests for container with coverage - working-directory: ./containers/${{env.CONTAINER}} - run: | - python -m pytest --cov-report xml --cov=. -m "not integration" tests/ - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: ${{ env.CONTAINER }} - - build-container: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build ${{ env.CONTAINER }} Container - uses: docker/build-push-action@v3 - with: - context: ./containers/${{ env.CONTAINER }} - push: false - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/container-ingestion.yaml b/.github/workflows/container-ingestion.yaml index 5386e8ab42..83c63210f8 100644 --- a/.github/workflows/container-ingestion.yaml +++ b/.github/workflows/container-ingestion.yaml @@ -6,7 +6,6 @@ on: - "**" paths: - containers/ingestion/** - - containers/dibbs/** - containers/fhir-converter/** merge_group: types: diff --git a/.github/workflows/container-message-parser.yaml b/.github/workflows/container-message-parser.yaml index 269366b4c4..cbed15ebd0 100644 --- a/.github/workflows/container-message-parser.yaml +++ b/.github/workflows/container-message-parser.yaml @@ -6,7 +6,6 @@ on: - "**" paths: - containers/message-parser/** - - containers/dibbs/** - containers/fhir-converter/** merge_group: types: diff --git a/.github/workflows/container-message-refiner.yaml b/.github/workflows/container-message-refiner.yaml index 508d65b89f..56c0a41fe1 100644 --- a/.github/workflows/container-message-refiner.yaml +++ b/.github/workflows/container-message-refiner.yaml @@ -6,7 +6,6 @@ on: - "**" paths: - containers/message-refiner/** - - containers/dibbs/** - containers/fhir-converter/** merge_group: types: diff --git a/.github/workflows/container-record-linkage.yaml b/.github/workflows/container-record-linkage.yaml index 80c687ed45..e554bb0ec7 100644 --- a/.github/workflows/container-record-linkage.yaml +++ b/.github/workflows/container-record-linkage.yaml @@ -6,7 +6,6 @@ on: - "**" paths: - containers/record-linkage/** - - containers/dibbs/** merge_group: types: - checks_requested diff --git a/.github/workflows/container-trigger-code-reference.yaml b/.github/workflows/container-trigger-code-reference.yaml index 8e6db6ae7b..688c155fcb 100644 --- a/.github/workflows/container-trigger-code-reference.yaml +++ b/.github/workflows/container-trigger-code-reference.yaml @@ -6,7 +6,6 @@ on: - "**" paths: - containers/trigger-code-reference/** - - containers/dibbs/** merge_group: types: - checks_requested diff --git a/.github/workflows/container-validation.yaml b/.github/workflows/container-validation.yaml index 46b3f3de46..9eb3d03ada 100644 --- a/.github/workflows/container-validation.yaml +++ b/.github/workflows/container-validation.yaml @@ -6,7 +6,6 @@ on: - "**" paths: - containers/validation/** - - containers/dibbs/** - containers/fhir-converter/** merge_group: types: diff --git a/containers/dibbs/Dockerfile b/containers/dibbs/Dockerfile deleted file mode 100644 index 6609ea2856..0000000000 --- a/containers/dibbs/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM python:3.11-slim - -RUN apt-get update && \ - apt-get upgrade -y - -RUN pip install --upgrade pip - -COPY . /dibbs -RUN pip install /dibbs \ No newline at end of file diff --git a/containers/dibbs/README.md b/containers/dibbs/README.md deleted file mode 100644 index bd8f9e2202..0000000000 --- a/containers/dibbs/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# The dibbs Package - -This package contains additional functionality that supports building containerized REST web services with functions from other packages. \ No newline at end of file diff --git a/containers/dibbs/dev-requirements.txt b/containers/dibbs/dev-requirements.txt deleted file mode 100644 index b610ae55c9..0000000000 --- a/containers/dibbs/dev-requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytest -httpx \ No newline at end of file diff --git a/containers/dibbs/dibbs/__init__.py b/containers/dibbs/dibbs/__init__.py deleted file mode 100644 index 3dc1f76bc6..0000000000 --- a/containers/dibbs/dibbs/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.1.0" diff --git a/containers/dibbs/dibbs/utils.py b/containers/dibbs/dibbs/utils.py deleted file mode 100644 index bf0df0cb49..0000000000 --- a/containers/dibbs/dibbs/utils.py +++ /dev/null @@ -1,51 +0,0 @@ -import json -import pathlib - - -def read_json_from_assets(filename: str) -> dict: - """ - Reads a JSON file from the assets directory. - - :param filename: The name of the file to read. - :return: A dictionary containing the contents of the file. - """ - return json.load(open((pathlib.Path(__file__).parent.parent / "assets" / filename))) - - -def read_json_from_test_assets(filename: str) -> dict: - """ - Reads a JSON file from the assets directory. - - :param filename: The name of the file to read. - :return: A dictionary containing the contents of the file. - """ - return json.load( - open((pathlib.Path(__file__).parent.parent / "tests" / "assets" / filename)) - ) - - -def read_file_from_assets(filename: str) -> str: - """ - Reads a file from the assets directory. - - :param filename: The name of the file to read. - :return: A string containing the contents of the file. - """ - with open( - (pathlib.Path(__file__).parent.parent / "assets" / filename), "r" - ) as file: - return file.read() - - -def read_file_from_test_assets(filename: str) -> str: - """ - Reads a file from the assets directory. - - :param filename: The name of the file to read. - :return: A string containing the contents of the file. - """ - with open( - (pathlib.Path(__file__).parent.parent / "tests" / "assets" / filename), - "r", - ) as file: - return file.read() diff --git a/containers/dibbs/pyproject.toml b/containers/dibbs/pyproject.toml deleted file mode 100644 index 90fd9dda30..0000000000 --- a/containers/dibbs/pyproject.toml +++ /dev/null @@ -1,9 +0,0 @@ -[build-system] -requires = ["setuptools>=40.6.0", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "dibbs" -version = "0.1.0" -description = "A small library of tools shared across the DIBBS project" -dependencies = ["pydantic==1.10.13", "fastapi>=0.109.1", "uvicorn"] diff --git a/containers/dibbs/requirements.txt b/containers/dibbs/requirements.txt deleted file mode 100644 index f3083e6fbc..0000000000 --- a/containers/dibbs/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pydantic==1.10.13 -fastapi>=0.109.1 -uvicorn -httpx \ No newline at end of file diff --git a/containers/dibbs/tests/assets/test_description.md b/containers/dibbs/tests/assets/test_description.md deleted file mode 100644 index afed4a58ee..0000000000 --- a/containers/dibbs/tests/assets/test_description.md +++ /dev/null @@ -1 +0,0 @@ -This is a test description. \ No newline at end of file diff --git a/containers/dibbs/tests/test_base_service.py b/containers/dibbs/tests/test_base_service.py deleted file mode 100644 index 31f513ab53..0000000000 --- a/containers/dibbs/tests/test_base_service.py +++ /dev/null @@ -1,61 +0,0 @@ -from importlib import metadata -from pathlib import Path - -from dibbs.base_service import BaseService -from dibbs.base_service import DIBBS_CONTACT -from dibbs.base_service import LICENSES -from fastapi.testclient import TestClient - -default_app_version = metadata.version("dibbs") - - -def test_base_service(): - service = BaseService( - service_name="Test Service", - service_path="/test-service", - description_path=Path(__file__).parent / "assets" / "test_description.md", - ) - assert service.app.title == "Test Service" - assert service.app.version == default_app_version - assert service.app.contact == DIBBS_CONTACT - assert service.app.license_info == LICENSES["CreativeCommonsZero"] - assert service.app.description == "This is a test description." - assert service.app.openapi_url == "/openapi.json" - - client = TestClient(service.start()) - - # Test the health check endpoint - response = client.get("/") - assert response.status_code == 200 - assert response.json() == {"status": "OK"} - - # Test the path rewrite middleware - response = client.get("/test-service") - assert response.status_code == 200 - assert response.json() == {"status": "OK"} - - # Test the redoc endpoint - response = client.get("/redoc") - assert response.status_code == 200 - - -def test_base_service_alternate_license(): - service = BaseService( - service_name="Test Service", - service_path="/test-service", - description_path=Path(__file__).parent / "assets" / "test_description.md", - license_info="MIT", - ) - assert service.app.title == "Test Service" - assert service.app.version == default_app_version - assert service.app.contact == DIBBS_CONTACT - assert service.app.license_info == LICENSES["MIT"] - assert service.app.description == "This is a test description." - - client = TestClient(service.start()) - response = client.get("/") - assert response.status_code == 200 - assert response.json() == {"status": "OK"} - - response = client.get("/redoc") - assert response.status_code == 200 diff --git a/containers/ecr-viewer/package.json b/containers/ecr-viewer/package.json index db128f3fd4..ad3c43f07f 100644 --- a/containers/ecr-viewer/package.json +++ b/containers/ecr-viewer/package.json @@ -12,6 +12,7 @@ "lint": "next lint", "test": "TZ=America/New_York jest", "test:watch": "TZ=America/New_York jest --watch", + "clear-local": "docker compose -f ./docker-compose.yml --profile \"*\" down -v", "convert-seed-data": "npx @dotenvx/dotenvx run -f .env.local -- sh -c 'docker compose -f ./docker-compose.yml --profile ${SOURCE} --profile ${METADATA_DATABASE_TYPE} --profile ecr-viewer up -d && docker compose -f ./seed-scripts/docker-compose-seed.yml up --abort-on-container-exit && docker compose -f ./docker-compose.yml --profile \"*\" down && docker compose -f ./seed-scripts/docker-compose-seed.yml --profile \"*\" down'", "convert-seed-data:build": "npx @dotenvx/dotenvx run -f .env.local -- sh -c 'docker compose -f ./docker-compose.yml --profile ${SOURCE} --profile ${METADATA_DATABASE_TYPE} --profile ecr-viewer up -d --build && docker compose -f ./seed-scripts/docker-compose-seed.yml up --abort-on-container-exit --build && docker compose -f ./docker-compose.yml --profile \"*\" down && docker compose -f ./seed-scripts/docker-compose-seed.yml --profile \"*\" down'", "update-cypress-data": "docker compose -f ./seed-scripts/docker-compose-create-sql.yml up --build --abort-on-container-exit", diff --git a/containers/ingestion/Dockerfile b/containers/ingestion/Dockerfile index b10a0a8313..6556b9269a 100644 --- a/containers/ingestion/Dockerfile +++ b/containers/ingestion/Dockerfile @@ -1,4 +1,9 @@ -FROM ghcr.io/cdcgov/phdi/dibbs +FROM python:3.11-slim + +RUN apt-get update && \ + apt-get upgrade -y + +RUN pip install --upgrade pip WORKDIR /code diff --git a/containers/dibbs/dibbs/base_service.py b/containers/ingestion/app/base_service.py similarity index 98% rename from containers/dibbs/dibbs/base_service.py rename to containers/ingestion/app/base_service.py index 5181dd60cd..a30ddd4754 100644 --- a/containers/dibbs/dibbs/base_service.py +++ b/containers/ingestion/app/base_service.py @@ -1,4 +1,3 @@ -from importlib import metadata from pathlib import Path from typing import Literal @@ -69,7 +68,7 @@ def __init__( self.include_health_check_endpoint = include_health_check_endpoint self.app = FastAPI( title=service_name, - version=metadata.version("dibbs"), + version="1.7.1", contact=DIBBS_CONTACT, license_info=LICENSES[license_info], description=description, diff --git a/containers/ingestion/app/main.py b/containers/ingestion/app/main.py index 5cf2ea13c4..b0f038515e 100644 --- a/containers/ingestion/app/main.py +++ b/containers/ingestion/app/main.py @@ -1,7 +1,6 @@ from pathlib import Path -from dibbs.base_service import BaseService - +from app.base_service import BaseService from app.config import get_settings from app.routers import cloud_storage from app.routers import fhir_geospatial diff --git a/containers/ingestion/dev-requirements.txt b/containers/ingestion/dev-requirements.txt index 8e8ba2d9da..7cf4453c19 100644 --- a/containers/ingestion/dev-requirements.txt +++ b/containers/ingestion/dev-requirements.txt @@ -1,3 +1,4 @@ pytest rich -testcontainers[compose]==3.7.1 \ No newline at end of file +testcontainers[compose]==3.7.1 +toml \ No newline at end of file diff --git a/containers/ingestion/requirements.txt b/containers/ingestion/requirements.txt index 2810092e4e..185d2d3061 100644 --- a/containers/ingestion/requirements.txt +++ b/containers/ingestion/requirements.txt @@ -1,4 +1,6 @@ -../dibbs +pydantic==1.10.13 +fastapi>=0.109.1 +uvicorn azure-identity azure-keyvault-secrets azure-storage-blob diff --git a/containers/ingestion/tests/test_base_service.py b/containers/ingestion/tests/test_base_service.py index 4d52f87faa..895d33d5ab 100644 --- a/containers/ingestion/tests/test_base_service.py +++ b/containers/ingestion/tests/test_base_service.py @@ -1,12 +1,16 @@ -from importlib import metadata from pathlib import Path -from dibbs.base_service import BaseService -from dibbs.base_service import DIBBS_CONTACT -from dibbs.base_service import LICENSES +import toml +from app.base_service import BaseService +from app.base_service import DIBBS_CONTACT +from app.base_service import LICENSES from fastapi.testclient import TestClient -default_app_version = metadata.version("dibbs") +with open( + Path(__file__).parent.parent.parent.parent / "pyproject.toml" +) as project_config_file: + project_config = toml.load(project_config_file) + default_app_version = project_config["tool"]["poetry"]["version"][1:] def test_base_service(): diff --git a/containers/message-parser/Dockerfile b/containers/message-parser/Dockerfile index 4293116e54..bc970e3f05 100644 --- a/containers/message-parser/Dockerfile +++ b/containers/message-parser/Dockerfile @@ -1,4 +1,9 @@ -FROM ghcr.io/cdcgov/phdi/dibbs +FROM python:3.11-slim + +RUN apt-get update && \ + apt-get upgrade -y + +RUN pip install --upgrade pip WORKDIR /code diff --git a/containers/message-parser/app/base_service.py b/containers/message-parser/app/base_service.py new file mode 100644 index 0000000000..a30ddd4754 --- /dev/null +++ b/containers/message-parser/app/base_service.py @@ -0,0 +1,129 @@ +from pathlib import Path +from typing import Literal + +from fastapi import FastAPI +from fastapi import Request +from fastapi import Response +from pydantic import BaseModel + +# create a class with the DIBBs default Creative Commons Zero v1.0 and +# MIT license to be used by the BaseService class +LICENSES = { + "CreativeCommonsZero": { + "name": "Creative Commons Zero v1.0 Universal", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + }, + "MIT": {"name": "The MIT License", "url": "https://mit-license.org/"}, +} + +DIBBS_CONTACT = { + "name": "CDC Public Health Data Infrastructure", + "url": "https://cdcgov.github.io/dibbs-site/", + "email": "dibbs@cdc.gov", +} + + +STATUS_OK = {"status": "OK"} + + +class StatusResponse(BaseModel): + """ + The schema for the response from the health check endpoint. + """ + + status: Literal["OK"] + + +class BaseService: + def __init__( + self, + service_name: str, + service_path: str, + description_path: str, + include_health_check_endpoint: bool = True, + license_info: Literal["CreativeCommonsZero", "MIT"] = "CreativeCommonsZero", + openapi_url: str = "/openapi.json", + ): + """ + Initialize a BaseService instance. + + :param service_name: The name of the service. + :param service_path: The path to used to access the service from a gateway. + :param description_path: The path to a markdown file containing a description of + the service. + :param include_health_check_endpoint: If True, the standard DIBBs health check + endpoint will be added. + :param license_info: If empty, the standard DIBBs Creative Commons Zero v1.0 + Universal license will be used. The other available option is to use the + MIT license. + :param openapi_url: Optionally, the url of the OpenAPI.json file that FastAPI + should use when auto-constructing docs at the `/redoc` endpoint. For + services deployed in public environments managed by application gateways + or other routers, this parameter should be set to + "/{service-name}/openapi.json". If omitted, the parameter is set to the + FastAPI default. + """ + description = Path(description_path).read_text(encoding="utf-8") + self.service_path = service_path + self.include_health_check_endpoint = include_health_check_endpoint + self.app = FastAPI( + title=service_name, + version="1.7.1", + contact=DIBBS_CONTACT, + license_info=LICENSES[license_info], + description=description, + openapi_url=openapi_url, + ) + + """ + Because this is a reusable class, middlewares and endpoints need to be + added after the class is instantiated. This is done by calling the start + method, which in turn calls the add_path_rewrite_middleware and + add_health_check_endpoint methods. + """ + + def add_path_rewrite_middleware(self): + """ + Add middleware to the FastAPI instance to strip the service_path + from the URL path if it is present. This is useful when the service + is behind a gateway that is using a path-based routing strategy. + """ + + @self.app.middleware("http") + async def rewrite_path(request: Request, call_next: callable) -> Response: + if ( + request.url.path.startswith(self.service_path) + and "openapi.json" not in request.url.path + ): + request.scope["path"] = request.scope["path"].replace( + self.service_path, "" + ) + if request.scope["path"] == "": + request.scope["path"] = "/" + return await call_next(request) + + def add_health_check_endpoint(self): + """ + Adds a health check endpoint to the web service. + """ + + @self.app.get("/") + async def health_check() -> StatusResponse: + """ + This endpoint checks service status. If an HTTP 200 status code is returned along with + '{"status": "OK"}' then the service is available and running properly. + """ + return STATUS_OK + + def start(self) -> FastAPI: + """ + Return a FastAPI instance with DIBBs metadata set. If + `include_health_check_endpoint` is True, then the health check endpoint + will be added. + + :return: The FastAPI instance. + """ + self.add_path_rewrite_middleware() + if self.include_health_check_endpoint: + self.add_health_check_endpoint() + return self.app diff --git a/containers/message-parser/app/main.py b/containers/message-parser/app/main.py index 60ee1497d9..8db98bfbe8 100644 --- a/containers/message-parser/app/main.py +++ b/containers/message-parser/app/main.py @@ -3,11 +3,11 @@ from pathlib import Path from typing import Annotated -from dibbs.base_service import BaseService from fastapi import Body from fastapi import Response from fastapi import status +from app.base_service import BaseService from app.config import get_settings from app.models import FhirToPhdcInput from app.models import GetSchemaResponse diff --git a/containers/message-parser/dev-requirements.txt b/containers/message-parser/dev-requirements.txt index 8ff99f31e2..7cf4453c19 100644 --- a/containers/message-parser/dev-requirements.txt +++ b/containers/message-parser/dev-requirements.txt @@ -1,3 +1,4 @@ pytest rich testcontainers[compose]==3.7.1 +toml \ No newline at end of file diff --git a/containers/message-parser/requirements.txt b/containers/message-parser/requirements.txt index d21cd3041d..4bf440a7a7 100644 --- a/containers/message-parser/requirements.txt +++ b/containers/message-parser/requirements.txt @@ -1,4 +1,6 @@ -../dibbs +pydantic==1.10.13 +fastapi>=0.109.1 +uvicorn azure-identity azure-keyvault-secrets azure-storage-blob diff --git a/containers/message-parser/tests/test_base_service.py b/containers/message-parser/tests/test_base_service.py index 4d52f87faa..895d33d5ab 100644 --- a/containers/message-parser/tests/test_base_service.py +++ b/containers/message-parser/tests/test_base_service.py @@ -1,12 +1,16 @@ -from importlib import metadata from pathlib import Path -from dibbs.base_service import BaseService -from dibbs.base_service import DIBBS_CONTACT -from dibbs.base_service import LICENSES +import toml +from app.base_service import BaseService +from app.base_service import DIBBS_CONTACT +from app.base_service import LICENSES from fastapi.testclient import TestClient -default_app_version = metadata.version("dibbs") +with open( + Path(__file__).parent.parent.parent.parent / "pyproject.toml" +) as project_config_file: + project_config = toml.load(project_config_file) + default_app_version = project_config["tool"]["poetry"]["version"][1:] def test_base_service(): diff --git a/containers/message-refiner/Dockerfile b/containers/message-refiner/Dockerfile index e2fbce9a25..f408923d70 100644 --- a/containers/message-refiner/Dockerfile +++ b/containers/message-refiner/Dockerfile @@ -1,4 +1,9 @@ -FROM ghcr.io/cdcgov/phdi/dibbs +FROM python:3.11-slim + +RUN apt-get update && \ + apt-get upgrade -y + +RUN pip install --upgrade pip WORKDIR /code diff --git a/containers/message-refiner/app/base_service.py b/containers/message-refiner/app/base_service.py new file mode 100644 index 0000000000..a30ddd4754 --- /dev/null +++ b/containers/message-refiner/app/base_service.py @@ -0,0 +1,129 @@ +from pathlib import Path +from typing import Literal + +from fastapi import FastAPI +from fastapi import Request +from fastapi import Response +from pydantic import BaseModel + +# create a class with the DIBBs default Creative Commons Zero v1.0 and +# MIT license to be used by the BaseService class +LICENSES = { + "CreativeCommonsZero": { + "name": "Creative Commons Zero v1.0 Universal", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + }, + "MIT": {"name": "The MIT License", "url": "https://mit-license.org/"}, +} + +DIBBS_CONTACT = { + "name": "CDC Public Health Data Infrastructure", + "url": "https://cdcgov.github.io/dibbs-site/", + "email": "dibbs@cdc.gov", +} + + +STATUS_OK = {"status": "OK"} + + +class StatusResponse(BaseModel): + """ + The schema for the response from the health check endpoint. + """ + + status: Literal["OK"] + + +class BaseService: + def __init__( + self, + service_name: str, + service_path: str, + description_path: str, + include_health_check_endpoint: bool = True, + license_info: Literal["CreativeCommonsZero", "MIT"] = "CreativeCommonsZero", + openapi_url: str = "/openapi.json", + ): + """ + Initialize a BaseService instance. + + :param service_name: The name of the service. + :param service_path: The path to used to access the service from a gateway. + :param description_path: The path to a markdown file containing a description of + the service. + :param include_health_check_endpoint: If True, the standard DIBBs health check + endpoint will be added. + :param license_info: If empty, the standard DIBBs Creative Commons Zero v1.0 + Universal license will be used. The other available option is to use the + MIT license. + :param openapi_url: Optionally, the url of the OpenAPI.json file that FastAPI + should use when auto-constructing docs at the `/redoc` endpoint. For + services deployed in public environments managed by application gateways + or other routers, this parameter should be set to + "/{service-name}/openapi.json". If omitted, the parameter is set to the + FastAPI default. + """ + description = Path(description_path).read_text(encoding="utf-8") + self.service_path = service_path + self.include_health_check_endpoint = include_health_check_endpoint + self.app = FastAPI( + title=service_name, + version="1.7.1", + contact=DIBBS_CONTACT, + license_info=LICENSES[license_info], + description=description, + openapi_url=openapi_url, + ) + + """ + Because this is a reusable class, middlewares and endpoints need to be + added after the class is instantiated. This is done by calling the start + method, which in turn calls the add_path_rewrite_middleware and + add_health_check_endpoint methods. + """ + + def add_path_rewrite_middleware(self): + """ + Add middleware to the FastAPI instance to strip the service_path + from the URL path if it is present. This is useful when the service + is behind a gateway that is using a path-based routing strategy. + """ + + @self.app.middleware("http") + async def rewrite_path(request: Request, call_next: callable) -> Response: + if ( + request.url.path.startswith(self.service_path) + and "openapi.json" not in request.url.path + ): + request.scope["path"] = request.scope["path"].replace( + self.service_path, "" + ) + if request.scope["path"] == "": + request.scope["path"] = "/" + return await call_next(request) + + def add_health_check_endpoint(self): + """ + Adds a health check endpoint to the web service. + """ + + @self.app.get("/") + async def health_check() -> StatusResponse: + """ + This endpoint checks service status. If an HTTP 200 status code is returned along with + '{"status": "OK"}' then the service is available and running properly. + """ + return STATUS_OK + + def start(self) -> FastAPI: + """ + Return a FastAPI instance with DIBBs metadata set. If + `include_health_check_endpoint` is True, then the health check endpoint + will be added. + + :return: The FastAPI instance. + """ + self.add_path_rewrite_middleware() + if self.include_health_check_endpoint: + self.add_health_check_endpoint() + return self.app diff --git a/containers/message-refiner/app/main.py b/containers/message-refiner/app/main.py index b8617357d4..c98df6ed6e 100644 --- a/containers/message-refiner/app/main.py +++ b/containers/message-refiner/app/main.py @@ -2,7 +2,6 @@ from typing import Annotated import httpx -from dibbs.base_service import BaseService from fastapi import Query from fastapi import Request from fastapi import Response @@ -10,6 +9,7 @@ from fastapi.openapi.utils import get_openapi from fastapi.responses import FileResponse +from app.base_service import BaseService from app.config import get_settings from app.models import RefineECRResponse from app.refine import refine diff --git a/containers/message-refiner/requirements.txt b/containers/message-refiner/requirements.txt index 2eabb1dfa2..6651985147 100644 --- a/containers/message-refiner/requirements.txt +++ b/containers/message-refiner/requirements.txt @@ -1,4 +1,6 @@ -../dibbs +pydantic==1.10.13 +fastapi>=0.109.1 +uvicorn httpx lxml pathlib diff --git a/containers/orchestration/Dockerfile b/containers/orchestration/Dockerfile index d33ee590ab..5855448be0 100644 --- a/containers/orchestration/Dockerfile +++ b/containers/orchestration/Dockerfile @@ -4,7 +4,12 @@ ARG TRIGGER_CODE_REFERENCE_URL ARG SMARTY_AUTH_ID ARG SMARTY_AUTH_TOKEN -FROM ghcr.io/cdcgov/phdi/dibbs +FROM python:3.11-slim + +RUN apt-get update && \ + apt-get upgrade -y + +RUN pip install --upgrade pip WORKDIR /code diff --git a/containers/orchestration/app/base_service.py b/containers/orchestration/app/base_service.py new file mode 100644 index 0000000000..a30ddd4754 --- /dev/null +++ b/containers/orchestration/app/base_service.py @@ -0,0 +1,129 @@ +from pathlib import Path +from typing import Literal + +from fastapi import FastAPI +from fastapi import Request +from fastapi import Response +from pydantic import BaseModel + +# create a class with the DIBBs default Creative Commons Zero v1.0 and +# MIT license to be used by the BaseService class +LICENSES = { + "CreativeCommonsZero": { + "name": "Creative Commons Zero v1.0 Universal", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + }, + "MIT": {"name": "The MIT License", "url": "https://mit-license.org/"}, +} + +DIBBS_CONTACT = { + "name": "CDC Public Health Data Infrastructure", + "url": "https://cdcgov.github.io/dibbs-site/", + "email": "dibbs@cdc.gov", +} + + +STATUS_OK = {"status": "OK"} + + +class StatusResponse(BaseModel): + """ + The schema for the response from the health check endpoint. + """ + + status: Literal["OK"] + + +class BaseService: + def __init__( + self, + service_name: str, + service_path: str, + description_path: str, + include_health_check_endpoint: bool = True, + license_info: Literal["CreativeCommonsZero", "MIT"] = "CreativeCommonsZero", + openapi_url: str = "/openapi.json", + ): + """ + Initialize a BaseService instance. + + :param service_name: The name of the service. + :param service_path: The path to used to access the service from a gateway. + :param description_path: The path to a markdown file containing a description of + the service. + :param include_health_check_endpoint: If True, the standard DIBBs health check + endpoint will be added. + :param license_info: If empty, the standard DIBBs Creative Commons Zero v1.0 + Universal license will be used. The other available option is to use the + MIT license. + :param openapi_url: Optionally, the url of the OpenAPI.json file that FastAPI + should use when auto-constructing docs at the `/redoc` endpoint. For + services deployed in public environments managed by application gateways + or other routers, this parameter should be set to + "/{service-name}/openapi.json". If omitted, the parameter is set to the + FastAPI default. + """ + description = Path(description_path).read_text(encoding="utf-8") + self.service_path = service_path + self.include_health_check_endpoint = include_health_check_endpoint + self.app = FastAPI( + title=service_name, + version="1.7.1", + contact=DIBBS_CONTACT, + license_info=LICENSES[license_info], + description=description, + openapi_url=openapi_url, + ) + + """ + Because this is a reusable class, middlewares and endpoints need to be + added after the class is instantiated. This is done by calling the start + method, which in turn calls the add_path_rewrite_middleware and + add_health_check_endpoint methods. + """ + + def add_path_rewrite_middleware(self): + """ + Add middleware to the FastAPI instance to strip the service_path + from the URL path if it is present. This is useful when the service + is behind a gateway that is using a path-based routing strategy. + """ + + @self.app.middleware("http") + async def rewrite_path(request: Request, call_next: callable) -> Response: + if ( + request.url.path.startswith(self.service_path) + and "openapi.json" not in request.url.path + ): + request.scope["path"] = request.scope["path"].replace( + self.service_path, "" + ) + if request.scope["path"] == "": + request.scope["path"] = "/" + return await call_next(request) + + def add_health_check_endpoint(self): + """ + Adds a health check endpoint to the web service. + """ + + @self.app.get("/") + async def health_check() -> StatusResponse: + """ + This endpoint checks service status. If an HTTP 200 status code is returned along with + '{"status": "OK"}' then the service is available and running properly. + """ + return STATUS_OK + + def start(self) -> FastAPI: + """ + Return a FastAPI instance with DIBBs metadata set. If + `include_health_check_endpoint` is True, then the health check endpoint + will be added. + + :return: The FastAPI instance. + """ + self.add_path_rewrite_middleware() + if self.include_health_check_endpoint: + self.add_health_check_endpoint() + return self.app diff --git a/containers/orchestration/app/main.py b/containers/orchestration/app/main.py index e7bff11e65..c095b297c8 100644 --- a/containers/orchestration/app/main.py +++ b/containers/orchestration/app/main.py @@ -4,7 +4,6 @@ from pathlib import Path from typing import Annotated -from dibbs.base_service import BaseService from fastapi import Body from fastapi import File from fastapi import Form @@ -18,6 +17,7 @@ from opentelemetry import trace from opentelemetry.trace.status import StatusCode +from app.base_service import BaseService from app.config import get_settings from app.constants import process_message_response_examples from app.constants import sample_get_config_response diff --git a/containers/orchestration/requirements.txt b/containers/orchestration/requirements.txt index c1db0ef8f3..752db76daa 100644 --- a/containers/orchestration/requirements.txt +++ b/containers/orchestration/requirements.txt @@ -1,4 +1,3 @@ -../dibbs typing_extensions>=4.8.0 pydantic==1.10.13 fastapi==0.112.2 diff --git a/containers/record-linkage/Dockerfile b/containers/record-linkage/Dockerfile index 0a65109ce9..42e68f080a 100644 --- a/containers/record-linkage/Dockerfile +++ b/containers/record-linkage/Dockerfile @@ -1,4 +1,9 @@ -FROM ghcr.io/cdcgov/phdi/dibbs +FROM python:3.11-slim + +RUN apt-get update && \ + apt-get upgrade -y + +RUN pip install --upgrade pip WORKDIR /code diff --git a/containers/record-linkage/app/base_service.py b/containers/record-linkage/app/base_service.py new file mode 100644 index 0000000000..a30ddd4754 --- /dev/null +++ b/containers/record-linkage/app/base_service.py @@ -0,0 +1,129 @@ +from pathlib import Path +from typing import Literal + +from fastapi import FastAPI +from fastapi import Request +from fastapi import Response +from pydantic import BaseModel + +# create a class with the DIBBs default Creative Commons Zero v1.0 and +# MIT license to be used by the BaseService class +LICENSES = { + "CreativeCommonsZero": { + "name": "Creative Commons Zero v1.0 Universal", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + }, + "MIT": {"name": "The MIT License", "url": "https://mit-license.org/"}, +} + +DIBBS_CONTACT = { + "name": "CDC Public Health Data Infrastructure", + "url": "https://cdcgov.github.io/dibbs-site/", + "email": "dibbs@cdc.gov", +} + + +STATUS_OK = {"status": "OK"} + + +class StatusResponse(BaseModel): + """ + The schema for the response from the health check endpoint. + """ + + status: Literal["OK"] + + +class BaseService: + def __init__( + self, + service_name: str, + service_path: str, + description_path: str, + include_health_check_endpoint: bool = True, + license_info: Literal["CreativeCommonsZero", "MIT"] = "CreativeCommonsZero", + openapi_url: str = "/openapi.json", + ): + """ + Initialize a BaseService instance. + + :param service_name: The name of the service. + :param service_path: The path to used to access the service from a gateway. + :param description_path: The path to a markdown file containing a description of + the service. + :param include_health_check_endpoint: If True, the standard DIBBs health check + endpoint will be added. + :param license_info: If empty, the standard DIBBs Creative Commons Zero v1.0 + Universal license will be used. The other available option is to use the + MIT license. + :param openapi_url: Optionally, the url of the OpenAPI.json file that FastAPI + should use when auto-constructing docs at the `/redoc` endpoint. For + services deployed in public environments managed by application gateways + or other routers, this parameter should be set to + "/{service-name}/openapi.json". If omitted, the parameter is set to the + FastAPI default. + """ + description = Path(description_path).read_text(encoding="utf-8") + self.service_path = service_path + self.include_health_check_endpoint = include_health_check_endpoint + self.app = FastAPI( + title=service_name, + version="1.7.1", + contact=DIBBS_CONTACT, + license_info=LICENSES[license_info], + description=description, + openapi_url=openapi_url, + ) + + """ + Because this is a reusable class, middlewares and endpoints need to be + added after the class is instantiated. This is done by calling the start + method, which in turn calls the add_path_rewrite_middleware and + add_health_check_endpoint methods. + """ + + def add_path_rewrite_middleware(self): + """ + Add middleware to the FastAPI instance to strip the service_path + from the URL path if it is present. This is useful when the service + is behind a gateway that is using a path-based routing strategy. + """ + + @self.app.middleware("http") + async def rewrite_path(request: Request, call_next: callable) -> Response: + if ( + request.url.path.startswith(self.service_path) + and "openapi.json" not in request.url.path + ): + request.scope["path"] = request.scope["path"].replace( + self.service_path, "" + ) + if request.scope["path"] == "": + request.scope["path"] = "/" + return await call_next(request) + + def add_health_check_endpoint(self): + """ + Adds a health check endpoint to the web service. + """ + + @self.app.get("/") + async def health_check() -> StatusResponse: + """ + This endpoint checks service status. If an HTTP 200 status code is returned along with + '{"status": "OK"}' then the service is available and running properly. + """ + return STATUS_OK + + def start(self) -> FastAPI: + """ + Return a FastAPI instance with DIBBs metadata set. If + `include_health_check_endpoint` is True, then the health check endpoint + will be added. + + :return: The FastAPI instance. + """ + self.add_path_rewrite_middleware() + if self.include_health_check_endpoint: + self.add_health_check_endpoint() + return self.app diff --git a/containers/record-linkage/app/main.py b/containers/record-linkage/app/main.py index 6e8f529e4e..b4054c8559 100644 --- a/containers/record-linkage/app/main.py +++ b/containers/record-linkage/app/main.py @@ -3,13 +3,13 @@ from typing import Annotated from typing import Optional -from dibbs.base_service import BaseService from fastapi import Body from fastapi import Response from fastapi import status from pydantic import BaseModel from pydantic import Field +from app.base_service import BaseService from app.linkage.algorithms import DIBBS_BASIC from app.linkage.algorithms import DIBBS_ENHANCED from app.linkage.link import add_person_resource diff --git a/containers/record-linkage/requirements.txt b/containers/record-linkage/requirements.txt index c9df55101a..964196d3dd 100644 --- a/containers/record-linkage/requirements.txt +++ b/containers/record-linkage/requirements.txt @@ -1,4 +1,7 @@ -../dibbs +pydantic==1.10.13 +fastapi>=0.109.1 +uvicorn +httpx pathlib pyway==0.3.24 psycopg2-binary==2.9.9 @@ -8,4 +11,5 @@ pandas>2.0.0 sqlalchemy rapidfuzz pyarrow>=14.0.1 -mysql-connector-python>=9.1.0 # not directly required, pinned by Snyk to avoid a vulnerability \ No newline at end of file +mysql-connector-python>=9.1.0 # not directly required, pinned by Snyk to avoid a vulnerability +anyio==4.4.0 # not directly required, pinned to avoid vulnerability \ No newline at end of file diff --git a/containers/trigger-code-reference/Dockerfile b/containers/trigger-code-reference/Dockerfile index 678de9c6a1..5836d028f4 100644 --- a/containers/trigger-code-reference/Dockerfile +++ b/containers/trigger-code-reference/Dockerfile @@ -1,4 +1,9 @@ -FROM ghcr.io/cdcgov/phdi/dibbs +FROM python:3.11-slim + +RUN apt-get update && \ + apt-get upgrade -y + +RUN pip install --upgrade pip WORKDIR /code diff --git a/containers/trigger-code-reference/app/base_service.py b/containers/trigger-code-reference/app/base_service.py new file mode 100644 index 0000000000..a30ddd4754 --- /dev/null +++ b/containers/trigger-code-reference/app/base_service.py @@ -0,0 +1,129 @@ +from pathlib import Path +from typing import Literal + +from fastapi import FastAPI +from fastapi import Request +from fastapi import Response +from pydantic import BaseModel + +# create a class with the DIBBs default Creative Commons Zero v1.0 and +# MIT license to be used by the BaseService class +LICENSES = { + "CreativeCommonsZero": { + "name": "Creative Commons Zero v1.0 Universal", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + }, + "MIT": {"name": "The MIT License", "url": "https://mit-license.org/"}, +} + +DIBBS_CONTACT = { + "name": "CDC Public Health Data Infrastructure", + "url": "https://cdcgov.github.io/dibbs-site/", + "email": "dibbs@cdc.gov", +} + + +STATUS_OK = {"status": "OK"} + + +class StatusResponse(BaseModel): + """ + The schema for the response from the health check endpoint. + """ + + status: Literal["OK"] + + +class BaseService: + def __init__( + self, + service_name: str, + service_path: str, + description_path: str, + include_health_check_endpoint: bool = True, + license_info: Literal["CreativeCommonsZero", "MIT"] = "CreativeCommonsZero", + openapi_url: str = "/openapi.json", + ): + """ + Initialize a BaseService instance. + + :param service_name: The name of the service. + :param service_path: The path to used to access the service from a gateway. + :param description_path: The path to a markdown file containing a description of + the service. + :param include_health_check_endpoint: If True, the standard DIBBs health check + endpoint will be added. + :param license_info: If empty, the standard DIBBs Creative Commons Zero v1.0 + Universal license will be used. The other available option is to use the + MIT license. + :param openapi_url: Optionally, the url of the OpenAPI.json file that FastAPI + should use when auto-constructing docs at the `/redoc` endpoint. For + services deployed in public environments managed by application gateways + or other routers, this parameter should be set to + "/{service-name}/openapi.json". If omitted, the parameter is set to the + FastAPI default. + """ + description = Path(description_path).read_text(encoding="utf-8") + self.service_path = service_path + self.include_health_check_endpoint = include_health_check_endpoint + self.app = FastAPI( + title=service_name, + version="1.7.1", + contact=DIBBS_CONTACT, + license_info=LICENSES[license_info], + description=description, + openapi_url=openapi_url, + ) + + """ + Because this is a reusable class, middlewares and endpoints need to be + added after the class is instantiated. This is done by calling the start + method, which in turn calls the add_path_rewrite_middleware and + add_health_check_endpoint methods. + """ + + def add_path_rewrite_middleware(self): + """ + Add middleware to the FastAPI instance to strip the service_path + from the URL path if it is present. This is useful when the service + is behind a gateway that is using a path-based routing strategy. + """ + + @self.app.middleware("http") + async def rewrite_path(request: Request, call_next: callable) -> Response: + if ( + request.url.path.startswith(self.service_path) + and "openapi.json" not in request.url.path + ): + request.scope["path"] = request.scope["path"].replace( + self.service_path, "" + ) + if request.scope["path"] == "": + request.scope["path"] = "/" + return await call_next(request) + + def add_health_check_endpoint(self): + """ + Adds a health check endpoint to the web service. + """ + + @self.app.get("/") + async def health_check() -> StatusResponse: + """ + This endpoint checks service status. If an HTTP 200 status code is returned along with + '{"status": "OK"}' then the service is available and running properly. + """ + return STATUS_OK + + def start(self) -> FastAPI: + """ + Return a FastAPI instance with DIBBs metadata set. If + `include_health_check_endpoint` is True, then the health check endpoint + will be added. + + :return: The FastAPI instance. + """ + self.add_path_rewrite_middleware() + if self.include_health_check_endpoint: + self.add_health_check_endpoint() + return self.app diff --git a/containers/trigger-code-reference/app/main.py b/containers/trigger-code-reference/app/main.py index 711956427b..f50b1e5dd8 100644 --- a/containers/trigger-code-reference/app/main.py +++ b/containers/trigger-code-reference/app/main.py @@ -2,11 +2,11 @@ from pathlib import Path from typing import Annotated -from dibbs.base_service import BaseService from fastapi import Body from fastapi import Query from fastapi import Response +from app.base_service import BaseService from app.models import InsertConditionInput from app.utils import _find_codes_by_resource_type from app.utils import _stamp_resource_with_code_extension diff --git a/containers/trigger-code-reference/requirements.txt b/containers/trigger-code-reference/requirements.txt index 7f3695c04c..0d3257aa1a 100644 --- a/containers/trigger-code-reference/requirements.txt +++ b/containers/trigger-code-reference/requirements.txt @@ -1,4 +1,6 @@ -../dibbs +pydantic==1.10.13 +fastapi>=0.109.1 +uvicorn pathlib httpx requests diff --git a/containers/validation/Dockerfile b/containers/validation/Dockerfile index c9277042ed..b2a726a18d 100644 --- a/containers/validation/Dockerfile +++ b/containers/validation/Dockerfile @@ -1,4 +1,9 @@ -FROM ghcr.io/cdcgov/phdi/dibbs +FROM python:3.11-slim + +RUN apt-get update && \ + apt-get upgrade -y + +RUN pip install --upgrade pip WORKDIR /code diff --git a/containers/validation/app/base_service.py b/containers/validation/app/base_service.py new file mode 100644 index 0000000000..a30ddd4754 --- /dev/null +++ b/containers/validation/app/base_service.py @@ -0,0 +1,129 @@ +from pathlib import Path +from typing import Literal + +from fastapi import FastAPI +from fastapi import Request +from fastapi import Response +from pydantic import BaseModel + +# create a class with the DIBBs default Creative Commons Zero v1.0 and +# MIT license to be used by the BaseService class +LICENSES = { + "CreativeCommonsZero": { + "name": "Creative Commons Zero v1.0 Universal", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + }, + "MIT": {"name": "The MIT License", "url": "https://mit-license.org/"}, +} + +DIBBS_CONTACT = { + "name": "CDC Public Health Data Infrastructure", + "url": "https://cdcgov.github.io/dibbs-site/", + "email": "dibbs@cdc.gov", +} + + +STATUS_OK = {"status": "OK"} + + +class StatusResponse(BaseModel): + """ + The schema for the response from the health check endpoint. + """ + + status: Literal["OK"] + + +class BaseService: + def __init__( + self, + service_name: str, + service_path: str, + description_path: str, + include_health_check_endpoint: bool = True, + license_info: Literal["CreativeCommonsZero", "MIT"] = "CreativeCommonsZero", + openapi_url: str = "/openapi.json", + ): + """ + Initialize a BaseService instance. + + :param service_name: The name of the service. + :param service_path: The path to used to access the service from a gateway. + :param description_path: The path to a markdown file containing a description of + the service. + :param include_health_check_endpoint: If True, the standard DIBBs health check + endpoint will be added. + :param license_info: If empty, the standard DIBBs Creative Commons Zero v1.0 + Universal license will be used. The other available option is to use the + MIT license. + :param openapi_url: Optionally, the url of the OpenAPI.json file that FastAPI + should use when auto-constructing docs at the `/redoc` endpoint. For + services deployed in public environments managed by application gateways + or other routers, this parameter should be set to + "/{service-name}/openapi.json". If omitted, the parameter is set to the + FastAPI default. + """ + description = Path(description_path).read_text(encoding="utf-8") + self.service_path = service_path + self.include_health_check_endpoint = include_health_check_endpoint + self.app = FastAPI( + title=service_name, + version="1.7.1", + contact=DIBBS_CONTACT, + license_info=LICENSES[license_info], + description=description, + openapi_url=openapi_url, + ) + + """ + Because this is a reusable class, middlewares and endpoints need to be + added after the class is instantiated. This is done by calling the start + method, which in turn calls the add_path_rewrite_middleware and + add_health_check_endpoint methods. + """ + + def add_path_rewrite_middleware(self): + """ + Add middleware to the FastAPI instance to strip the service_path + from the URL path if it is present. This is useful when the service + is behind a gateway that is using a path-based routing strategy. + """ + + @self.app.middleware("http") + async def rewrite_path(request: Request, call_next: callable) -> Response: + if ( + request.url.path.startswith(self.service_path) + and "openapi.json" not in request.url.path + ): + request.scope["path"] = request.scope["path"].replace( + self.service_path, "" + ) + if request.scope["path"] == "": + request.scope["path"] = "/" + return await call_next(request) + + def add_health_check_endpoint(self): + """ + Adds a health check endpoint to the web service. + """ + + @self.app.get("/") + async def health_check() -> StatusResponse: + """ + This endpoint checks service status. If an HTTP 200 status code is returned along with + '{"status": "OK"}' then the service is available and running properly. + """ + return STATUS_OK + + def start(self) -> FastAPI: + """ + Return a FastAPI instance with DIBBs metadata set. If + `include_health_check_endpoint` is True, then the health check endpoint + will be added. + + :return: The FastAPI instance. + """ + self.add_path_rewrite_middleware() + if self.include_health_check_endpoint: + self.add_health_check_endpoint() + return self.app diff --git a/containers/validation/app/main.py b/containers/validation/app/main.py index 9f3f7ca529..7097afe5ac 100644 --- a/containers/validation/app/main.py +++ b/containers/validation/app/main.py @@ -1,9 +1,9 @@ from pathlib import Path from typing import Annotated -from dibbs.base_service import BaseService from fastapi import Body +from app.base_service import BaseService from app.constants import ValidateInput from app.constants import ValidateResponse from app.utils import check_for_and_extract_rr_data diff --git a/containers/validation/dev-requirements.txt b/containers/validation/dev-requirements.txt index e20edb6c73..13f90be406 100644 --- a/containers/validation/dev-requirements.txt +++ b/containers/validation/dev-requirements.txt @@ -2,3 +2,4 @@ pytest testcontainers[compose]==3.7.1 requests>=2.32.2 # not directly required, pinned by Snyk to avoid a vulnerability urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability +toml \ No newline at end of file diff --git a/containers/validation/requirements.txt b/containers/validation/requirements.txt index d8f58fd30a..f102de72d4 100644 --- a/containers/validation/requirements.txt +++ b/containers/validation/requirements.txt @@ -1,4 +1,6 @@ -../dibbs +pydantic==1.10.13 +fastapi>=0.109.1 +uvicorn hl7 httpx==0.27.0 jsonschema diff --git a/containers/validation/tests/test_base_service.py b/containers/validation/tests/test_base_service.py index 4d52f87faa..895d33d5ab 100644 --- a/containers/validation/tests/test_base_service.py +++ b/containers/validation/tests/test_base_service.py @@ -1,12 +1,16 @@ -from importlib import metadata from pathlib import Path -from dibbs.base_service import BaseService -from dibbs.base_service import DIBBS_CONTACT -from dibbs.base_service import LICENSES +import toml +from app.base_service import BaseService +from app.base_service import DIBBS_CONTACT +from app.base_service import LICENSES from fastapi.testclient import TestClient -default_app_version = metadata.version("dibbs") +with open( + Path(__file__).parent.parent.parent.parent / "pyproject.toml" +) as project_config_file: + project_config = toml.load(project_config_file) + default_app_version = project_config["tool"]["poetry"]["version"][1:] def test_base_service(): diff --git a/phdi/containers/base_service.py b/phdi/containers/base_service.py index f502236465..630bcdcdfb 100644 --- a/phdi/containers/base_service.py +++ b/phdi/containers/base_service.py @@ -1,4 +1,3 @@ -from importlib import metadata from pathlib import Path from typing import Literal @@ -60,7 +59,7 @@ def __init__( self.include_health_check_endpoint = include_health_check_endpoint self.app = FastAPI( title=service_name, - version=metadata.version("phdi"), + version="1.7.1", contact=DIBBS_CONTACT, license_info=LICENSES[license_info], description=description, diff --git a/tests/containers/test_base_service.py b/tests/containers/test_base_service.py index 07bb502367..fc615c4ba8 100644 --- a/tests/containers/test_base_service.py +++ b/tests/containers/test_base_service.py @@ -1,13 +1,15 @@ -from importlib import metadata from pathlib import Path +import toml from fastapi.testclient import TestClient from phdi.containers.base_service import BaseService from phdi.containers.base_service import DIBBS_CONTACT from phdi.containers.base_service import LICENSES -default_app_version = metadata.version("phdi") +with open(Path(__file__).parent.parent / "pyproject.toml") as project_config_file: + project_config = toml.load(project_config_file) + default_app_version = project_config["tool"]["poetry"]["version"] def test_base_service():