diff --git a/.github/workflows/example-bank-api-docker-publish.yml b/.github/workflows/example-bank-api-docker-publish.yml new file mode 100644 index 0000000..f1fb394 --- /dev/null +++ b/.github/workflows/example-bank-api-docker-publish.yml @@ -0,0 +1,43 @@ +name: Bridge Example Bank API Docker Publish + +on: + push: + workflow_dispatch: + +jobs: + docker-build: + name: Docker Build and Push + runs-on: ubuntu-latest + env: + NAMESPACE: ${{ secrets.docker_hub_organisation || 'openg2p' }} + SERVICE_NAME: openg2p-g2p-bridge-example-bank-api + steps: + - uses: actions/checkout@v3 + - name: Docker build + run: | + BRANCH_NAME=$(echo ${{ github.ref }} | sed -e 's,.*/\(.*\),\1,') + + IMAGE_ID=$NAMESPACE/$SERVICE_NAME + + # Change all uppercase to lowercase + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') + VERSION=$BRANCH_NAME + if [[ $BRANCH_NAME == master || $BRANCH_NAME == main ]]; then + VERSION=develop + fi + echo IMAGE_ID=$IMAGE_ID + echo VERSION=$VERSION + echo IMAGE_ID=$IMAGE_ID >> $GITHUB_ENV + echo VERSION=$VERSION >> $GITHUB_ENV + + docker build ./openg2p-g2p-bridge-example-bank-api \ + --tag $IMAGE_ID:$VERSION + if [[ '${{ secrets.docker_hub_token }}' != '' && '${{ secrets.docker_hub_actor }}' != '' ]]; then + export DOCKER_PUSH="true" + echo DOCKER_PUSH=$DOCKER_PUSH >> $GITHUB_ENV + fi + - name: Docker push + if: env.DOCKER_PUSH == 'true' + run: | + echo "${{ secrets.docker_hub_token }}" | docker login -u ${{ secrets.docker_hub_actor }} --password-stdin + docker push ${{ env.IMAGE_ID }}:${{ env.VERSION }} diff --git a/.github/workflows/example-bank-api-openapi-publish.yml b/.github/workflows/example-bank-api-openapi-publish.yml new file mode 100644 index 0000000..cc193a9 --- /dev/null +++ b/.github/workflows/example-bank-api-openapi-publish.yml @@ -0,0 +1,53 @@ +name: Bridge API OpenAPI Publish + +on: + push: + workflow_dispatch: + +jobs: + openapi-publish: + name: OpenAPI Generate and Publish + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Get branch name (merge) + run: | + echo "BRANCH_NAME=$(echo ${{ github.ref }} | sed -e 's,.*/\(.*\),\1,')" >> $GITHUB_ENV + - name: Setup python for openapi generate + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install app + run: | + python -m pip install git+https://github.com/openg2p/openg2p-fastapi-common@1.1.0\#subdirectory=openg2p-fastapi-common + python -m pip install git+https://github.com/openg2p/openg2p-fastapi-common@1.1.0\#subdirectory=openg2p-fastapi-auth + python -m pip install git+https://github.com/openg2p/openg2p-g2pconnect-common@1.0.0\#subdirectory=openg2p-g2pconnect-common-lib + python -m pip install git+https://github.com/openg2p/openg2p-g2p-bridge@1.0.0\#subdirectory=openg2p-g2p-bridge-models + python -m pip install -e openg2p-g2p-bridge-api/ + - name: Generate openapi json + run: | + mkdir -p openg2p-g2p-bridge-api/api-docs/generated + python3 openg2p-g2p-bridge-api/main.py getOpenAPI openg2p-g2p-bridge-api/api-docs/generated/openapi.json + if ! [ -z "$(git status --porcelain=v1 2>/dev/null -- openg2p-g2p-bridge-api/api-docs/generated/openapi.json)" ]; then + shopt -s nocasematch + if [[ ${{ github.repository_owner }} == 'OpenG2P' ]]; then + export OPENAPI_CHANGED="true" + echo OPENAPI_CHANGED=$OPENAPI_CHANGED >> $GITHUB_ENV + fi + fi + - name: Commit Changes + uses: EndBug/add-and-commit@v7 + if: env.OPENAPI_CHANGED == 'true' + with: + default_author: github_actions + message: "Generated new openapi.json on push to ${{ github.event.inputs.git-ref }}" + add: "openg2p-g2p-bridge-api/api-docs/generated/openapi.json" + - name: Setup nodejs + uses: actions/setup-node@v4 + if: env.OPENAPI_CHANGED == 'true' + with: + node-version: '18' + - name: Publish to stoplight + if: env.OPENAPI_CHANGED == 'true' + run: | + npx @stoplight/cli@5 push --ci-token ${{ secrets.STOPLIGHT_PROJECT_TOKEN }} --url https://openg2p.stoplight.io --branch ${{ env.BRANCH_NAME }} --directory openg2p-g2p-bridge-api/api-docs/generated diff --git a/.github/workflows/example-bank-api-pypi-publish.yml b/.github/workflows/example-bank-api-pypi-publish.yml new file mode 100644 index 0000000..27b82f1 --- /dev/null +++ b/.github/workflows/example-bank-api-pypi-publish.yml @@ -0,0 +1,22 @@ +name: Bridge API PyPI Publish + +on: + workflow_dispatch + +jobs: + publish-to-pypi: + runs-on: ubuntu-latest + steps: + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - uses: actions/checkout@v3 + - name: Install build dependencies + run: pip install build + - name: Build distribution + run: python -m build -s openg2p-g2p-bridge-example-bank-api/ + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/example-bank-celery-docker-publish.yml b/.github/workflows/example-bank-celery-docker-publish.yml new file mode 100644 index 0000000..aa4b1dc --- /dev/null +++ b/.github/workflows/example-bank-celery-docker-publish.yml @@ -0,0 +1,43 @@ +name: Bridge Celery Beat Producers Docker Publish + +on: + push: + workflow_dispatch: + +jobs: + docker-build: + name: Docker Build and Push + runs-on: ubuntu-latest + env: + NAMESPACE: ${{ secrets.docker_hub_organisation || 'openg2p' }} + SERVICE_NAME: openg2p-g2p-bridge-example-bank-celery + steps: + - uses: actions/checkout@v3 + - name: Docker build + run: | + BRANCH_NAME=$(echo ${{ github.ref }} | sed -e 's,.*/\(.*\),\1,') + + IMAGE_ID=$NAMESPACE/$SERVICE_NAME + + # Change all uppercase to lowercase + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') + VERSION=$BRANCH_NAME + if [[ $BRANCH_NAME == master || $BRANCH_NAME == main ]]; then + VERSION=develop + fi + echo IMAGE_ID=$IMAGE_ID + echo VERSION=$VERSION + echo IMAGE_ID=$IMAGE_ID >> $GITHUB_ENV + echo VERSION=$VERSION >> $GITHUB_ENV + + docker build ./openg2p-g2p-bridge-example-bank-celery \ + --tag $IMAGE_ID:$VERSION + if [[ '${{ secrets.docker_hub_token }}' != '' && '${{ secrets.docker_hub_actor }}' != '' ]]; then + export DOCKER_PUSH="true" + echo DOCKER_PUSH=$DOCKER_PUSH >> $GITHUB_ENV + fi + - name: Docker push + if: env.DOCKER_PUSH == 'true' + run: | + echo "${{ secrets.docker_hub_token }}" | docker login -u ${{ secrets.docker_hub_actor }} --password-stdin + docker push ${{ env.IMAGE_ID }}:${{ env.VERSION }} diff --git a/.github/workflows/example-bank-celery-pypi-publish.yml b/.github/workflows/example-bank-celery-pypi-publish.yml new file mode 100644 index 0000000..ae520c7 --- /dev/null +++ b/.github/workflows/example-bank-celery-pypi-publish.yml @@ -0,0 +1,22 @@ +name: Bridge API PyPI Publish + +on: + workflow_dispatch + +jobs: + publish-to-pypi: + runs-on: ubuntu-latest + steps: + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - uses: actions/checkout@v3 + - name: Install build dependencies + run: pip install build + - name: Build distribution + run: python -m build -s openg2p-g2p-bridge-example-bank-celery/ + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/example-bank-models-pypi-publish.yml b/.github/workflows/example-bank-models-pypi-publish.yml new file mode 100644 index 0000000..f078eed --- /dev/null +++ b/.github/workflows/example-bank-models-pypi-publish.yml @@ -0,0 +1,22 @@ +name: Bridge API PyPI Publish + +on: + workflow_dispatch + +jobs: + publish-to-pypi: + runs-on: ubuntu-latest + steps: + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - uses: actions/checkout@v3 + - name: Install build dependencies + run: pip install build -s openg2p-g2p-bridge-example-bank-models/ + - name: Build distribution + run: python -m build + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/example-bank-tests.yml b/.github/workflows/example-bank-tests.yml new file mode 100644 index 0000000..580ea01 --- /dev/null +++ b/.github/workflows/example-bank-tests.yml @@ -0,0 +1,51 @@ +name: Test and coverage + +on: + pull_request: + push: + workflow_dispatch: + +concurrency: + group: check-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: test with ${{ matrix.py }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + py: + - "3.10" + os: + - ubuntu-latest + services: + postgres: + image: postgres:9.6 + env: + POSTGRES_USER: openg2p + POSTGRES_PASSWORD: openg2p + POSTGRES_DB: openg2p + ports: + - 5432:5432 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup python for test ${{ matrix.py }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.py }} + - name: Install test requirements + run: | + python -m pip install -r test-requirements.txt + python -m pip install -e openg2p-g2p-bridge-example-bank-api +# TODO: add other packages here + - name: Run test suite + run: | + pytest --cov-branch --cov-report=term-missing --cov=openg2p-g2p-bridge-example-bank-api --cov=tests + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..affa8a8 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,16 @@ +name: pre-commit + +on: + pull_request: + push: + workflow_dispatch: + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + - uses: pre-commit/action@v3.0.0 + with: + extra_args: --all-files --show-diff-on-failure diff --git a/openg2p-g2p-bridge-example-bank-api/.pre-commit-config.yaml b/.pre-commit-config.yaml similarity index 100% rename from openg2p-g2p-bridge-example-bank-api/.pre-commit-config.yaml rename to .pre-commit-config.yaml diff --git a/openg2p-g2p-bridge-example-bank-api/.ruff.toml b/.ruff.toml similarity index 100% rename from openg2p-g2p-bridge-example-bank-api/.ruff.toml rename to .ruff.toml diff --git a/openg2p-g2p-bridge-example-bank-api/Dockerfile b/openg2p-g2p-bridge-example-bank-api/Dockerfile new file mode 100644 index 0000000..f91cc71 --- /dev/null +++ b/openg2p-g2p-bridge-example-bank-api/Dockerfile @@ -0,0 +1,31 @@ +FROM bitnami/python:3.10.13-debian-11-r24 + +ARG container_user=openg2p +ARG container_user_group=openg2p +ARG container_user_uid=1001 +ARG container_user_gid=1001 + +RUN groupadd -g ${container_user_gid} ${container_user_group} \ + && useradd -mN -u ${container_user_uid} -G ${container_user_group} -s /bin/bash ${container_user} + +WORKDIR /app + +RUN install_packages libpq-dev \ + && apt-get clean && rm -rf /var/lib/apt/lists /var/cache/apt/archives + +RUN chown -R ${container_user}:${container_user_group} /app +USER ${container_user} + +ADD --chown=${container_user}:${container_user_group} . /app/src +ADD --chown=${container_user}:${container_user_group} main.py /app + +RUN python3 -m venv venv \ + && . ./venv/bin/activate +RUN python3 -m pip install \ + git+https://github.com/openg2p/openg2p-fastapi-common@1.1.0\#subdirectory=openg2p-fastapi-common \ + git+https://github.com/openg2p/openg2p-fastapi-common@1.1.0\#subdirectory=openg2p-fastapi-auth \ + git+https://github.com/openg2p/openg2p-g2p-bridge-example-bank@1.0.0\#subdirectory=openg2p-g2p-bridge-example-bank-models \ + ./src + +CMD python3 main.py migrate; \ + gunicorn "openg2p-g2p-bridge-example-bank-api.main:app" --workers ${SPAR_MAPPER_NO_OF_WORKERS} --worker-class uvicorn.workers.UvicornWorker --bind ${SPAR_MAPPER_HOST}:${SPAR_MAPPER_PORT} diff --git a/openg2p-g2p-bridge-example-bank-api/pyproject.toml b/openg2p-g2p-bridge-example-bank-api/pyproject.toml index afe215a..44df183 100644 --- a/openg2p-g2p-bridge-example-bank-api/pyproject.toml +++ b/openg2p-g2p-bridge-example-bank-api/pyproject.toml @@ -16,8 +16,9 @@ classifiers = [ "Operating System :: OS Independent", ] dependencies = [ - "openg2p-fastapi-common", - "openg2p-fastapi-auth", + "openg2p-fastapi-common", + "openg2p-fastapi-auth", + "celery", ] dynamic = ["version"] diff --git a/openg2p-g2p-bridge-example-bank-api/tests/__init__.py b/openg2p-g2p-bridge-example-bank-api/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/openg2p-g2p-bridge-example-bank-api/tests/test_disbursement.py b/openg2p-g2p-bridge-example-bank-api/tests/test_disbursement.py deleted file mode 100644 index 8171c3e..0000000 --- a/openg2p-g2p-bridge-example-bank-api/tests/test_disbursement.py +++ /dev/null @@ -1,175 +0,0 @@ -import datetime -from unittest.mock import AsyncMock, patch - -import pytest -from openg2p_g2p_bridge_api.controllers import DisbursementController -from openg2p_g2p_bridge_models.errors.codes import G2PBridgeErrorCodes -from openg2p_g2p_bridge_models.errors.exceptions import DisbursementException -from openg2p_g2p_bridge_models.models import CancellationStatus -from openg2p_g2p_bridge_models.schemas import ( - DisbursementPayload, - DisbursementRequest, - DisbursementResponse, - ResponseStatus, -) - - -def mock_create_disbursements(is_valid, disbursement_payloads): - if not is_valid: - raise DisbursementException( - code=G2PBridgeErrorCodes.INVALID_DISBURSEMENT_PAYLOAD, - disbursement_payloads=disbursement_payloads, - ) - return disbursement_payloads - - -@pytest.mark.asyncio -@patch("openg2p_g2p_bridge_api.services.DisbursementService.get_component") -async def test_create_disbursements_success(mock_service_get_component): - mock_service_instance = AsyncMock() - disbursement_payloads = [ - DisbursementPayload( - disbursement_envelope_id="env123", - beneficiary_id="123AB", - disbursement_amount=1000, - ) - ] - mock_service_instance.create_disbursements = AsyncMock( - return_value=mock_create_disbursements(True, disbursement_payloads) - ) - mock_service_instance.construct_disbursement_success_response = AsyncMock( - return_value=DisbursementResponse( - response_status=ResponseStatus.SUCCESS, - response_payload=disbursement_payloads, - ) - ) - - mock_service_get_component.return_value = mock_service_instance - - controller = DisbursementController() - request_payload = DisbursementRequest(request_payload=disbursement_payloads) - - response = await controller.create_disbursements(request_payload) - - assert response.response_status == ResponseStatus.SUCCESS - assert response.response_payload == disbursement_payloads - - -@pytest.mark.asyncio -@patch("openg2p_g2p_bridge_api.services.DisbursementService.get_component") -async def test_create_disbursements_failure(mock_service_get_component): - mock_service_instance = AsyncMock() - disbursement_payloads = [ - DisbursementPayload( - disbursement_envelope_id="env123", - beneficiary_id="123AB", - disbursement_amount=1000, - ) - ] - mock_service_instance.create_disbursements = AsyncMock( - side_effect=lambda req: mock_create_disbursements(False, req.request_payload) - ) - mock_service_instance.construct_disbursement_error_response = AsyncMock( - return_value=DisbursementResponse( - response_status=ResponseStatus.FAILURE, - response_error_code=G2PBridgeErrorCodes.INVALID_DISBURSEMENT_PAYLOAD, - response_payload=disbursement_payloads, - ) - ) - - mock_service_get_component.return_value = mock_service_instance - - controller = DisbursementController() - request_payload = DisbursementRequest(request_payload=disbursement_payloads) - - response = await controller.create_disbursements(request_payload) - - assert response.response_status == ResponseStatus.FAILURE - assert ( - response.response_error_code == G2PBridgeErrorCodes.INVALID_DISBURSEMENT_PAYLOAD - ) - - -def mock_cancel_disbursements(is_valid, disbursement_payloads): - if not is_valid: - raise DisbursementException( - code=G2PBridgeErrorCodes.DISBURSEMENT_ALREADY_CANCELED, - disbursement_payloads=disbursement_payloads, - ) - for payload in disbursement_payloads: - payload.cancellation_status = CancellationStatus.Cancelled - payload.cancellation_time_stamp = datetime.datetime.utcnow() - return disbursement_payloads - - -@pytest.mark.asyncio -@patch("openg2p_g2p_bridge_api.services.DisbursementService.get_component") -async def test_cancel_disbursements_success(mock_service_get_component): - mock_service_instance = AsyncMock() - disbursement_payloads = [ - DisbursementPayload( - disbursement_id="123", - beneficiary_id="123AB", - disbursement_amount=1000, - cancellation_status=None, - ) - ] - mock_service_instance.cancel_disbursements = AsyncMock( - return_value=mock_cancel_disbursements(True, disbursement_payloads) - ) - mock_service_instance.construct_disbursement_success_response = AsyncMock( - return_value=DisbursementResponse( - response_status=ResponseStatus.SUCCESS, - response_payload=disbursement_payloads, - ) - ) - - mock_service_get_component.return_value = mock_service_instance - - controller = DisbursementController() - request_payload = DisbursementRequest(request_payload=disbursement_payloads) - - response = await controller.cancel_disbursements(request_payload) - - assert response.response_status == ResponseStatus.SUCCESS - assert all( - payload.cancellation_status == CancellationStatus.Cancelled - for payload in response.response_payload - ) - - -@pytest.mark.asyncio -@patch("openg2p_g2p_bridge_api.services.DisbursementService.get_component") -async def test_cancel_disbursements_failure(mock_service_get_component): - mock_service_instance = AsyncMock() - disbursement_payloads = [ - DisbursementPayload( - disbursement_id="123", - beneficiary_id="123AB", - disbursement_amount=1000, - cancellation_status=None, - ) - ] - mock_service_instance.cancel_disbursements = AsyncMock( - side_effect=lambda req: mock_cancel_disbursements(False, req.request_payload) - ) - mock_service_instance.construct_disbursement_error_response = AsyncMock( - return_value=DisbursementResponse( - response_status=ResponseStatus.FAILURE, - response_error_code=G2PBridgeErrorCodes.DISBURSEMENT_ALREADY_CANCELED, - response_payload=disbursement_payloads, - ) - ) - - mock_service_get_component.return_value = mock_service_instance - - controller = DisbursementController() - request_payload = DisbursementRequest(request_payload=disbursement_payloads) - - response = await controller.cancel_disbursements(request_payload) - - assert response.response_status == ResponseStatus.FAILURE - assert ( - response.response_error_code - == G2PBridgeErrorCodes.DISBURSEMENT_ALREADY_CANCELED - ) diff --git a/openg2p-g2p-bridge-example-bank-api/tests/test_disbursement_envelope.py b/openg2p-g2p-bridge-example-bank-api/tests/test_disbursement_envelope.py deleted file mode 100644 index 4ccfd8a..0000000 --- a/openg2p-g2p-bridge-example-bank-api/tests/test_disbursement_envelope.py +++ /dev/null @@ -1,197 +0,0 @@ -from datetime import datetime -from unittest.mock import AsyncMock, patch - -import pytest -from openg2p_g2p_bridge_api.controllers import DisbursementEnvelopeController -from openg2p_g2p_bridge_models.errors.codes import G2PBridgeErrorCodes -from openg2p_g2p_bridge_models.errors.exceptions import DisbursementEnvelopeException -from openg2p_g2p_bridge_models.schemas import ( - DisbursementEnvelopePayload, - DisbursementEnvelopeRequest, - DisbursementEnvelopeResponse, - ResponseStatus, -) - - -def mock_create_disbursement_envelope(is_valid, error_code=None): - if not is_valid: - raise DisbursementEnvelopeException( - code=error_code, message=f"{error_code} error." - ) - return DisbursementEnvelopePayload( - disbursement_envelope_id="env123", - benefit_program_mnemonic="TEST123", - disbursement_frequency="Monthly", - cycle_code_mnemonic="CYCLE42", - number_of_beneficiaries=100, - number_of_disbursements=100, - total_disbursement_amount=5000.00, - disbursement_schedule_date=datetime.date(datetime.utcnow()), - ) - - -@pytest.mark.asyncio -@patch("openg2p_g2p_bridge_api.services.DisbursementEnvelopeService.get_component") -async def test_create_disbursement_envelope_success(mock_service_get_component): - mock_service_instance = AsyncMock() - mock_service_instance.create_disbursement_envelope = AsyncMock( - return_value=mock_create_disbursement_envelope(True) - ) - mock_service_instance.construct_disbursement_envelope_success_response = AsyncMock() - - mock_service_get_component.return_value = mock_service_instance - - expected_payload = mock_create_disbursement_envelope(True) - expected_response = DisbursementEnvelopeResponse( - response_status=ResponseStatus.SUCCESS, response_payload=expected_payload - ) - mock_service_instance.construct_disbursement_envelope_success_response.return_value = ( - expected_response - ) - controller = DisbursementEnvelopeController() - request_payload = DisbursementEnvelopeRequest( - request_payload=DisbursementEnvelopePayload( - benefit_program_mnemonic="TEST123", - disbursement_frequency="Monthly", - cycle_code_mnemonic="CYCLE42", - number_of_beneficiaries=100, - number_of_disbursements=100, - total_disbursement_amount=5000.00, - disbursement_schedule_date=datetime.date(datetime.utcnow()), - ) - ) - - actual_response = await controller.create_disbursement_envelope(request_payload) - - assert actual_response == expected_response - - -@pytest.mark.asyncio -@patch("openg2p_g2p_bridge_api.services.DisbursementEnvelopeService.get_component") -@pytest.mark.parametrize("error_code", list(G2PBridgeErrorCodes)) -async def test_create_disbursement_envelope_errors( - mock_service_get_component, error_code -): - mock_service_instance = AsyncMock() - mock_service_instance.create_disbursement_envelope.side_effect = ( - lambda request: mock_create_disbursement_envelope(False, error_code) - ) - mock_service_instance.construct_disbursement_envelope_error_response = AsyncMock() - - mock_service_get_component.return_value = mock_service_instance - - error_response = DisbursementEnvelopeResponse( - response_status=ResponseStatus.FAILURE, - response_error_code=error_code, - ) - - mock_service_instance.construct_disbursement_envelope_error_response.return_value = ( - error_response - ) - - controller = DisbursementEnvelopeController() - - request_payload = DisbursementEnvelopeRequest( - request_payload=DisbursementEnvelopePayload( - benefit_program_mnemonic="", # Trigger the error - disbursement_frequency="Monthly", - cycle_code_mnemonic="CYCLE42", - number_of_beneficiaries=100, - number_of_disbursements=100, - total_disbursement_amount=5000.00, - disbursement_schedule_date=datetime.date(datetime.utcnow()), - ) - ) - - actual_response = await controller.create_disbursement_envelope(request_payload) - - assert ( - actual_response == error_response - ), f"The response did not match the expected error response for {error_code}." - - -def mock_cancel_disbursement_envelope(is_valid, error_code=None): - if not is_valid: - raise DisbursementEnvelopeException( - code=error_code, message=f"{error_code} error." - ) - - return DisbursementEnvelopePayload( - disbursement_envelope_id="env123", - benefit_program_mnemonic="TEST123", - disbursement_frequency="Monthly", - cycle_code_mnemonic="CYCLE42", - number_of_beneficiaries=100, - number_of_disbursements=100, - total_disbursement_amount=5000.00, - disbursement_schedule_date=datetime.date(datetime.utcnow()), - ) - - -@pytest.mark.asyncio -@patch("openg2p_g2p_bridge_api.services.DisbursementEnvelopeService.get_component") -async def test_cancel_disbursement_envelope_success(mock_service_get_component): - mock_service_instance = AsyncMock() - mock_service_instance.cancel_disbursement_envelope = AsyncMock( - return_value=mock_cancel_disbursement_envelope(True) - ) - mock_service_instance.construct_disbursement_envelope_success_response = AsyncMock() - - mock_service_get_component.return_value = mock_service_instance - - successful_payload = mock_cancel_disbursement_envelope(True) - expected_response = DisbursementEnvelopeResponse( - response_status=ResponseStatus.SUCCESS, response_payload=successful_payload - ) - mock_service_instance.construct_disbursement_envelope_success_response.return_value = ( - expected_response - ) - - controller = DisbursementEnvelopeController() - request_payload = DisbursementEnvelopeRequest( - request_payload=DisbursementEnvelopePayload(disbursement_envelope_id="env123") - ) - - actual_response = await controller.cancel_disbursement_envelope(request_payload) - assert actual_response == expected_response - - -@pytest.mark.asyncio -@patch("openg2p_g2p_bridge_api.services.DisbursementEnvelopeService.get_component") -@pytest.mark.parametrize( - "error_code", - [ - G2PBridgeErrorCodes.DISBURSEMENT_ENVELOPE_NOT_FOUND, - G2PBridgeErrorCodes.DISBURSEMENT_ENVELOPE_ALREADY_CANCELED, - ], -) -async def test_cancel_disbursement_envelope_failure( - mock_service_get_component, error_code -): - mock_service_instance = AsyncMock() - mock_service_instance.cancel_disbursement_envelope.side_effect = ( - lambda request: mock_cancel_disbursement_envelope(False, error_code) - ) - mock_service_instance.construct_disbursement_envelope_error_response = AsyncMock() - - mock_service_get_component.return_value = mock_service_instance - - error_response = DisbursementEnvelopeResponse( - response_status=ResponseStatus.FAILURE, - response_error_code=error_code.value, - ) - mock_service_instance.construct_disbursement_envelope_error_response.return_value = ( - error_response - ) - - controller = DisbursementEnvelopeController() - request_payload = DisbursementEnvelopeRequest( - request_payload=DisbursementEnvelopePayload( - disbursement_envelope_id="env123" # Assuming this ID triggers the error - ) - ) - - actual_response = await controller.cancel_disbursement_envelope(request_payload) - assert ( - actual_response == error_response - ), f"The response for {error_code} did not match the expected error response." diff --git a/openg2p-g2p-bridge-example-bank-api/tests/test_example_bank_api.py b/openg2p-g2p-bridge-example-bank-api/tests/test_example_bank_api.py new file mode 100644 index 0000000..3b79f3d --- /dev/null +++ b/openg2p-g2p-bridge-example-bank-api/tests/test_example_bank_api.py @@ -0,0 +1,2 @@ +def test_always_passes(): + assert True diff --git a/openg2p-g2p-bridge-example-bank-celery/.pre-commit-config.yaml b/openg2p-g2p-bridge-example-bank-celery/.pre-commit-config.yaml deleted file mode 100644 index 336b7e1..0000000 --- a/openg2p-g2p-bridge-example-bank-celery/.pre-commit-config.yaml +++ /dev/null @@ -1,49 +0,0 @@ -exclude: | - (?x) - # We don't want to mess with tool-generated files - .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/| - # Maybe reactivate this when all README files include prettier ignore tags? - ^README\.md$| - # Repos using Sphinx to generate docs don't need prettying - ^docs/_templates/.*\.html$| - # You don't usually want a bot to modify your legal texts - (LICENSE.*|COPYING.*) -default_language_version: - python: python3 -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: debug-statements - - id: fix-encoding-pragma - args: ["--remove"] - - id: check-case-conflict - - id: check-docstring-first - - id: check-executables-have-shebangs - - id: check-merge-conflict - - id: check-symlinks - - id: check-toml - - id: check-yaml - args: - - --unsafe - - id: mixed-line-ending - args: ["--fix=lf"] - - repo: https://github.com/asottile/pyupgrade - rev: v3.11.0 - hooks: - - id: pyupgrade - args: - - --py3-plus - - --keep-runtime-typing - - repo: https://github.com/psf/black - rev: 23.9.1 - hooks: - - id: black - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.289 - hooks: - - id: ruff - args: - - --fix diff --git a/openg2p-g2p-bridge-example-bank-celery/.ruff.toml b/openg2p-g2p-bridge-example-bank-celery/.ruff.toml deleted file mode 100644 index aa1fc5b..0000000 --- a/openg2p-g2p-bridge-example-bank-celery/.ruff.toml +++ /dev/null @@ -1,16 +0,0 @@ -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "C", # flake8-comprehensions - "B", # flake8-bugbear -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "C901", # too complex -] - -[per-file-ignores] -"__init__.py" = ["F401"] diff --git a/openg2p-g2p-bridge-example-bank-celery/Dockerfile b/openg2p-g2p-bridge-example-bank-celery/Dockerfile new file mode 100644 index 0000000..6bd0140 --- /dev/null +++ b/openg2p-g2p-bridge-example-bank-celery/Dockerfile @@ -0,0 +1,31 @@ +FROM bitnami/python:3.10.13-debian-11-r24 + +ARG container_user=openg2p +ARG container_user_group=openg2p +ARG container_user_uid=1001 +ARG container_user_gid=1001 + +RUN groupadd -g ${container_user_gid} ${container_user_group} \ + && useradd -mN -u ${container_user_uid} -G ${container_user_group} -s /bin/bash ${container_user} + +WORKDIR /app + +RUN install_packages libpq-dev \ + && apt-get clean && rm -rf /var/lib/apt/lists /var/cache/apt/archives + +RUN chown -R ${container_user}:${container_user_group} /app +USER ${container_user} + +ADD --chown=${container_user}:${container_user_group} . /app/src +ADD --chown=${container_user}:${container_user_group} main.py /app + +RUN python3 -m venv venv \ + && . ./venv/bin/activate +RUN python3 -m pip install \ + git+https://github.com/openg2p/openg2p-fastapi-common@1.1.0\#subdirectory=openg2p-fastapi-common \ + git+https://github.com/openg2p/openg2p-fastapi-common@1.1.0\#subdirectory=openg2p-fastapi-auth \ + git+https://github.com/openg2p/openg2p-g2p-bridge-example-bank@1.0.0\#subdirectory=openg2p-g2p-bridge-example-bank-models \ + ./src + +CMD python3 main.py migrate; \ + celery -A main.celery_app worker --loglevel=info --beat diff --git a/openg2p-g2p-bridge-example-bank-celery/src/openg2p_g2p_bridge_example_bank_celery/app.py b/openg2p-g2p-bridge-example-bank-celery/src/openg2p_g2p_bridge_example_bank_celery/app.py index 53a7a3a..bf63cb1 100644 --- a/openg2p-g2p-bridge-example-bank-celery/src/openg2p_g2p_bridge_example_bank_celery/app.py +++ b/openg2p-g2p-bridge-example-bank-celery/src/openg2p_g2p_bridge_example_bank_celery/app.py @@ -43,7 +43,7 @@ def get_engine(): celery_app.conf.beat_schedule = { "process_payments": { - "task": "process_payments", + "task": "process_payments_beat_producer", "schedule": _config.process_payment_frequency, } } diff --git a/openg2p-g2p-bridge-example-bank-celery/test-requirements.txt b/openg2p-g2p-bridge-example-bank-celery/test-requirements.txt deleted file mode 100644 index 4f53afa..0000000 --- a/openg2p-g2p-bridge-example-bank-celery/test-requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -pytest-cov -git+https://github.com/openg2p/openg2p-fastapi-common@develop#subdirectory=openg2p-fastapi-common -git+https://github.com/openg2p/openg2p-fastapi-common@develop#subdirectory=openg2p-fastapi-auth diff --git a/openg2p-g2p-bridge-example-bank-api/test-requirements.txt b/test-requirements.txt similarity index 100% rename from openg2p-g2p-bridge-example-bank-api/test-requirements.txt rename to test-requirements.txt