From 1713c1619eee571ec00c6fd5946357cfb2f2e888 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 5 Mar 2024 12:22:14 -0800 Subject: [PATCH 01/87] 19910 - Python 3.12.2 upgrade (#1435) * Update docker files to python 3.12.2 * Update CI flows * update requirements * Update grcpiostatus and protobuf * Add python 3.12 to test CI * Add in more python 3.12 * Fix flake8 * Get the bulk of the unit tests working, upgrade marshmellow, upgrade flask, upgrade sqlalchemy - still need to fix "tracing", still need to fix sql-continuum, still need to fix exception handling, still need to fix JWT library flask-oidc-jwt (doesn't work with Flask 3.0 without update) * Small cleanup * remove autoflush param, didn't mean for it to be in there * Update requirements to pull from github, where we fixed the library * Clear out some old TODOs * clear out old todo * remove todo * old todo * todo removal * Remove todo * change default database name * Use more modern database * nuke tracer for now * add in ci for queue_python_upgrade * Fix linting * remove setup.cfg changes * fix unit test * Small unit test fix * use utcnow instead of now. * Move created on to utcnow * unit test fixes * Small unit test tweak * use datetime.now(tz=timezone.utc) --- .github/workflows/bcol-api-ci.yml | 10 +- .github/workflows/events-listener-ci.yml | 10 +- .github/workflows/ftp-poller-ci.yml | 10 +- .github/workflows/notebook-report-ci.yml | 10 +- .github/workflows/pay-admin-ci.yml | 10 +- .github/workflows/pay-api-ci.yml | 19 +- .github/workflows/payment-jobs-ci.yml | 10 +- .../workflows/payment-reconciliations-ci.yml | 10 +- .github/workflows/report-api-ci.yml | 10 +- bcol-api/Dockerfile | 2 +- jobs/ftp-poller/Dockerfile | 2 +- jobs/notebook-report/Dockerfile | 2 +- jobs/payment-jobs/Dockerfile | 2 +- pay-admin/Dockerfile | 2 +- pay-api/Dockerfile | 2 +- pay-api/devops/vaults.json | 9 - pay-api/docs/dotenv_template | 8 +- pay-api/migrations/README.md | 2 +- ...caed21_populate_receipt_number_payments.py | 7 +- .../1283463d3b4a_new_payment_method_tables.py | 4 +- ..._update_refund_payment_date_to_invoices.py | 13 +- .../2023_09_28_456234145e5e_eft_processing.py | 1 - ...fix_payment_account_id_in_payment_table.py | 4 +- ...3_ppr_and_dissolution_filing_type_codes.py | 10 +- .../4cc39da0bee7_payment_system_account.py | 131 ------------- .../56c5db141909_refactor_payment_account.py | 6 +- .../5d9997f7e649_variable_fee_code.py | 6 +- .../migrations/versions/609b98d87a72_rpt.py | 6 +- .../migrations/versions/643790dd3334_cso.py | 4 +- pay-api/migrations/versions/6a6b042b831a_.py | 4 +- .../6f0fe9f23d8c_distribution_code_changes.py | 12 +- .../7c19ee3a58aa_invoice_amount_to_payment.py | 4 +- .../8652bf9c03ff_payment_to_invoice.py | 4 +- ...44955fb6_add_created_user_display_names.py | 4 +- .../a11be9fe1a6a_distribution_code.py | 6 +- .../b336780735dc_registration_filing_type.py | 6 +- .../b6e28faea978_rename_tables_to_plurals.py | 8 +- .../versions/b7443d501d98_entity_fee_codes.py | 4 +- .../c67213f860ea_incorporation_fee_codes.py | 6 +- .../versions/c871202927f0_rush_fee_code.py | 6 +- .../versions/dbe9dc38ac33_firms_fee_codes.py | 6 +- pay-api/migrations/versions/e748b5c19247_.py | 4 +- pay-api/pre_hook_create_database.py | 2 +- pay-api/requirements.txt | 110 +++++------ pay-api/requirements/dev.txt | 2 +- pay-api/requirements/prod.txt | 16 +- pay-api/setup.cfg | 2 +- pay-api/src/pay_api/__init__.py | 2 +- pay-api/src/pay_api/config.py | 15 -- .../pay_api/factory/payment_system_factory.py | 3 +- pay-api/src/pay_api/models/base_model.py | 29 ++- pay-api/src/pay_api/models/base_schema.py | 3 +- .../pay_api/models/cfs_account_status_code.py | 3 +- pay-api/src/pay_api/models/corp_type.py | 3 +- pay-api/src/pay_api/models/credit.py | 3 +- pay-api/src/pay_api/models/custom_query.py | 5 +- pay-api/src/pay_api/models/db.py | 10 +- .../models/disbursement_status_code.py | 3 +- .../src/pay_api/models/distribution_code.py | 3 +- .../pay_api/models/eft_process_status_code.py | 3 +- pay-api/src/pay_api/models/error_code.py | 3 +- pay-api/src/pay_api/models/fee_code.py | 3 +- pay-api/src/pay_api/models/fee_schedule.py | 5 +- pay-api/src/pay_api/models/filing_type.py | 3 +- pay-api/src/pay_api/models/invoice.py | 8 +- .../models/invoice_reference_status_code.py | 3 +- .../src/pay_api/models/invoice_status_code.py | 3 +- .../pay_api/models/line_item_status_code.py | 3 +- .../models/notification_status_code.py | 3 +- pay-api/src/pay_api/models/payment.py | 5 +- .../src/pay_api/models/payment_line_item.py | 3 +- pay-api/src/pay_api/models/payment_method.py | 3 +- .../src/pay_api/models/payment_status_code.py | 3 +- pay-api/src/pay_api/models/payment_system.py | 3 +- pay-api/src/pay_api/models/receipt.py | 3 +- pay-api/src/pay_api/models/refund.py | 3 +- pay-api/src/pay_api/models/routing_slip.py | 2 +- .../models/routing_slip_status_code.py | 3 +- pay-api/src/pay_api/models/statement.py | 69 +++---- .../src/pay_api/models/statement_invoices.py | 3 +- .../pay_api/models/statement_recipients.py | 5 +- .../src/pay_api/models/statement_settings.py | 3 +- .../pay_api/models/transaction_status_code.py | 3 +- pay-api/src/pay_api/resources/ops.py | 2 +- pay-api/src/pay_api/resources/v1/account.py | 7 - .../resources/v1/account_statements.py | 4 - .../v1/account_statements_notifications.py | 3 - .../v1/account_statements_settings.py | 3 - pay-api/src/pay_api/resources/v1/code.py | 3 - .../src/pay_api/resources/v1/distributions.py | 7 - .../pay_api/resources/v1/eft_short_names.py | 4 - .../pay_api/resources/v1/fas/routing_slip.py | 10 - pay-api/src/pay_api/resources/v1/fee.py | 2 - .../src/pay_api/resources/v1/fee_schedule.py | 2 - pay-api/src/pay_api/resources/v1/invoice.py | 7 - pay-api/src/pay_api/resources/v1/invoices.py | 2 - .../resources/v1/non_sufficient_funds.py | 3 - pay-api/src/pay_api/resources/v1/payment.py | 3 - .../src/pay_api/resources/v1/transaction.py | 5 - .../pay_api/services/base_payment_system.py | 10 +- .../src/pay_api/services/distribution_code.py | 2 +- .../src/pay_api/services/eft_short_names.py | 6 +- .../src/pay_api/services/fas/routing_slip.py | 2 - pay-api/src/pay_api/services/fee_schedule.py | 6 +- .../pay_api/services/gcp_queue_publisher.py | 13 +- .../pay_api/services/internal_pay_service.py | 3 +- .../pay_api/services/non_sufficient_funds.py | 2 +- pay-api/src/pay_api/services/payment.py | 2 +- .../src/pay_api/services/payment_account.py | 17 +- .../pay_api/services/payment_transaction.py | 13 +- .../src/pay_api/services/queue_publisher.py | 83 -------- pay-api/src/pay_api/services/statement.py | 5 +- .../pay_api/services/statement_settings.py | 2 +- pay-api/src/pay_api/utils/handlers.py | 29 --- pay-api/src/pay_api/utils/logging.py | 7 +- pay-api/src/pay_api/utils/trace.py | 22 --- pay-api/src/pay_api/utils/util.py | 18 +- pay-api/tests/conftest.py | 182 +++++------------- pay-api/tests/docker/docker-compose.yml | 12 -- pay-api/tests/docker/nginx.conf | 2 +- pay-api/tests/unit/api/test_fee.py | 5 +- pay-api/tests/unit/api/test_ops.py | 11 +- pay-api/tests/unit/api/test_partial_refund.py | 4 +- pay-api/tests/unit/api/test_receipt.py | 9 +- pay-api/tests/unit/api/test_refund.py | 14 +- pay-api/tests/unit/api/test_transaction.py | 4 +- .../unit/services/test_distribution_code.py | 2 +- .../unit/services/test_payment_transaction.py | 14 +- .../unit/services/test_queue_publisher.py | 119 ------------ pay-api/tests/unit/services/test_refund.py | 3 - pay-api/tests/unit/services/test_statement.py | 5 +- pay-api/tests/unit/services/utils.py | 40 ---- pay-api/tests/utilities/base_test.py | 34 ++-- queue_services/events-listener/Dockerfile | 2 +- .../payment-reconciliations/Dockerfile | 2 +- report-api/Dockerfile | 2 +- 136 files changed, 480 insertions(+), 1053 deletions(-) delete mode 100644 pay-api/src/pay_api/services/queue_publisher.py delete mode 100644 pay-api/src/pay_api/utils/handlers.py delete mode 100644 pay-api/src/pay_api/utils/trace.py delete mode 100644 pay-api/tests/unit/services/test_queue_publisher.py delete mode 100644 pay-api/tests/unit/services/utils.py diff --git a/.github/workflows/bcol-api-ci.yml b/.github/workflows/bcol-api-ci.yml index a04cca67b..19a136f4e 100644 --- a/.github/workflows/bcol-api-ci.yml +++ b/.github/workflows/bcol-api-ci.yml @@ -28,7 +28,7 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - uses: actions/checkout@v3 @@ -79,6 +79,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -118,6 +122,10 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - uses: actions/checkout@v3 - name: build to check strictness diff --git a/.github/workflows/events-listener-ci.yml b/.github/workflows/events-listener-ci.yml index f1084640d..5ab9ccc96 100644 --- a/.github/workflows/events-listener-ci.yml +++ b/.github/workflows/events-listener-ci.yml @@ -29,7 +29,7 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - uses: actions/checkout@v3 @@ -58,6 +58,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -95,6 +99,10 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - uses: actions/checkout@v3 - name: build to check strictness diff --git a/.github/workflows/ftp-poller-ci.yml b/.github/workflows/ftp-poller-ci.yml index 2142bcf0a..35e0be77b 100644 --- a/.github/workflows/ftp-poller-ci.yml +++ b/.github/workflows/ftp-poller-ci.yml @@ -28,7 +28,7 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - uses: actions/checkout@v3 @@ -63,6 +63,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -100,6 +104,10 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - uses: actions/checkout@v3 - name: build to check strictness diff --git a/.github/workflows/notebook-report-ci.yml b/.github/workflows/notebook-report-ci.yml index c0aa4f503..8a7a8fffa 100644 --- a/.github/workflows/notebook-report-ci.yml +++ b/.github/workflows/notebook-report-ci.yml @@ -28,7 +28,7 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - uses: actions/checkout@v3 @@ -55,6 +55,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -92,6 +96,10 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - uses: actions/checkout@v3 - name: build to check strictness diff --git a/.github/workflows/pay-admin-ci.yml b/.github/workflows/pay-admin-ci.yml index 97bfefdd6..ec28da2ad 100644 --- a/.github/workflows/pay-admin-ci.yml +++ b/.github/workflows/pay-admin-ci.yml @@ -29,7 +29,7 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - uses: actions/checkout@v3 @@ -58,6 +58,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -95,6 +99,10 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - uses: actions/checkout@v3 - name: build to check strictness diff --git a/.github/workflows/pay-api-ci.yml b/.github/workflows/pay-api-ci.yml index 65f1656f9..0a6971992 100644 --- a/.github/workflows/pay-api-ci.yml +++ b/.github/workflows/pay-api-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - queue_python_upgrade paths: - "pay-api/**" @@ -28,7 +29,7 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - uses: actions/checkout@v3 @@ -52,7 +53,8 @@ jobs: needs: setup-job env: FLASK_ENV: "testing" - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + # Needs different database than POSTGRES otherwise dropping database doesn't work + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" TEST_NATS_DOCKER: "YES" USE_TEST_KEYCLOAK_DOCKER: "YES" USE_DOCKER_MOCK: "YES" @@ -72,13 +74,18 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: - image: postgres:12 + image: postgres:15.6 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + # Needs different database than POSTGRES otherwise dropping database doesn't work + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck @@ -109,6 +116,10 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - uses: actions/checkout@v3 - name: build to check strictness diff --git a/.github/workflows/payment-jobs-ci.yml b/.github/workflows/payment-jobs-ci.yml index cd89f0865..048cb1b74 100644 --- a/.github/workflows/payment-jobs-ci.yml +++ b/.github/workflows/payment-jobs-ci.yml @@ -30,7 +30,7 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - uses: actions/checkout@v3 @@ -65,6 +65,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -102,6 +106,10 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - uses: actions/checkout@v3 - name: build to check strictness diff --git a/.github/workflows/payment-reconciliations-ci.yml b/.github/workflows/payment-reconciliations-ci.yml index 6e06ec15d..415889d0a 100644 --- a/.github/workflows/payment-reconciliations-ci.yml +++ b/.github/workflows/payment-reconciliations-ci.yml @@ -30,7 +30,7 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - uses: actions/checkout@v3 @@ -70,6 +70,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -107,6 +111,10 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - uses: actions/checkout@v3 - name: build to check strictness diff --git a/.github/workflows/report-api-ci.yml b/.github/workflows/report-api-ci.yml index aa67e68e0..57359f7c4 100644 --- a/.github/workflows/report-api-ci.yml +++ b/.github/workflows/report-api-ci.yml @@ -28,7 +28,7 @@ jobs: strategy: matrix: - python-version: [3.8] + python-version: [3.12] steps: - uses: actions/checkout@v3 @@ -61,6 +61,10 @@ jobs: runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + services: postgres: image: postgres:12 @@ -98,6 +102,10 @@ jobs: needs: setup-job runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.12] + steps: - uses: actions/checkout@v3 - name: build to check strictness diff --git a/bcol-api/Dockerfile b/bcol-api/Dockerfile index f31a0a12f..288189b4e 100644 --- a/bcol-api/Dockerfile +++ b/bcol-api/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye ARG VCS_REF="missing" ARG BUILD_DATE="missing" diff --git a/jobs/ftp-poller/Dockerfile b/jobs/ftp-poller/Dockerfile index de6bb744f..26740742e 100644 --- a/jobs/ftp-poller/Dockerfile +++ b/jobs/ftp-poller/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye # ======================================================================================================== # Install go-crond (from https://github.com/BCDevOps/go-crond) diff --git a/jobs/notebook-report/Dockerfile b/jobs/notebook-report/Dockerfile index cab50539f..06a79a2b3 100644 --- a/jobs/notebook-report/Dockerfile +++ b/jobs/notebook-report/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye ARG VCS_REF="missing" ARG BUILD_DATE="missing" diff --git a/jobs/payment-jobs/Dockerfile b/jobs/payment-jobs/Dockerfile index c76d801ed..8b940bf5f 100644 --- a/jobs/payment-jobs/Dockerfile +++ b/jobs/payment-jobs/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye # ======================================================================================================== # Install go-crond (from https://github.com/BCDevOps/go-crond) diff --git a/pay-admin/Dockerfile b/pay-admin/Dockerfile index 7ec36797c..a6fbe5388 100644 --- a/pay-admin/Dockerfile +++ b/pay-admin/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye ARG VCS_REF="missing" ARG BUILD_DATE="missing" diff --git a/pay-api/Dockerfile b/pay-api/Dockerfile index 7ec36797c..a6fbe5388 100644 --- a/pay-api/Dockerfile +++ b/pay-api/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye ARG VCS_REF="missing" ARG BUILD_DATE="missing" diff --git a/pay-api/devops/vaults.json b/pay-api/devops/vaults.json index 2eef392fc..3bcf4805a 100644 --- a/pay-api/devops/vaults.json +++ b/pay-api/devops/vaults.json @@ -13,15 +13,6 @@ "sbc-auth-admin" ] }, - { - "vault": "nats", - "application": [ - "base", - "account-events-listener", - "account-mailer", - "payment" - ] - }, { "vault": "gcp-queue", "application": [ diff --git a/pay-api/docs/dotenv_template b/pay-api/docs/dotenv_template index 5a8d961a8..af1dbf20e 100644 --- a/pay-api/docs/dotenv_template +++ b/pay-api/docs/dotenv_template @@ -43,7 +43,7 @@ JWT_OIDC_AUDIENCE= JWT_OIDC_CLIENT_SECRET= JWT_OIDC_JWKS_CACHE_TIMEOUT= -KEYCLOAK_ADMIN_CLIENTID=' +KEYCLOAK_ADMIN_CLIENTID= KEYCLOAK_ADMIN_SECRET= KEYCLOAK_AUTH_AUDIENCE= KEYCLOAK_AUTH_CLIENT_SECRET= @@ -58,12 +58,6 @@ BCOL_LINK_CODE= BCOL_LDAP_USER_DN_PATTERN= BCOL_LDAP_SERVER= -NATS_CLIENT_NAME= -NATS_CLUSTER_ID= -NATS_QUEUE= -NATS_SERVERS= -NATS_SUBJECT= - AUTH_API_ENDPOINT= AUTH_WEB_PAY_TRANSACTION_URL= diff --git a/pay-api/migrations/README.md b/pay-api/migrations/README.md index 88c686dc4..a55983d6b 100644 --- a/pay-api/migrations/README.md +++ b/pay-api/migrations/README.md @@ -2,6 +2,6 @@ Run `python manage.py db migrate` -If you are updating a large table (i.e. invoices, invoice_references, etc.) add `op.execute("set statement_timeout=20000;")` to the top of your new migration scripts for upgrade/downgrade. This will prevent the deployment from causing errors in prod when it takes too long to complete (> 20 seconds). +If you are updating a large table (i.e. invoices, invoice_references, etc.) add `op.execute(op.text("set statement_timeout=20000;")` to the top of your new migration scripts for upgrade/downgrade. This will prevent the deployment from causing errors in prod when it takes too long to complete (> 20 seconds).) _Note: If it takes too long this will cause the prehook to fail in the deployment so you will need to watch it and redeploy at a less busy time if necessary_ diff --git a/pay-api/migrations/versions/03b2c7caed21_populate_receipt_number_payments.py b/pay-api/migrations/versions/03b2c7caed21_populate_receipt_number_payments.py index 3dbdc79c1..85cb2f9d4 100644 --- a/pay-api/migrations/versions/03b2c7caed21_populate_receipt_number_payments.py +++ b/pay-api/migrations/versions/03b2c7caed21_populate_receipt_number_payments.py @@ -7,6 +7,7 @@ """ from alembic import op +from sqlalchemy import text # revision identifiers, used by Alembic. revision = '03b2c7caed21' @@ -17,14 +18,14 @@ def upgrade(): conn = op.get_bind() - res = conn.execute(f"select id, invoice_number from payments where receipt_number is null;") + res = conn.execute(text(f"select id, invoice_number from payments where receipt_number is null;")) results = res.fetchall() for result in results: pay_id = result[0] invoice_number = result[1] - res = conn.execute(f"select r.receipt_number from receipts r left join invoice_references ir " + res = conn.execute(text(f"select r.receipt_number from receipts r left join invoice_references ir " f"on ir.invoice_id=r.invoice_id where ir.status_code='COMPLETED' " - f"and invoice_number='{invoice_number}'") + f"and invoice_number='{invoice_number}'")) receipt_number_result = res.fetchall() if receipt_number_result: receipt_number = receipt_number_result[0][0] diff --git a/pay-api/migrations/versions/1283463d3b4a_new_payment_method_tables.py b/pay-api/migrations/versions/1283463d3b4a_new_payment_method_tables.py index 379c0c2f0..10443a569 100644 --- a/pay-api/migrations/versions/1283463d3b4a_new_payment_method_tables.py +++ b/pay-api/migrations/versions/1283463d3b4a_new_payment_method_tables.py @@ -101,8 +101,8 @@ def upgrade(): # Update invoice which have credit_account_id with matching details from cfs_account conn = op.get_bind() - res = conn.execute("select cfs.id, credit.id from cfs_account cfs, credit_payment_account credit " - "where credit.paybc_account=cfs.cfs_account and credit.account_id=cfs.account_id;") + res = conn.execute(sa.text("select cfs.id, credit.id from cfs_account cfs, credit_payment_account credit " + "where credit.paybc_account=cfs.cfs_account and credit.account_id=cfs.account_id;")) cfs_accounts = res.fetchall() for cfs_account in cfs_accounts: cfs_id = cfs_account[0] diff --git a/pay-api/migrations/versions/2023_02_23_d2d9864164d1_update_refund_payment_date_to_invoices.py b/pay-api/migrations/versions/2023_02_23_d2d9864164d1_update_refund_payment_date_to_invoices.py index dd5dc7f5e..0f9b2df54 100644 --- a/pay-api/migrations/versions/2023_02_23_d2d9864164d1_update_refund_payment_date_to_invoices.py +++ b/pay-api/migrations/versions/2023_02_23_d2d9864164d1_update_refund_payment_date_to_invoices.py @@ -6,6 +6,7 @@ """ from alembic import op +from sqlalchemy import text # revision identifiers, used by Alembic. @@ -21,29 +22,29 @@ def upgrade(): # 2. Update payment_date with invoice_id. op.execute("set statement_timeout=20000;") conn = op.get_bind() - res = conn.execute(f'select i.id as "id", p.payment_date as "date"\ + res = conn.execute(text(f'select i.id as "id", p.payment_date as "date"\ from invoice_references ir \ inner join invoices i on i.id = ir.invoice_id \ inner join payments p on p.invoice_number = ir.invoice_number \ - where p.payment_date IS NOT null AND i.payment_date IS null;') + where p.payment_date IS NOT null AND i.payment_date IS null;')) payments = res.fetchall() for payment in payments: invoice_id = payment[0] payment_date = payment[1] - op.execute(f"update invoices set payment_date=\'{payment_date}\' where id = {invoice_id}") + op.execute(text(f"update invoices set payment_date=\'{payment_date}\' where id = {invoice_id}")) # 1. Find all id and refund date from invoice table linking with refunds table. # 2. Update refund_date with invoice_id. - res = conn.execute(f'select i.id as "id", r.requested_date as "date"\ + res = conn.execute(text(f'select i.id as "id", r.requested_date as "date"\ from invoices i \ inner join refunds r on r.invoice_id = i.id\ where i.invoice_status_code = \'REFUNDED\'\ - and i.refund_date is null;') + and i.refund_date is null;')) refunds = res.fetchall() for refund in refunds: id = refund[0] refund_date = refund[1] - op.execute(f"update invoices set refund_date=\'{refund_date}\' where id = {id}") + op.execute(text(f"update invoices set refund_date=\'{refund_date}\' where id = {id}")) def downgrade(): diff --git a/pay-api/migrations/versions/2023_09_28_456234145e5e_eft_processing.py b/pay-api/migrations/versions/2023_09_28_456234145e5e_eft_processing.py index 0bd18d990..8ad95bafd 100644 --- a/pay-api/migrations/versions/2023_09_28_456234145e5e_eft_processing.py +++ b/pay-api/migrations/versions/2023_09_28_456234145e5e_eft_processing.py @@ -56,7 +56,6 @@ def upgrade(): sa.Column('completed_on', sa.DateTime(), nullable=True), sa.Column('line_number', sa.Integer(), nullable=False), sa.Column('line_type', sa.String(20), nullable=False), - sa.Column('batch_number', sa.String(10), nullable=True), sa.Column('sequence_number', sa.String(3), nullable=True), sa.Column('jv_type', sa.String(1), nullable=True), sa.Column('jv_number', sa.String(10), nullable=True), diff --git a/pay-api/migrations/versions/331c2cdb0b94_fix_payment_account_id_in_payment_table.py b/pay-api/migrations/versions/331c2cdb0b94_fix_payment_account_id_in_payment_table.py index 394956987..97b12f07a 100644 --- a/pay-api/migrations/versions/331c2cdb0b94_fix_payment_account_id_in_payment_table.py +++ b/pay-api/migrations/versions/331c2cdb0b94_fix_payment_account_id_in_payment_table.py @@ -30,12 +30,12 @@ def upgrade(): # 2. Find the account id from invoice table linking with invoice_reference table. # 3. Update payment with payment account id. conn = op.get_bind() - res = conn.execute(f"select id,invoice_number from payment where payment_account_id is null;") + res = conn.execute(sa.text(f"select id,invoice_number from payment where payment_account_id is null;")) results = res.fetchall() for result in results: pay_id = result[0] invoice_number = result[1] - res = conn.execute(f"select payment_account_id from invoice where id=(select invoice_id from invoice_reference where invoice_number='{invoice_number}');") + res = conn.execute(sa.text(f"select payment_account_id from invoice where id=(select invoice_id from invoice_reference where invoice_number='{invoice_number}');")) payment_account_id_result = res.fetchall() if payment_account_id_result: payment_account_id = payment_account_id_result[0][0] diff --git a/pay-api/migrations/versions/4cb0dc8e0013_ppr_and_dissolution_filing_type_codes.py b/pay-api/migrations/versions/4cb0dc8e0013_ppr_and_dissolution_filing_type_codes.py index a3e5c2352..4172081c9 100644 --- a/pay-api/migrations/versions/4cb0dc8e0013_ppr_and_dissolution_filing_type_codes.py +++ b/pay-api/migrations/versions/4cb0dc8e0013_ppr_and_dissolution_filing_type_codes.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String +from sqlalchemy import Date, String, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -410,11 +410,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='PPR' and filing_type_code='SSRCH'") + text(f"select fee_schedule_id from fee_schedules where corp_type_code='PPR' and filing_type_code='SSRCH'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link = [{ 'distribution_code_id': distribution_code_id, @@ -428,11 +428,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where filing_type_code in ('DIS_VOL', 'DIS_INVOL', 'DIS_ADMIN', 'DIS_LQD', 'DIS_COLQD', 'DIS_RSTR', 'AFDVT', 'SPRLN')") + text(f"select fee_schedule_id from fee_schedules where filing_type_code in ('DIS_VOL', 'DIS_INVOL', 'DIS_ADMIN', 'DIS_LQD', 'DIS_COLQD', 'DIS_RSTR', 'AFDVT', 'SPRLN')")) distr_code_links = [] for result in res.fetchall(): fee_schedule_id = result[0] diff --git a/pay-api/migrations/versions/4cc39da0bee7_payment_system_account.py b/pay-api/migrations/versions/4cc39da0bee7_payment_system_account.py index 50ff74e42..489d64aa2 100644 --- a/pay-api/migrations/versions/4cc39da0bee7_payment_system_account.py +++ b/pay-api/migrations/versions/4cc39da0bee7_payment_system_account.py @@ -78,25 +78,6 @@ def upgrade(): op.add_column('invoice', sa.Column('corp_type_code', sa.String(length=10), nullable=True)) op.create_foreign_key(None, 'invoice', 'corp_type', ['corp_type_code'], ['code']) - # Create temp variable to keep the payment accounts - # payment_accounts: [] = [] - # conn = op.get_bind() - # res = conn.execute( - # f"select id,corp_number,corp_type_code,payment_system_code,account_number,party_number,site_number from payment_account;") - # results = res.fetchall() - # for result in results: - # payment_accounts.append( - # { - # "id": result[0], - # "corp_number": result[1], - # "corp_type_code": result[2], - # "payment_system_code": result[3], - # "account_number": result[4], - # "party_number": result[5], - # "site_number": result[6] - # } - # ) - # Now drop the columns from payment_account table op.drop_index('ix_payment_account_account_number', table_name='payment_account') op.drop_index('ix_payment_account_bcol_account_id', table_name='payment_account') @@ -114,118 +95,6 @@ def upgrade(): # Delete all records from payment_account op.execute('delete from payment_account;') - - # Create admin token to call auth-api - # config = current_app.config - # token_url = config.get( - # 'JWT_OIDC_ISSUER') + '/protocol/openid-connect/token' # https://sso-dev.pathfinder.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/token - # basic_auth_encoded = base64.b64encode( - # bytes(config.get('KEYCLOAK_SERVICE_ACCOUNT_ID') + ':' + config.get('KEYCLOAK_SERVICE_ACCOUNT_SECRET'), - # 'utf-8')).decode('utf-8') - # data = 'grant_type=client_credentials' - # token_response = OAuthService.post(token_url, basic_auth_encoded, AuthHeaderType.BASIC, - # ContentType.FORM_URL_ENCODED, data) - # token = token_response.json().get('access_token') - # - # payment_account_table = table('payment_account', column('auth_account_id', String)) - # credit_payment_account_table = table('credit_payment_account', - # column('corp_number', String), - # column('corp_type_code', String), - # column('paybc_account', String), - # column('paybc_party', String), - # column('paybc_site', String), - # column('account_id', Integer)) - # - # internal_payment_account_table = table('internal_payment_account', - # column('corp_number', String), - # column('corp_type_code', String), - # column('account_id', Integer)) - # - # for payment_account in payment_accounts: - # corp_number = payment_account.get('corp_number') - # payment_system_code = payment_account.get('payment_system_code') - # corp_type_code = payment_account.get('corp_type_code') - # account_number = payment_account.get('account_number') - # party_number = payment_account.get('party_number') - # site_number = payment_account.get('site_number') - # id = payment_account.get('id') - # - # if corp_number: - # # Call Auth-API to get the auth_account id - # auth_search_url = config.get( - # 'AUTH_API_ENDPOINT') + 'orgs?affiliation=' + corp_number.upper() - # orgs_response = None - # try: - # orgs_response = OAuthService.get(auth_search_url, token, AuthHeaderType.BEARER, - # ContentType.JSON).json().get('orgs') - # except Exception as e: - # print(f'--- Error Occured while getting org for affiliation {corp_number}') - # - # if orgs_response and orgs_response[0]: - # auth_account_id = str(orgs_response[0].get('id')) - # else: - # auth_account_id = f'PASSCODE_ACCOUNT_{corp_number}' - # - # res = conn.execute(f"select id from payment_account where auth_account_id = '{auth_account_id}'") - # results = res.fetchall() - # - # credit_payment_account_id = None - # internal_payment_account_id = None - # if results and results[0]: - # payment_account_id = results[0][0] - # else: - # op.bulk_insert( - # payment_account_table, [ - # {"auth_account_id": auth_account_id} - # ] - # ) - # res = conn.execute( - # f"select id from payment_account where auth_account_id = '{auth_account_id}'") - # results = res.fetchall() - # payment_account_id = results[0][0] - # - # if payment_system_code == 'PAYBC': - # print(f'Creating credit account for {payment_account_id}') - # op.bulk_insert( - # credit_payment_account_table, [ - # { - # "corp_number": corp_number, - # "corp_type_code": corp_type_code, - # "paybc_account": account_number, - # "paybc_party": party_number, - # "paybc_site": site_number, - # "account_id": payment_account_id - # } - # ] - # ) - # res = conn.execute( - # f"select id from credit_payment_account where account_id = '{payment_account_id}' and corp_number='{corp_number}' and paybc_account='{account_number}'") - # results = res.fetchall() - # credit_payment_account_id = results[0][0] - # elif payment_system_code == 'INTERNAL': - # print(f'Creating internal payment account for {payment_account_id}') - # op.bulk_insert( - # internal_payment_account_table, [ - # { - # "corp_number": corp_number, - # "corp_type_code": corp_type_code, - # "account_id": payment_account_id - # } - # ] - # ) - # res = conn.execute( - # f"select id from internal_payment_account where account_id = '{payment_account_id}' and corp_number='{corp_number}' and corp_type_code='{corp_type_code}'") - # results = res.fetchall() - # internal_payment_account_id = results[0][0] - # print('Internal ', internal_payment_account_id) - # print('Credit ', credit_payment_account_id) - # if internal_payment_account_id: - # op.execute( - # f"update invoice set internal_account_id = '{internal_payment_account_id}', corp_type_code='{corp_type_code}', business_identifier='{corp_number}' where account_id = '{id}'") - # elif credit_payment_account_id: - # op.execute( - # f"update invoice set credit_account_id = '{credit_payment_account_id}', corp_type_code='{corp_type_code}', business_identifier='{corp_number}' where account_id = '{id}'") - op.drop_column('invoice', 'account_id') # ### end Alembic commands ### diff --git a/pay-api/migrations/versions/56c5db141909_refactor_payment_account.py b/pay-api/migrations/versions/56c5db141909_refactor_payment_account.py index 25bdc7a84..19dca034b 100644 --- a/pay-api/migrations/versions/56c5db141909_refactor_payment_account.py +++ b/pay-api/migrations/versions/56c5db141909_refactor_payment_account.py @@ -65,7 +65,7 @@ def upgrade(): # Update payment account id here conn = op.get_bind() - res = conn.execute("select id, bcol_user_id, bcol_account_id, account_id from bcol_payment_account;") + res = conn.execute(sa.text("select id, bcol_user_id, bcol_account_id, account_id from bcol_payment_account;")) bcol_payment_accounts = res.fetchall() for bcol_payment_account in bcol_payment_accounts: id = bcol_payment_account[0] @@ -82,7 +82,7 @@ def upgrade(): # Insert cfs_account details and mark it as inactive, as all the existing accounts are entity based. op.execute("insert into cfs_account (account_id, cfs_account, cfs_party, cfs_site, is_active) select account_id, paybc_account, paybc_party, paybc_site, false from credit_payment_account") - res = conn.execute("select id, account_id from credit_payment_account;") + res = conn.execute(sa.text("select id, account_id from credit_payment_account;")) credit_payment_accounts = res.fetchall() for credit_payment_account in credit_payment_accounts: id = credit_payment_account[0] @@ -91,7 +91,7 @@ def upgrade(): # Update payment account with bcol information op.execute(f"update invoice set payment_account_id={account_id} where credit_account_id='{id}'") - res = conn.execute("select id, account_id from internal_payment_account;") + res = conn.execute(sa.text("select id, account_id from internal_payment_account;")) internal_payment_accounts = res.fetchall() for internal_payment_account in internal_payment_accounts: id = internal_payment_account[0] diff --git a/pay-api/migrations/versions/5d9997f7e649_variable_fee_code.py b/pay-api/migrations/versions/5d9997f7e649_variable_fee_code.py index 7fc48c4f6..ffbe5a240 100644 --- a/pay-api/migrations/versions/5d9997f7e649_variable_fee_code.py +++ b/pay-api/migrations/versions/5d9997f7e649_variable_fee_code.py @@ -117,12 +117,12 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(sa.text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - "select fee_schedule_id from fee_schedules where corp_type_code='CSO' and " - "filing_type_code in ('CSBVFEE', 'CSBSRCH', 'CSBPDOC', 'CSCRMTFC')") + sa.text("select fee_schedule_id from fee_schedules where corp_type_code='CSO' and " + "filing_type_code in ('CSBVFEE', 'CSBSRCH', 'CSBPDOC', 'CSCRMTFC')")) distr_code_links = [] for result in res.fetchall(): diff --git a/pay-api/migrations/versions/609b98d87a72_rpt.py b/pay-api/migrations/versions/609b98d87a72_rpt.py index c8521d8bc..0f720fdaa 100644 --- a/pay-api/migrations/versions/609b98d87a72_rpt.py +++ b/pay-api/migrations/versions/609b98d87a72_rpt.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String, Boolean +from sqlalchemy import Date, String, Boolean, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -77,11 +77,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='RPT' and filing_type_code='OLLXTFI'") + text(f"select fee_schedule_id from fee_schedules where corp_type_code='RPT' and filing_type_code='OLLXTFI'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link = [{ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/migrations/versions/643790dd3334_cso.py b/pay-api/migrations/versions/643790dd3334_cso.py index c7bfb2eab..33957916e 100644 --- a/pay-api/migrations/versions/643790dd3334_cso.py +++ b/pay-api/migrations/versions/643790dd3334_cso.py @@ -80,11 +80,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(sa.text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='CSO' and filing_type_code='CSBFILE'") + sa.text(f"select fee_schedule_id from fee_schedules where corp_type_code='CSO' and filing_type_code='CSBFILE'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link = [{ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/migrations/versions/6a6b042b831a_.py b/pay-api/migrations/versions/6a6b042b831a_.py index 91adb62ec..da308951f 100644 --- a/pay-api/migrations/versions/6a6b042b831a_.py +++ b/pay-api/migrations/versions/6a6b042b831a_.py @@ -113,13 +113,13 @@ def upgrade(): distribution_code_id_query = "select distribution_code_id from distribution_codes where name = 'Corporate Registry - Searches' and created_by = 'Alembic'" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(sa.text(distribution_code_id_query)) distribution_code_id = res.fetchall()[0][0] new_codes = ('BSRCH',) distr_code_link_values = [] for new_code in new_codes: res = conn.execute( - f"select fee_schedule_id from fee_schedules where filing_type_code='{new_code}' and corp_type_code='BUS'") + sa.text(f"select fee_schedule_id from fee_schedules where filing_type_code='{new_code}' and corp_type_code='BUS'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link_values.append({ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/migrations/versions/6f0fe9f23d8c_distribution_code_changes.py b/pay-api/migrations/versions/6f0fe9f23d8c_distribution_code_changes.py index fd47a9b07..4765ea3ed 100644 --- a/pay-api/migrations/versions/6f0fe9f23d8c_distribution_code_changes.py +++ b/pay-api/migrations/versions/6f0fe9f23d8c_distribution_code_changes.py @@ -74,8 +74,8 @@ def upgrade(): conn = op.get_bind() res = conn.execute( - " select distinct service_fee_memo_name, service_fee_stob, service_fee_line, service_fee_client, service_fee_project_code, service_fee_responsibility_centre, distribution_code_id " - " from distribution_codes where service_fee_memo_name is not null and service_fee_stob is not null;") + sa.text(" select distinct service_fee_memo_name, service_fee_stob, service_fee_line, service_fee_client, service_fee_project_code, service_fee_responsibility_centre, distribution_code_id " + " from distribution_codes where service_fee_memo_name is not null and service_fee_stob is not null;")) results = res.fetchall() for result in results: service_fee_memo_name = result[0] @@ -88,10 +88,10 @@ def upgrade(): print('distribution_code_id..', distribution_code_id) # Check if there is a record existing with the service_fee_responsibility_centre as that's unique. - res = conn.execute(f"select distribution_code_id from distribution_codes " + res = conn.execute(sa.text(f"select distribution_code_id from distribution_codes " f"where stob='{service_fee_stob}' and service_line='{service_fee_line}' and client='{service_fee_client}' " f" and project_code='{service_fee_project_code}' and responsibility_centre='{service_fee_responsibility_centre}' " - f"and service_fee_distribution_code_id is null") + f"and service_fee_distribution_code_id is null")) results = res.fetchall() service_fee_distribution_code_id = None if len(results) == 0 else results[0][0] print('service_fee_distribution_code_id --->', service_fee_distribution_code_id) @@ -102,10 +102,10 @@ def upgrade(): f"'{service_fee_stob}', '{service_fee_project_code}', '{today}', 'alembic');") # Get the inserted record id - res = conn.execute(f"select distribution_code_id from distribution_codes " + res = conn.execute(sa.text(f"select distribution_code_id from distribution_codes " f"where stob='{service_fee_stob}' and service_line='{service_fee_line}' and client='{service_fee_client}' " f" and project_code='{service_fee_project_code}' and responsibility_centre='{service_fee_responsibility_centre}' " - f"and service_fee_distribution_code_id is null") + f"and service_fee_distribution_code_id is null")) service_fee_distribution_code_id = res.fetchall()[0][0] diff --git a/pay-api/migrations/versions/7c19ee3a58aa_invoice_amount_to_payment.py b/pay-api/migrations/versions/7c19ee3a58aa_invoice_amount_to_payment.py index 7a28f0090..40e43055e 100644 --- a/pay-api/migrations/versions/7c19ee3a58aa_invoice_amount_to_payment.py +++ b/pay-api/migrations/versions/7c19ee3a58aa_invoice_amount_to_payment.py @@ -24,8 +24,8 @@ def upgrade(): op.add_column('payment', sa.Column('invoice_amount', sa.Numeric(), nullable=True)) # lookup the invoice table 'total' field by linking with invoice_reference conn = op.get_bind() - res = conn.execute("select inv.total, inv_ref.invoice_number, inv.payment_account_id " - "from invoice inv, invoice_reference inv_ref where inv.id=inv_ref.invoice_id") + res = conn.execute(sa.text("select inv.total, inv_ref.invoice_number, inv.payment_account_id " + "from invoice inv, invoice_reference inv_ref where inv.id=inv_ref.invoice_id")) invoices = res.fetchall() diff --git a/pay-api/migrations/versions/8652bf9c03ff_payment_to_invoice.py b/pay-api/migrations/versions/8652bf9c03ff_payment_to_invoice.py index ccaaf227d..9b28ea9d3 100644 --- a/pay-api/migrations/versions/8652bf9c03ff_payment_to_invoice.py +++ b/pay-api/migrations/versions/8652bf9c03ff_payment_to_invoice.py @@ -47,7 +47,7 @@ def upgrade(): # Select payment method from payment table and update to invoice table. conn = op.get_bind() - res = conn.execute("select id, payment_method_code, payment_status_code from payment ") + res = conn.execute(sa.text("select id, payment_method_code, payment_status_code from payment ")) payments = res.fetchall() for payment in payments: @@ -78,7 +78,7 @@ def upgrade(): # Populate invoice number to payment table. conn = op.get_bind() - res = conn.execute("select id, invoice_status_code, payment_id from invoice ") + res = conn.execute(sa.text("select id, invoice_status_code, payment_id from invoice ")) invoices = res.fetchall() for invoice in invoices: inv_id = invoice[0] diff --git a/pay-api/migrations/versions/8eac44955fb6_add_created_user_display_names.py b/pay-api/migrations/versions/8eac44955fb6_add_created_user_display_names.py index 6c2cbdef9..80d78ef36 100644 --- a/pay-api/migrations/versions/8eac44955fb6_add_created_user_display_names.py +++ b/pay-api/migrations/versions/8eac44955fb6_add_created_user_display_names.py @@ -34,9 +34,9 @@ def upgrade(): # Check if there are records in payment or invoice table to update names conn = op.get_bind() - res = conn.execute(f"select id from payment;") + res = conn.execute(sa.text(f"select id from payment;")) pay_results = res.fetchall() - res = conn.execute(f"select id from invoice;") + res = conn.execute(sa.text(f"select id from invoice;")) inv_results = res.fetchall() if len(pay_results) > 0 or len(inv_results) > 0: diff --git a/pay-api/migrations/versions/a11be9fe1a6a_distribution_code.py b/pay-api/migrations/versions/a11be9fe1a6a_distribution_code.py index ba9ab674f..4ce3791d9 100644 --- a/pay-api/migrations/versions/a11be9fe1a6a_distribution_code.py +++ b/pay-api/migrations/versions/a11be9fe1a6a_distribution_code.py @@ -97,7 +97,7 @@ def upgrade(): 'distribution_code_id': 4, 'created_by': 'Alembic', 'created_on': date.today(), - 'memo_name': 'TODO-VS', + 'memo_name': 'VS', 'service_fee_memo_name': 'SBC Modernization Service Charge', 'start_date': date.today(), }, @@ -105,7 +105,7 @@ def upgrade(): 'distribution_code_id': 5, 'created_by': 'Alembic', 'created_on': date.today(), - 'memo_name': 'TODO-PPR', + 'memo_name': 'PPR', 'service_fee_memo_name': 'SBC Modernization Service Charge', 'start_date': date.today(), } @@ -115,7 +115,7 @@ def upgrade(): conn = op.get_bind() res = conn.execute( - 'select fee_schedule_id, corp_type_code from fee_schedule;') + sa.text('select fee_schedule_id, corp_type_code from fee_schedule;')) results = res.fetchall() for result in results: distribution_code_id = None diff --git a/pay-api/migrations/versions/b336780735dc_registration_filing_type.py b/pay-api/migrations/versions/b336780735dc_registration_filing_type.py index 2fbbbad19..43adb8c88 100644 --- a/pay-api/migrations/versions/b336780735dc_registration_filing_type.py +++ b/pay-api/migrations/versions/b336780735dc_registration_filing_type.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String, Boolean, Float +from sqlalchemy import Date, String, Boolean, Float, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -118,11 +118,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - "select fee_schedule_id from fee_schedules where filing_type_code = 'FRREG'") + text("select fee_schedule_id from fee_schedules where filing_type_code = 'FRREG'")) distr_code_links = [] for result in res.fetchall(): diff --git a/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py b/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py index a473642fc..866243b23 100644 --- a/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py +++ b/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py @@ -74,8 +74,8 @@ def upgrade(): conn = op.get_bind() inspector = Inspector.from_engine(conn) tables = inspector.get_table_names() - metadata = MetaData(conn) - metadata.reflect() + metadata = MetaData() + metadata.reflect(conn) table: str for table in tables: @@ -90,8 +90,8 @@ def downgrade(): conn = op.get_bind() inspector = Inspector.from_engine(conn) tables = inspector.get_table_names() - metadata = MetaData(conn) - metadata.reflect() + metadata = MetaData() + metadata.reflect(conn) table_mapping_reversed = {y: x for x, y in table_mapping.items()} table: str for table in tables: diff --git a/pay-api/migrations/versions/b7443d501d98_entity_fee_codes.py b/pay-api/migrations/versions/b7443d501d98_entity_fee_codes.py index f1b6bf547..44d37f814 100644 --- a/pay-api/migrations/versions/b7443d501d98_entity_fee_codes.py +++ b/pay-api/migrations/versions/b7443d501d98_entity_fee_codes.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, Float, String +from sqlalchemy import Date, Float, String, text from sqlalchemy.sql import column, table @@ -37,7 +37,7 @@ def upgrade(): ) # Patch to adjust manual code entries in other dev/test/prod conn = op.get_bind() - res = conn.execute("select * from corp_type where code='BEN'") + res = conn.execute(text("select * from corp_type where code='BEN'")) ben_code = res.fetchall() if len(ben_code) == 0: # Insert BEN diff --git a/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py b/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py index 9c9290dde..20ab083f6 100644 --- a/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py +++ b/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String, Boolean +from sqlalchemy import Date, String, Boolean, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -117,13 +117,13 @@ def upgrade(): "where fs.filing_type_code = 'BCINC' and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) distribution_code_id = res.fetchall()[0][0] new_codes = ('CCC', 'ULC', 'LTD', 'BC') distr_code_link_values = [] for new_code in new_codes: res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='{new_code}' and filing_type_code='BCINC'") + text(f"select fee_schedule_id from fee_schedules where corp_type_code='{new_code}' and filing_type_code='BCINC'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link_values.append({ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/migrations/versions/c871202927f0_rush_fee_code.py b/pay-api/migrations/versions/c871202927f0_rush_fee_code.py index c67c6789b..98519de3a 100644 --- a/pay-api/migrations/versions/c871202927f0_rush_fee_code.py +++ b/pay-api/migrations/versions/c871202927f0_rush_fee_code.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String, Float +from sqlalchemy import Date, String, Float, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -76,11 +76,11 @@ def upgrade(): "where upper(dc.name) = 'VITAL STATISTICS' and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where corp_type_code='VS' and filing_type_code='WILLRUSH'") + text(f"select fee_schedule_id from fee_schedules where corp_type_code='VS' and filing_type_code='WILLRUSH'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link = [{ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/migrations/versions/dbe9dc38ac33_firms_fee_codes.py b/pay-api/migrations/versions/dbe9dc38ac33_firms_fee_codes.py index e10a65566..7e1fc5a6f 100644 --- a/pay-api/migrations/versions/dbe9dc38ac33_firms_fee_codes.py +++ b/pay-api/migrations/versions/dbe9dc38ac33_firms_fee_codes.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String +from sqlalchemy import Date, String, text from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -66,11 +66,11 @@ def upgrade(): "and dc.start_date <= CURRENT_DATE " \ "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(text(distribution_code_id_query)) if (res_fetch := res.fetchall()) and res_fetch[0]: distribution_code_id = res_fetch[0][0] res = conn.execute( - f"select fee_schedule_id from fee_schedules where filing_type_code in ('DIS_VOL') and corp_type_code in ('SP','GP') ") + text(f"select fee_schedule_id from fee_schedules where filing_type_code in ('DIS_VOL') and corp_type_code in ('SP','GP') ")) distr_code_links = [] for result in res.fetchall(): fee_schedule_id = result[0] diff --git a/pay-api/migrations/versions/e748b5c19247_.py b/pay-api/migrations/versions/e748b5c19247_.py index 3a388cb93..c58a8d01c 100644 --- a/pay-api/migrations/versions/e748b5c19247_.py +++ b/pay-api/migrations/versions/e748b5c19247_.py @@ -130,13 +130,13 @@ def upgrade(): distribution_code_id_query = "select distribution_code_id from distribution_codes where name = 'BC Assessment' and created_by = 'Alembic'" conn = op.get_bind() - res = conn.execute(distribution_code_id_query) + res = conn.execute(sa.text(distribution_code_id_query)) distribution_code_id = res.fetchall()[0][0] new_codes = ('OLAARTOQ', 'OLAARTAQ', 'OLAARTIQ') distr_code_link_values = [] for new_code in new_codes: res = conn.execute( - f"select fee_schedule_id from fee_schedules where filing_type_code='{new_code}' and corp_type_code='BCA'") + sa.text(f"select fee_schedule_id from fee_schedules where filing_type_code='{new_code}' and corp_type_code='BCA'")) fee_schedule_id = res.fetchall()[0][0] distr_code_link_values.append({ 'distribution_code_id': distribution_code_id, diff --git a/pay-api/pre_hook_create_database.py b/pay-api/pre_hook_create_database.py index da26c970a..6910f3aa3 100644 --- a/pay-api/pre_hook_create_database.py +++ b/pay-api/pre_hook_create_database.py @@ -37,4 +37,4 @@ with contextlib.suppress(sqlalchemy.exc.ProgrammingError): with sqlalchemy.create_engine(DATABASE_URI, isolation_level='AUTOCOMMIT').connect() as connection: DATABASE_NAME = ProdConfig.DB_NAME - connection.execute(f'CREATE DATABASE {DATABASE_NAME}') + connection.execute(f'CREATE DATABASE "{DATABASE_NAME}"') diff --git a/pay-api/requirements.txt b/pay-api/requirements.txt index 34e9990e9..08bc10d9c 100644 --- a/pay-api/requirements.txt +++ b/pay-api/requirements.txt @@ -1,80 +1,74 @@ -Flask-Caching==2.0.2 -Flask-Cors==3.0.10 +Flask-Caching==2.1.0 +Flask-Cors==4.0.0 Flask-Migrate==2.7.0 Flask-Moment==1.0.5 -Flask-SQLAlchemy==2.5.1 +Flask-SQLAlchemy==3.1.1 Flask-Script==2.0.6 -Flask==1.1.2 -Jinja2==3.0.3 -Mako==1.2.4 -MarkupSafe==2.1.2 -PyMeeus==0.5.12 -SQLAlchemy-Continuum==1.3.14 -SQLAlchemy-Utils==0.41.0 -SQLAlchemy==1.3.24 -Werkzeug==1.0.1 -alembic==1.10.3 -aniso8601==9.0.1 -asyncio-nats-client==0.11.5 -asyncio-nats-streaming==0.4.0 -attrs==23.1.0 -backports.zoneinfo==0.2.1 -blinker==1.6.2 +Flask==3.0.2 +Jinja2==3.1.3 +Mako==1.3.2 +MarkupSafe==2.1.5 +SQLAlchemy-Utils==0.41.1 +SQLAlchemy==2.0.27 +Werkzeug==3.0.1 +alembic==1.13.1 +attrs==23.2.0 +blinker==1.7.0 cachelib==0.9.0 -cattrs==22.2.0 -certifi==2023.7.22 -cffi==1.15.1 -charset-normalizer==3.1.0 -click==8.1.3 -convertdate==2.4.0 -croniter==1.3.14 -cryptography==42.0.2 -dpath==2.1.5 +cachetools==5.3.3 +cattrs==23.2.3 +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +croniter==2.0.2 +cryptography==42.0.5 +dpath==2.1.6 ecdsa==0.18.0 -exceptiongroup==1.1.1 expiringdict==1.2.2 -flask-jwt-oidc==0.3.0 -flask-marshmallow==0.11.0 -google-api-core==2.11.0 +flask-marshmallow==1.2.0 +google-api-core==2.17.1 google-auth==2.18.1 google-cloud-pubsub==2.17.0 -googleapis-common-protos==1.59.0 -gunicorn==20.1.0 -hijri-converter==2.2.4 +googleapis-common-protos==1.62.0 +greenlet==3.0.3 +grpc-google-iam-v1==0.13.0 +grpcio-status==1.62.0 +grpcio==1.62.0 +gunicorn==21.2.0 holidays==0.37 -idna==3.4 -importlib-metadata==6.4.1 -importlib-resources==5.12.0 -itsdangerous==2.0.1 +idna==3.6 +itsdangerous==2.1.2 jaeger-client==4.8.0 jsonschema==4.17.3 -korean-lunar-calendar==0.3.1 -launchdarkly-server-sdk==8.1.1 -marshmallow-sqlalchemy==0.25.0 -marshmallow==3.19.0 +launchdarkly-eventsource==1.1.1 +launchdarkly-server-sdk==9.2.1 +marshmallow-sqlalchemy==1.0.0 +marshmallow==3.21.0 opentracing==2.4.0 -packaging==23.1 -pkgutil_resolve_name==1.3.10 -protobuf==3.19.6 -psycopg2-binary==2.9.6 +packaging==23.2 +proto-plus==1.23.0 +protobuf==4.25.3 +psycopg2-binary==2.9.9 pyRFC3339==1.1 -pyasn1==0.4.8 +pyasn1-modules==0.3.0 +pyasn1==0.5.1 pycparser==2.21 -pyrsistent==0.19.3 -python-dateutil==2.8.2 -python-dotenv==1.0.0 +pyrsistent==0.20.0 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 python-jose==3.3.0 -pytz==2023.3 +pytz==2024.1 requests==2.31.0 rsa==4.9 -semver==2.13.0 -sentry-sdk==1.19.1 +semver==3.0.2 +sentry-sdk==1.40.6 six==1.16.0 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.3 -typing_extensions==4.5.0 -urllib3==1.26.17 -zipp==3.15.0 +tornado==6.4 +typing_extensions==4.10.0 +urllib3==1.26.18 -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python git+https://github.com/daxiom/simple-cloudevent.py.git +git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-api/requirements/dev.txt b/pay-api/requirements/dev.txt index ad98209c9..0938b0432 100755 --- a/pay-api/requirements/dev.txt +++ b/pay-api/requirements/dev.txt @@ -10,7 +10,7 @@ pytest-cov FreezeGun # Lint and code style -flake8==5.0.4 +flake8 flake8-blind-except flake8-debugger flake8-docstrings diff --git a/pay-api/requirements/prod.txt b/pay-api/requirements/prod.txt index 7efc35139..d84882ac4 100644 --- a/pay-api/requirements/prod.txt +++ b/pay-api/requirements/prod.txt @@ -6,28 +6,24 @@ Flask-Migrate<3 Flask-Script Flask-Moment Flask-SQLAlchemy -flask-marshmallow==0.11.0 +flask-marshmallow flask-jwt-oidc python-dotenv psycopg2-binary -marshmallow-sqlalchemy==0.25.0 +marshmallow-sqlalchemy jsonschema==4.17.3 requests -asyncio-nats-client -asyncio-nats-streaming croniter sentry-sdk[flask] cattrs jaeger-client dpath -Werkzeug<2 -SQLAlchemy-Continuum +Werkzeug cryptography sqlalchemy_utils -sqlalchemy<1.4 -itsdangerous==2.0.1 -Jinja2==3.0.3 -protobuf~=3.19.5 +sqlalchemy +itsdangerous +Jinja2 launchdarkly-server-sdk holidays==0.37 google-auth==2.18.1 diff --git a/pay-api/setup.cfg b/pay-api/setup.cfg index 9736f38da..35e4ccdc0 100755 --- a/pay-api/setup.cfg +++ b/pay-api/setup.cfg @@ -45,7 +45,7 @@ per-file-ignores = max_line_length = 120 ignore = E501 docstring-min-length=10 -notes=FIXME,XXX # TODO is ignored +notes=FIXME,XXX match_dir = src/pay_api ignored-modules=flask_sqlalchemy sqlalchemy diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index 1a77c8a64..0af1e12ea 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -20,7 +20,7 @@ import sentry_sdk # noqa: I001; pylint: disable=ungrouped-imports,wrong-import-order; conflicts with Flake8 from flask import Flask -from sbc_common_components.exception_handling.exception_handler import ExceptionHandler # noqa: I001 +from sbc_common_components.exception_handling.exception_handler import ExceptionHandler from sbc_common_components.utils.camel_case_response import convert_to_camel from sentry_sdk.integrations.flask import FlaskIntegration # noqa: I001 diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index d84d022ac..68579db3e 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -61,7 +61,6 @@ def _get_config(config_key: str, **kwargs): value = os.getenv(config_key, kwargs.get('default')) else: value = os.getenv(config_key) - # assert value TODO Un-comment once we find a solution to run pre-hook without initializing app return value @@ -121,18 +120,6 @@ class _Config(): # pylint: disable=too-few-public-methods PAYBC_DIRECT_PAY_CLIENT_SECRET = _get_config('PAYBC_DIRECT_PAY_CLIENT_SECRET') PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL = _get_config('PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL') - # NATS Config - NATS_SERVERS = _get_config('NATS_SERVERS', default='nats://127.0.0.1:4222').split(',') - NATS_CLUSTER_ID = _get_config('NATS_CLUSTER_ID', default='test-cluster') - NATS_PAYMENT_CLIENT_NAME = _get_config('NATS_PAYMENT_CLIENT_NAME', default='entity.filing.worker') - NATS_PAYMENT_SUBJECT = _get_config('NATS_PAYMENT_SUBJECT', default='entity.{product}.payment') - - NATS_MAILER_CLIENT_NAME = _get_config('NATS_MAILER_CLIENT_NAME', default='account.mailer.worker') - NATS_MAILER_SUBJECT = _get_config('NATS_MAILER_SUBJECT', default='account.mailer') - - NATS_ACCOUNT_CLIENT_NAME = os.getenv('NATS_ACCOUNT_CLIENT_NAME', 'account.events.worker') - NATS_ACCOUNT_SUBJECT = os.getenv('NATS_ACCOUNT_SUBJECT', 'account.events') - # GCP PubSub AUDIENCE = os.getenv('AUDIENCE', None) GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) @@ -294,8 +281,6 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods AUTH_API_ENDPOINT = 'http://localhost:8080/auth-api/' - NATS_SUBJECT = 'entity.filing.test' - BCOL_API_ENDPOINT = 'http://localhost:8080/bcol-api' VALID_REDIRECT_URLS = ['http://localhost:8080/*'] diff --git a/pay-api/src/pay_api/factory/payment_system_factory.py b/pay-api/src/pay_api/factory/payment_system_factory.py index fc89a26f9..f5ffbb998 100644 --- a/pay-api/src/pay_api/factory/payment_system_factory.py +++ b/pay-api/src/pay_api/factory/payment_system_factory.py @@ -109,7 +109,8 @@ def create(**kwargs): @staticmethod def _validate_and_throw_error(instance: PaymentSystemService, payment_account: PaymentAccount): if isinstance(instance, PadService): - is_in_pad_confirmation_period = payment_account.pad_activation_date > datetime.now() + is_in_pad_confirmation_period = payment_account.pad_activation_date > \ + datetime.now(payment_account.pad_activation_date.tzinfo) is_cfs_account_in_pending_status = payment_account.cfs_account_status == \ CfsAccountStatus.PENDING_PAD_ACTIVATION.value diff --git a/pay-api/src/pay_api/models/base_model.py b/pay-api/src/pay_api/models/base_model.py index d7ce5f809..f4cb85e86 100644 --- a/pay-api/src/pay_api/models/base_model.py +++ b/pay-api/src/pay_api/models/base_model.py @@ -13,12 +13,12 @@ # limitations under the License. """Super class to handle all operations related to base model.""" -from flask import current_app -from sqlalchemy_continuum.plugins.flask import fetch_remote_addr +# from flask import current_app +# from sqlalchemy_continuum.plugins.flask import fetch_remote_addr from pay_api.utils.user_context import user_context -from .db import activity_plugin, db +from .db import db class BaseModel(db.Model): @@ -74,18 +74,17 @@ def find_by_id(cls, identifier: int): @classmethod def create_activity(cls, obj, is_delete=False): """Create activity records if the model is versioned.""" - if isinstance(obj, VersionedModel) and not current_app.config.get('DISABLE_ACTIVITY_LOGS'): - if is_delete: - verb = 'delete' - else: - verb = 'update' - - activity = activity_plugin.activity_cls(verb=verb, object=obj, data={ - 'user_name': cls._get_user_name(), - 'remote_addr': fetch_remote_addr() - }) - - db.session.add(activity) + # TODO fix this later + # if isinstance(obj, VersionedModel) and not current_app.config.get('DISABLE_ACTIVITY_LOGS'): + # if is_delete: + # verb = 'delete' + # else: + # verb = 'update' + # activity = activity_plugin.activity_cls(verb=verb, object=obj, data={ + # 'user_name': cls._get_user_name(), + # 'remote_addr': fetch_remote_addr() + # }) + # db.session.add(activity) @staticmethod @user_context diff --git a/pay-api/src/pay_api/models/base_schema.py b/pay-api/src/pay_api/models/base_schema.py index da6a1b7b1..e4255a85b 100644 --- a/pay-api/src/pay_api/models/base_schema.py +++ b/pay-api/src/pay_api/models/base_schema.py @@ -18,7 +18,7 @@ from .db import ma -class BaseSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class BaseSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Base Schema.""" def __init__(self, *args, **kwargs): @@ -31,6 +31,7 @@ class Meta: # pylint: disable=too-few-public-methods """Meta class to declare any class attributes.""" datetimeformat = '%Y-%m-%dT%H:%M:%S+00:00' + load_instance = True @post_dump(pass_many=True) def _remove_empty(self, data, many): diff --git a/pay-api/src/pay_api/models/cfs_account_status_code.py b/pay-api/src/pay_api/models/cfs_account_status_code.py index c3f7bc66d..1507d6495 100644 --- a/pay-api/src/pay_api/models/cfs_account_status_code.py +++ b/pay-api/src/pay_api/models/cfs_account_status_code.py @@ -42,10 +42,11 @@ class CfsAccountStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class CfsAccountStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class CfsAccountStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = CfsAccountStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/corp_type.py b/pay-api/src/pay_api/models/corp_type.py index 5b7f69061..0174857b7 100644 --- a/pay-api/src/pay_api/models/corp_type.py +++ b/pay-api/src/pay_api/models/corp_type.py @@ -70,10 +70,11 @@ def __str__(self): return f'{self.code}' -class CorpTypeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class CorpTypeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Business.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = CorpType + load_instance = True diff --git a/pay-api/src/pay_api/models/credit.py b/pay-api/src/pay_api/models/credit.py index d6d79f81a..1b010b921 100644 --- a/pay-api/src/pay_api/models/credit.py +++ b/pay-api/src/pay_api/models/credit.py @@ -63,10 +63,11 @@ def find_by_cfs_identifier(cls, cfs_identifier: str, credit_memo: bool = False): return cls.query.filter_by(cfs_identifier=cfs_identifier).filter_by(is_credit_memo=credit_memo).one_or_none() -class CreditSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class CreditSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Credit.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = Credit + load_instance = True diff --git a/pay-api/src/pay_api/models/custom_query.py b/pay-api/src/pay_api/models/custom_query.py index 553fbab68..7709db44f 100644 --- a/pay-api/src/pay_api/models/custom_query.py +++ b/pay-api/src/pay_api/models/custom_query.py @@ -11,13 +11,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# pylint: disable=W0223 """Custom Query class to extend BaseQuery class functionality.""" from datetime import date, datetime -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query from sqlalchemy import and_, func -class CustomQuery(BaseQuery): +class CustomQuery(Query): # pylint: disable=too-many-ancestors """Custom Query class to extend the base query class for helper functionality.""" def filter_conditionally(self, search_criteria, model_attribute, is_like: bool = False): diff --git a/pay-api/src/pay_api/models/db.py b/pay-api/src/pay_api/models/db.py index 3edab2d49..7f35342e0 100755 --- a/pay-api/src/pay_api/models/db.py +++ b/pay-api/src/pay_api/models/db.py @@ -17,8 +17,8 @@ """ from flask_marshmallow import Marshmallow from flask_sqlalchemy import SQLAlchemy -from sqlalchemy_continuum import make_versioned -from sqlalchemy_continuum.plugins import ActivityPlugin +# from sqlalchemy_continuum import make_versioned +# from sqlalchemy_continuum.plugins import ActivityPlugin from .custom_query import CustomQuery # by convention in the Flask community these are lower case, @@ -27,6 +27,8 @@ db = SQLAlchemy(query_class=CustomQuery) # pylint: disable=invalid-name -activity_plugin = ActivityPlugin() # pylint: disable=invalid-name +# TODO FIX THIS +# activity_plugin = ActivityPlugin() # pylint: disable=invalid-name -make_versioned(user_cls=None, plugins=[activity_plugin]) +# TODO fix this +# make_versioned(user_cls=None, plugins=[activity_plugin]) diff --git a/pay-api/src/pay_api/models/disbursement_status_code.py b/pay-api/src/pay_api/models/disbursement_status_code.py index d962567a5..7ffb5720c 100644 --- a/pay-api/src/pay_api/models/disbursement_status_code.py +++ b/pay-api/src/pay_api/models/disbursement_status_code.py @@ -42,10 +42,11 @@ class DisbursementStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class DisbursementStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class DisbursementStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = DisbursementStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/distribution_code.py b/pay-api/src/pay_api/models/distribution_code.py index ae97b8e6a..494ea8883 100644 --- a/pay-api/src/pay_api/models/distribution_code.py +++ b/pay-api/src/pay_api/models/distribution_code.py @@ -156,7 +156,7 @@ def find_by_active_for_account(cls, account_id: int): return distribution_code -class DistributionCodeLinkSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class DistributionCodeLinkSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the DistributionCodeLink.""" class Meta: # pylint: disable=too-few-public-methods @@ -164,6 +164,7 @@ class Meta: # pylint: disable=too-few-public-methods model = DistributionCodeLink exclude = ['disbursement'] + load_instance = True class DistributionCodeSchema(AuditSchema, BaseSchema): # pylint: disable=too-many-ancestors diff --git a/pay-api/src/pay_api/models/eft_process_status_code.py b/pay-api/src/pay_api/models/eft_process_status_code.py index fd8c4e6a0..c414a60d9 100644 --- a/pay-api/src/pay_api/models/eft_process_status_code.py +++ b/pay-api/src/pay_api/models/eft_process_status_code.py @@ -42,10 +42,11 @@ class EFTProcessStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(100), nullable=False) -class EFTProcessStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class EFTProcessStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = EFTProcessStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/error_code.py b/pay-api/src/pay_api/models/error_code.py index 1c7547e66..c0190cbb7 100644 --- a/pay-api/src/pay_api/models/error_code.py +++ b/pay-api/src/pay_api/models/error_code.py @@ -48,12 +48,13 @@ class ErrorCode(db.Model, CodeTable): detail = db.Column(db.String(500)) -class ErrorCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class ErrorCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Error code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = ErrorCode + load_instance = True code = fields.String(data_key='type') diff --git a/pay-api/src/pay_api/models/fee_code.py b/pay-api/src/pay_api/models/fee_code.py index 5f2b6ddeb..b0992c696 100644 --- a/pay-api/src/pay_api/models/fee_code.py +++ b/pay-api/src/pay_api/models/fee_code.py @@ -56,11 +56,12 @@ def __str__(self): return f'{self.amount} ({self.code})' -class FeeCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class FeeCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Business.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = FeeCode + load_instance = True amount = fields.Float(data_key='amount') diff --git a/pay-api/src/pay_api/models/fee_schedule.py b/pay-api/src/pay_api/models/fee_schedule.py index 0b18681fe..70686b08f 100644 --- a/pay-api/src/pay_api/models/fee_schedule.py +++ b/pay-api/src/pay_api/models/fee_schedule.py @@ -130,7 +130,6 @@ def find_all(cls, corp_type_code: str = None, filing_type_code: str = None, desc query = query.filter_by(corp_type_code=corp_type_code) if description: - # TODO arrive at a better search descriptions = description.replace(' ', '%') query = query.join(CorpType, CorpType.code == FeeSchedule.corp_type_code). \ @@ -147,13 +146,15 @@ def save(self): db.session.commit() -class FeeScheduleSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class FeeScheduleSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Business.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = FeeSchedule + load_instance = True + exclude = ['distribution_codes'] # pylint: disable=no-member corp_type = ma.Nested(CorpTypeSchema, many=False, data_key='corp_type_code', diff --git a/pay-api/src/pay_api/models/filing_type.py b/pay-api/src/pay_api/models/filing_type.py index d34f24a4e..08f45fb84 100644 --- a/pay-api/src/pay_api/models/filing_type.py +++ b/pay-api/src/pay_api/models/filing_type.py @@ -54,10 +54,11 @@ def __str__(self): return f'{self.code}' -class FilingTypeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class FilingTypeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Business.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = FilingType + load_instance = True diff --git a/pay-api/src/pay_api/models/invoice.py b/pay-api/src/pay_api/models/invoice.py index e4b81d2c3..cdafa08bb 100644 --- a/pay-api/src/pay_api/models/invoice.py +++ b/pay-api/src/pay_api/models/invoice.py @@ -126,7 +126,7 @@ def update_invoices_for_revenue_updates(cls, fee_distribution_id: int): .join(PaymentLineItem, PaymentLineItem.invoice_id == Invoice.id) \ .filter(PaymentLineItem.fee_distribution_id == fee_distribution_id) - invoices: [Invoice] = query.all() + invoices: List[Invoice] = query.all() for invoice in invoices: if invoice.invoice_status_code == InvoiceStatus.PAID.value: invoice.invoice_status_code = InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value @@ -199,8 +199,8 @@ class Meta(BaseSchema.Meta): # pylint: disable=too-few-public-methods many=False) _links = ma.Hyperlinks({ - 'self': ma.URLFor('INVOICE.get_invoice', invoice_id=''), - 'collection': ma.URLFor('INVOICE.get_invoices', invoice_id='') + 'self': ma.URLFor('INVOICE.get_invoice', values={'invoice_id': ''}), + 'collection': ma.URLFor('INVOICE.get_invoices', values={'invoice_id': ''}) }) total = fields.Float(data_key='total') @@ -224,7 +224,7 @@ def _clean_up(self, data, many): # pylint: disable=unused-argument if data.get('business_identifier', None) and data.get('business_identifier').startswith('T'): data.pop('business_identifier') - # TODO remove it later, adding this here to make non-breaking changes for other teams + # Adding this here to make non-breaking changes for other teams EG: CSO if data.get('status_code') == InvoiceStatus.PAID.value: data['status_code'] = PaymentStatus.COMPLETED.value diff --git a/pay-api/src/pay_api/models/invoice_reference_status_code.py b/pay-api/src/pay_api/models/invoice_reference_status_code.py index 307129b1f..ea707dd5e 100644 --- a/pay-api/src/pay_api/models/invoice_reference_status_code.py +++ b/pay-api/src/pay_api/models/invoice_reference_status_code.py @@ -42,10 +42,11 @@ class InvoiceReferenceStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class InvoiceReferenceStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class InvoiceReferenceStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = InvoiceReferenceStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/invoice_status_code.py b/pay-api/src/pay_api/models/invoice_status_code.py index a4112f832..ce92ef38c 100644 --- a/pay-api/src/pay_api/models/invoice_status_code.py +++ b/pay-api/src/pay_api/models/invoice_status_code.py @@ -42,10 +42,11 @@ class InvoiceStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class InvoiceStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class InvoiceStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = InvoiceStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/line_item_status_code.py b/pay-api/src/pay_api/models/line_item_status_code.py index 46b98421f..4f7d86690 100644 --- a/pay-api/src/pay_api/models/line_item_status_code.py +++ b/pay-api/src/pay_api/models/line_item_status_code.py @@ -42,10 +42,11 @@ class LineItemStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class LineItemStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class LineItemStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = LineItemStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/notification_status_code.py b/pay-api/src/pay_api/models/notification_status_code.py index 9ab86a39b..856ae0aa6 100644 --- a/pay-api/src/pay_api/models/notification_status_code.py +++ b/pay-api/src/pay_api/models/notification_status_code.py @@ -42,10 +42,11 @@ class NotificationStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class NotificationStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class NotificationStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = NotificationStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/payment.py b/pay-api/src/pay_api/models/payment.py index 55c87097e..2faa840b0 100644 --- a/pay-api/src/pay_api/models/payment.py +++ b/pay-api/src/pay_api/models/payment.py @@ -153,7 +153,6 @@ def search_account_payments(cls, auth_account_id: str, payment_status: str, page .outerjoin(Invoice, InvoiceReference.invoice_id == Invoice.id) \ .filter(PaymentAccount.auth_account_id == auth_account_id) - # TODO handle other status and conditions gracefully. if payment_status: query = query.filter(Payment.payment_status_code == payment_status) if payment_status == PaymentStatus.FAILED.value: @@ -289,7 +288,7 @@ def get_count(cls, auth_account_id: str, search_filter: Dict): query = db.session.query(Invoice) \ .outerjoin(PaymentAccount, Invoice.payment_account_id == PaymentAccount.id) query = cls.filter(query, auth_account_id, search_filter, add_outer_joins=True) - count = query.group_by(Invoice.id).with_entities(func.count()).count() + count = query.group_by(Invoice.id).with_entities(func.count()).count() # pylint:disable=not-callable return count @classmethod @@ -313,7 +312,7 @@ def filter(cls, query, auth_account_id: str, search_filter: Dict, add_outer_join query = query.filter( Invoice.created_name.ilike(f'%{created_by}%')) # pylint: disable=no-member if created_name := search_filter.get('createdName', None): - query = query.filter(Invoice.created_name.ilike(f'%{created_name}%')) + query = query.filter(Invoice.created_name.ilike(f'%{created_name}%')) # pylint: disable=no-member if invoice_id := search_filter.get('id', None): query = query.filter(cast(Invoice.id, String).like(f'%{invoice_id}%')) diff --git a/pay-api/src/pay_api/models/payment_line_item.py b/pay-api/src/pay_api/models/payment_line_item.py index e6f64acf5..85a5acfd7 100644 --- a/pay-api/src/pay_api/models/payment_line_item.py +++ b/pay-api/src/pay_api/models/payment_line_item.py @@ -86,7 +86,7 @@ def find_by_invoice_ids(cls, invoice_ids: list): PaymentLineItem.invoice_id.desc()).all() -class PaymentLineItemSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class PaymentLineItemSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Payment line item.""" class Meta: # pylint: disable=too-few-public-methods @@ -94,6 +94,7 @@ class Meta: # pylint: disable=too-few-public-methods model = PaymentLineItem exclude = ['fee_schedule_id', 'fee_schedule'] + load_instance = True line_item_status_code = fields.String(data_key='status_code') filing_fees = fields.Float(data_key='filing_fees') diff --git a/pay-api/src/pay_api/models/payment_method.py b/pay-api/src/pay_api/models/payment_method.py index dba6adb42..8f77ef949 100644 --- a/pay-api/src/pay_api/models/payment_method.py +++ b/pay-api/src/pay_api/models/payment_method.py @@ -47,10 +47,11 @@ def save(self): db.session.commit() -class PaymentMethodSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class PaymentMethodSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the System Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = PaymentMethod + load_instance = True diff --git a/pay-api/src/pay_api/models/payment_status_code.py b/pay-api/src/pay_api/models/payment_status_code.py index 06fbabc9e..6fcf9da34 100644 --- a/pay-api/src/pay_api/models/payment_status_code.py +++ b/pay-api/src/pay_api/models/payment_status_code.py @@ -42,10 +42,11 @@ class PaymentStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class PaymentStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class PaymentStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = PaymentStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/payment_system.py b/pay-api/src/pay_api/models/payment_system.py index bef168a65..dd6f29074 100644 --- a/pay-api/src/pay_api/models/payment_system.py +++ b/pay-api/src/pay_api/models/payment_system.py @@ -47,10 +47,11 @@ def save(self): db.session.commit() -class PaymentSystemSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class PaymentSystemSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the System Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = PaymentSystem + load_instance = True diff --git a/pay-api/src/pay_api/models/receipt.py b/pay-api/src/pay_api/models/receipt.py index 18f4a724b..91b3faa27 100644 --- a/pay-api/src/pay_api/models/receipt.py +++ b/pay-api/src/pay_api/models/receipt.py @@ -67,12 +67,13 @@ def find_all_receipts_for_invoice(cls, invoice_id: int): return cls.query.filter_by(invoice_id=invoice_id).all() -class ReceiptSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class ReceiptSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Receipt.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = Receipt + load_instance = True receipt_date = fields.DateTime(tzinfo=pytz.timezone(LEGISLATIVE_TIMEZONE)) diff --git a/pay-api/src/pay_api/models/refund.py b/pay-api/src/pay_api/models/refund.py index 47c6fab21..c8db6e5ce 100644 --- a/pay-api/src/pay_api/models/refund.py +++ b/pay-api/src/pay_api/models/refund.py @@ -80,10 +80,11 @@ def find_by_routing_slip_id(cls, routing_slip_id: int): return cls.query.filter_by(routing_slip_id=routing_slip_id).one_or_none() -class RefundSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class RefundSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Refund.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = Refund + load_instance = True diff --git a/pay-api/src/pay_api/models/routing_slip.py b/pay-api/src/pay_api/models/routing_slip.py index b304290c1..6ed6baf24 100644 --- a/pay-api/src/pay_api/models/routing_slip.py +++ b/pay-api/src/pay_api/models/routing_slip.py @@ -195,7 +195,7 @@ def search(cls, search_filter: Dict, # pylint: disable=too-many-arguments, too- query = cls._add_date_filter(query, search_filter) if initiator := search_filter.get('initiator', None): - query = query.filter(RoutingSlip.created_name.ilike('%' + initiator + '%')) + query = query.filter(RoutingSlip.created_name.ilike('%' + initiator + '%')) # pylint: disable=no-member if business_identifier := search_filter.get('businessIdentifier', None): query = query.filter(Invoice.business_identifier == business_identifier) diff --git a/pay-api/src/pay_api/models/routing_slip_status_code.py b/pay-api/src/pay_api/models/routing_slip_status_code.py index 07e3c9d4b..f1ae33e70 100644 --- a/pay-api/src/pay_api/models/routing_slip_status_code.py +++ b/pay-api/src/pay_api/models/routing_slip_status_code.py @@ -42,10 +42,11 @@ class RoutingSlipStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class RoutingSlipStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class RoutingSlipStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = RoutingSlipStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/models/statement.py b/pay-api/src/pay_api/models/statement.py index a4fc4470e..fd3b0c5ed 100644 --- a/pay-api/src/pay_api/models/statement.py +++ b/pay-api/src/pay_api/models/statement.py @@ -15,10 +15,10 @@ import pytz from marshmallow import fields -from sqlalchemy import ForeignKey, and_, case, func, literal_column, or_ +from sqlalchemy import ForeignKey, and_, case, literal_column from sqlalchemy.ext.hybrid import hybrid_property -from sqlalchemy.orm import aliased -from sqlalchemy_continuum import transaction_class, version_class +# from sqlalchemy.orm import aliased +# from sqlalchemy_continuum import transaction_class, version_class from pay_api.utils.constants import LEGISLATIVE_TIMEZONE from pay_api.utils.enums import StatementFrequency @@ -72,22 +72,24 @@ class Statement(BaseModel): @hybrid_property def payment_methods(self): """Return all payment methods that were active during the statement period based on payment account versions.""" - payment_account_version = version_class(PaymentAccount) - transaction_start = aliased(transaction_class(PaymentAccount)) - transaction_end = aliased(transaction_class(PaymentAccount)) - - subquery = db.session.query(func.array_agg(func.DISTINCT(payment_account_version.payment_method)) - .label('payment_methods'))\ - .join(Statement, Statement.payment_account_id == payment_account_version.id)\ - .join(transaction_start, payment_account_version.transaction_id == transaction_start.id)\ - .outerjoin(transaction_end, payment_account_version.end_transaction_id == transaction_end.id)\ - .filter(payment_account_version.id == self.payment_account_id) \ - .filter(and_(Statement.id == self.id, transaction_start.issued_at <= Statement.to_date, - or_(transaction_end.issued_at >= Statement.from_date, - transaction_end.id.is_(None))))\ - .group_by(Statement.id).first() - - return subquery[0] if subquery else [] + return [] + # TODO FIX THIS. + # payment_account_version = version_class(PaymentAccount) + # transaction_start = aliased(transaction_class(PaymentAccount)) + # transaction_end = aliased(transaction_class(PaymentAccount)) + + # subquery = db.session.query(func.array_agg(func.DISTINCT(payment_account_version.payment_method)) + # .label('payment_methods'))\ + # .join(Statement, Statement.payment_account_id == payment_account_version.id)\ + # .join(transaction_start, payment_account_version.transaction_id == transaction_start.id)\ + # .outerjoin(transaction_end, payment_account_version.end_transaction_id == transaction_end.id)\ + # .filter(payment_account_version.id == self.payment_account_id) \ + # .filter(and_(Statement.id == self.id, transaction_start.issued_at <= Statement.to_date, + # or_(transaction_end.issued_at >= Statement.from_date, + # transaction_end.id.is_(None))))\ + # .group_by(Statement.id).first() + + # return subquery[0] if subquery else [] @classmethod def find_all_statements_for_account(cls, auth_account_id: str, page, limit): @@ -98,20 +100,18 @@ def find_all_statements_for_account(cls, auth_account_id: str, page, limit): PaymentAccount.auth_account_id == auth_account_id)) frequency_case = case( - [ - ( - Statement.frequency == StatementFrequency.MONTHLY.value, - literal_column("'1'") - ), - ( - Statement.frequency == StatementFrequency.WEEKLY.value, - literal_column("'2'") - ), - ( - Statement.frequency == StatementFrequency.DAILY.value, - literal_column("'3'") - ) - ], + ( + Statement.frequency == StatementFrequency.MONTHLY.value, + literal_column("'1'") + ), + ( + Statement.frequency == StatementFrequency.WEEKLY.value, + literal_column("'2'") + ), + ( + Statement.frequency == StatementFrequency.DAILY.value, + literal_column("'3'") + ), else_=literal_column("'4'") ) @@ -138,13 +138,14 @@ def find_all_payments_and_invoices_for_statement(cls, statement_id: str): return query.all() -class StatementSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class StatementSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Statements.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = Statement + load_instance = True from_date = fields.Date(tzinfo=pytz.timezone(LEGISLATIVE_TIMEZONE)) to_date = fields.Date(tzinfo=pytz.timezone(LEGISLATIVE_TIMEZONE)) diff --git a/pay-api/src/pay_api/models/statement_invoices.py b/pay-api/src/pay_api/models/statement_invoices.py index dc3fbf573..5dea45eec 100644 --- a/pay-api/src/pay_api/models/statement_invoices.py +++ b/pay-api/src/pay_api/models/statement_invoices.py @@ -55,10 +55,11 @@ def find_all_invoices_for_statement(cls, statement_identifier: str): return cls.query.filter_by(statement_id=statement_identifier).all() -class StatementInvoicesSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class StatementInvoicesSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Statements.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = StatementInvoices + load_instance = True diff --git a/pay-api/src/pay_api/models/statement_recipients.py b/pay-api/src/pay_api/models/statement_recipients.py index 5fa06fe91..5a89f4a4b 100644 --- a/pay-api/src/pay_api/models/statement_recipients.py +++ b/pay-api/src/pay_api/models/statement_recipients.py @@ -58,7 +58,7 @@ def find_all_recipients(cls, auth_account_id: str): """Return all active recipients for an account.""" return cls.query \ .join(PaymentAccount) \ - .filter(PaymentAccount.auth_account_id == auth_account_id).all() + .filter(PaymentAccount.auth_account_id == str(auth_account_id)).all() @classmethod def find_all_recipients_for_payment_id(cls, payment_account_id: str): @@ -79,10 +79,11 @@ def bulk_save_recipients(cls, recipients: list): BaseModel.commit() -class StatementRecipientsSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class StatementRecipientsSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Payment Account.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = StatementRecipients + load_instance = True diff --git a/pay-api/src/pay_api/models/statement_settings.py b/pay-api/src/pay_api/models/statement_settings.py index 9c52c8a52..3b4eacbbd 100644 --- a/pay-api/src/pay_api/models/statement_settings.py +++ b/pay-api/src/pay_api/models/statement_settings.py @@ -87,10 +87,11 @@ def find_accounts_settings_by_frequency(cls, valid_date: datetime, frequency: St return query.all() -class StatementSettingsSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class StatementSettingsSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Statements settings.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = StatementSettings + load_instance = True diff --git a/pay-api/src/pay_api/models/transaction_status_code.py b/pay-api/src/pay_api/models/transaction_status_code.py index 433d533cc..c42128c72 100644 --- a/pay-api/src/pay_api/models/transaction_status_code.py +++ b/pay-api/src/pay_api/models/transaction_status_code.py @@ -42,10 +42,11 @@ class TransactionStatusCode(db.Model, CodeTable): description = db.Column('description', db.String(200), nullable=False) -class TransactionStatusCodeSchema(ma.ModelSchema): # pylint: disable=too-many-ancestors +class TransactionStatusCodeSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors """Main schema used to serialize the Status Code.""" class Meta: # pylint: disable=too-few-public-methods """Returns all the fields from the SQLAlchemy class.""" model = TransactionStatusCode + load_instance = True diff --git a/pay-api/src/pay_api/resources/ops.py b/pay-api/src/pay_api/resources/ops.py index 5441be70a..2926fa831 100755 --- a/pay-api/src/pay_api/resources/ops.py +++ b/pay-api/src/pay_api/resources/ops.py @@ -27,7 +27,7 @@ def get_ops_healthz(): """Return a JSON object stating the health of the Service and dependencies.""" try: - db.engine.execute(SQL) + db.session.execute(SQL) except exc.SQLAlchemyError: return {'message': 'api is down'}, 500 diff --git a/pay-api/src/pay_api/resources/v1/account.py b/pay-api/src/pay_api/resources/v1/account.py index 2ba493ff4..097a638ae 100644 --- a/pay-api/src/pay_api/resources/v1/account.py +++ b/pay-api/src/pay_api/resources/v1/account.py @@ -28,7 +28,6 @@ from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import CfsAccountStatus, ContentType, Role from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('ACCOUNTS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/accounts') @@ -79,7 +78,6 @@ def get_eft_accounts(): @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'PUT', 'DELETE']) -@_tracing.trace() @_jwt.requires_auth def get_account(account_number: str): """Get payment account details.""" @@ -93,7 +91,6 @@ def get_account(account_number: str): @bp.route('//eft', methods=['PATCH']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.SYSTEM.value]) def patch_account(account_number: str): """Enable eft for an account.""" @@ -110,7 +107,6 @@ def patch_account(account_number: str): @bp.route('/', methods=['PUT']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.SYSTEM.value]) def put_account(account_number: str): """Update the payment account records.""" @@ -137,7 +133,6 @@ def put_account(account_number: str): @bp.route('/', methods=['DELETE']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.SYSTEM.value]) def delete_account(account_number: str): """Delete payment account details.""" @@ -228,7 +223,6 @@ def put_account_fee_product(account_number: str, product: str): @bp.route('//payments/queries', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.requires_auth def post_search_purchase_history(account_number: str): """Search purchase history.""" @@ -258,7 +252,6 @@ def post_search_purchase_history(account_number: str): @bp.route('//payments/reports', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.requires_auth def post_account_purchase_report(account_number: str): """Create the account purchase report.""" diff --git a/pay-api/src/pay_api/resources/v1/account_statements.py b/pay-api/src/pay_api/resources/v1/account_statements.py index 09eb46ebb..32a348c61 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements.py +++ b/pay-api/src/pay_api/resources/v1/account_statements.py @@ -24,7 +24,6 @@ from pay_api.utils.constants import EDIT_ROLE from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import ContentType -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('ACCOUNT_STATEMENTS', __name__, @@ -33,7 +32,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.requires_auth def get_account_statements(account_id): """Get all statements records for an account.""" @@ -52,7 +50,6 @@ def get_account_statements(account_id): @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.requires_auth def get_account_statement(account_id: str, statement_id: str): """Create the statement report.""" @@ -74,7 +71,6 @@ def get_account_statement(account_id: str, statement_id: str): @bp.route('/summary', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.requires_auth def get_account_statement_summary(account_id: str): """Create the statement report.""" diff --git a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py index 9b492d6c7..59beb51af 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py @@ -25,7 +25,6 @@ from pay_api.utils.constants import CHANGE_STATEMENT_SETTINGS, EDIT_ROLE from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('ACCOUNT_NOTIFICATIONS', __name__, @@ -34,7 +33,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_auth def get_account_notifications(account_id): """Get all statements records for an account.""" @@ -50,7 +48,6 @@ def get_account_notifications(account_id): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def post_account_notification(account_id): """Update the statement settings .""" diff --git a/pay-api/src/pay_api/resources/v1/account_statements_settings.py b/pay-api/src/pay_api/resources/v1/account_statements_settings.py index b40b70cd3..e1f4b42e7 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_settings.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_settings.py @@ -23,7 +23,6 @@ from pay_api.utils.auth import jwt as _jwt from pay_api.utils.constants import CHANGE_STATEMENT_SETTINGS, EDIT_ROLE from pay_api.utils.endpoints_enums import EndpointEnum -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('ACCOUNT_SETTINGS', __name__, @@ -32,7 +31,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_auth def get_account_statement_settings(account_id): """Get all statements records for an account.""" @@ -48,7 +46,6 @@ def get_account_statement_settings(account_id): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def post_account_statement_settings(account_id): """Update the statement settings .""" diff --git a/pay-api/src/pay_api/resources/v1/code.py b/pay-api/src/pay_api/resources/v1/code.py index 2794a7696..d1f3d64d6 100644 --- a/pay-api/src/pay_api/resources/v1/code.py +++ b/pay-api/src/pay_api/resources/v1/code.py @@ -18,7 +18,6 @@ from flask_cors import cross_origin from pay_api.services.code import Code as CodeService -from pay_api.utils.trace import tracing as _tracing from pay_api.utils.endpoints_enums import EndpointEnum @@ -27,7 +26,6 @@ @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() def get_codes_by_type(code_type): """Return all codes based on code_type.""" return CodeService.find_code_values_by_type(code_type), HTTPStatus.OK @@ -35,7 +33,6 @@ def get_codes_by_type(code_type): @bp.route('//', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() def get_code(code_type, code): """Return all codes based on code_type.""" return CodeService.find_code_value_by_type_and_code(code_type, code), HTTPStatus.OK diff --git a/pay-api/src/pay_api/resources/v1/distributions.py b/pay-api/src/pay_api/resources/v1/distributions.py index 5329c5c78..42e5b7b75 100644 --- a/pay-api/src/pay_api/resources/v1/distributions.py +++ b/pay-api/src/pay_api/resources/v1/distributions.py @@ -24,7 +24,6 @@ from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('DISTRIBUTIONS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/fees/distributions') @@ -32,7 +31,6 @@ @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'PUT']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def get_fee_distribution(distribution_code_id: int): """Return distribution by provided id.""" @@ -49,7 +47,6 @@ def get_fee_distribution(distribution_code_id: int): @bp.route('/', methods=['PUT']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def put_fee_distribution(distribution_code_id: int): """Update distribution from the payload.""" @@ -71,7 +68,6 @@ def put_fee_distribution(distribution_code_id: int): @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def get_fee_distributions(): """Return all distributions.""" @@ -88,7 +84,6 @@ def get_fee_distributions(): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def post_fee_distribution(): """Create a new distribution from the payload.""" @@ -110,7 +105,6 @@ def post_fee_distribution(): @bp.route('//schedules', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def get_fee_distribution_schedules(distribution_code_id: int): """Return all fee schedules linked to the distribution.""" @@ -127,7 +121,6 @@ def get_fee_distribution_schedules(distribution_code_id: int): @bp.route('//schedules', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) def post_fee_distribution_schedule(distribution_code_id: int): """Create link between distribution and fee schedule.""" diff --git a/pay-api/src/pay_api/resources/v1/eft_short_names.py b/pay-api/src/pay_api/resources/v1/eft_short_names.py index b55416988..2209d6f3a 100644 --- a/pay-api/src/pay_api/resources/v1/eft_short_names.py +++ b/pay-api/src/pay_api/resources/v1/eft_short_names.py @@ -26,7 +26,6 @@ from pay_api.utils.auth import jwt as _jwt from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role -from pay_api.utils.trace import tracing as _tracing from pay_api.utils.util import string_to_date @@ -76,7 +75,6 @@ def get_eft_shortnames(): @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'PATCH']) -@_tracing.trace() @_jwt.requires_auth @_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) def get_eft_shortname(short_name_id: int): @@ -94,7 +92,6 @@ def get_eft_shortname(short_name_id: int): @bp.route('/', methods=['PATCH']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) def patch_eft_shortname(short_name_id: int): """Update EFT short name mapping.""" @@ -117,7 +114,6 @@ def patch_eft_shortname(short_name_id: int): @bp.route('//transactions', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.requires_auth @_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) def get_eft_shortname_transactions(short_name_id: int): diff --git a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py index 6ab11edbc..aab02d8be 100644 --- a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py +++ b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py @@ -24,7 +24,6 @@ from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('FAS_ROUTING_SLIPS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/fas/routing-slips') @@ -32,7 +31,6 @@ @bp.route('', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_CREATE.value]) def post_routing_slip(): """Create routing slip.""" @@ -54,7 +52,6 @@ def post_routing_slip(): @bp.route('/queries', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_SEARCH.value]) def post_search_routing_slips(): """Get routing slips.""" @@ -79,7 +76,6 @@ def post_search_routing_slips(): @bp.route('//reports', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_REPORTS.value]) def post_routing_slip_report(date: str): """Create routing slip report.""" @@ -98,7 +94,6 @@ def post_routing_slip_report(date: str): @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'PATCH']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_VIEW.value]) def get_routing_slip(routing_slip_number: str): """Get routing slip.""" @@ -118,7 +113,6 @@ def get_routing_slip(routing_slip_number: str): @bp.route('/', methods=['PATCH']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_EDIT.value]) def patch_routing_slip(routing_slip_number: str): """Patch routing slip.""" @@ -135,7 +129,6 @@ def patch_routing_slip(routing_slip_number: str): @bp.route('//links', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_VIEW.value, Role.FAS_LINK.value]) def get_routing_slip_links(routing_slip_number: str): """Get routing slip links ;ie parent/child details.""" @@ -152,7 +145,6 @@ def get_routing_slip_links(routing_slip_number: str): @bp.route('/links', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.FAS_LINK.value]) def post_routing_slip_link(): """Get routing slip links ;ie parent/child details.""" @@ -176,7 +168,6 @@ def post_routing_slip_link(): @bp.route('//comments', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) @_jwt.has_one_of_roles([Role.FAS_VIEW.value]) -@_tracing.trace() def get_routing_slip_comments(routing_slip_number: str): """Get comments for a slip.""" current_app.logger.info('/comments', methods=['POST']) @cross_origin(origins='*') @_jwt.has_one_of_roles([Role.FAS_VIEW.value]) -@_tracing.trace() def post_routing_slip_comment(routing_slip_number: str): """Create comment for a slip.""" current_app.logger.info('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() @_jwt.has_one_of_roles([Role.VIEWER.value, Role.EDITOR.value, Role.STAFF.value]) def get_fee_by_corp_and_filing_type(corp_type, filing_type_code): """Calculate the fee for the filing using the corp type/filing type and return fee.""" diff --git a/pay-api/src/pay_api/resources/v1/fee_schedule.py b/pay-api/src/pay_api/resources/v1/fee_schedule.py index d26dccea0..4f148fc9f 100644 --- a/pay-api/src/pay_api/resources/v1/fee_schedule.py +++ b/pay-api/src/pay_api/resources/v1/fee_schedule.py @@ -20,14 +20,12 @@ from pay_api.exceptions import BusinessException from pay_api.services import FeeSchedule from pay_api.utils.endpoints_enums import EndpointEnum -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('FEE_SCHEDULE', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/fees/schedules') @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) -@_tracing.trace() def get_fee_schedules(): """Calculate the fee for the filing using the corp type/filing type and return fee.""" try: diff --git a/pay-api/src/pay_api/resources/v1/invoice.py b/pay-api/src/pay_api/resources/v1/invoice.py index fb3b20d65..9331a2da8 100644 --- a/pay-api/src/pay_api/resources/v1/invoice.py +++ b/pay-api/src/pay_api/resources/v1/invoice.py @@ -27,7 +27,6 @@ from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing from pay_api.utils.util import get_str_by_path bp = Blueprint('INVOICE', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/payment-requests') @@ -35,7 +34,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_roles([Role.SYSTEM.value]) def get_invoices(): """Get the invoice records.""" @@ -51,7 +49,6 @@ def get_invoices(): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def post_invoice(): """Create the payment request records.""" @@ -79,7 +76,6 @@ def post_invoice(): @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'DELETE', 'PATCH']) -@_tracing.trace() @_jwt.requires_auth def get_invoice(invoice_id): """Get the invoice records.""" @@ -92,7 +88,6 @@ def get_invoice(invoice_id): @bp.route('/', methods=['DELETE']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def delete_invoice(invoice_id): """Soft delete the invoice records.""" @@ -112,7 +107,6 @@ def delete_invoice(invoice_id): @bp.route('/', methods=['PATCH']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def patch_invoice(invoice_id: int = None): """Update the payment method for an online banking .""" @@ -139,7 +133,6 @@ def patch_invoice(invoice_id: int = None): @bp.route('//reports', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) @_jwt.requires_auth -@_tracing.trace() def post_invoice_report(invoice_id: int = None): """Update the payment method for an online banking .""" current_app.logger.info('/payments') @@ -34,7 +33,6 @@ @bp.route('', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_auth def get_account_payments(account_id: str): """Get account payments.""" @@ -52,7 +50,6 @@ def get_account_payments(account_id: str): @bp.route('', methods=['POST']) @cross_origin(origins='*') -@_tracing.trace() @_jwt.requires_auth def post_account_payment(account_id: str): """Create account payments.""" diff --git a/pay-api/src/pay_api/resources/v1/transaction.py b/pay-api/src/pay_api/resources/v1/transaction.py index c35aba958..4e167f116 100644 --- a/pay-api/src/pay_api/resources/v1/transaction.py +++ b/pay-api/src/pay_api/resources/v1/transaction.py @@ -23,7 +23,6 @@ from pay_api.utils.auth import jwt as _jwt from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.errors import Error -from pay_api.utils.trace import tracing as _tracing bp = Blueprint('TRANSACTIONS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}') @@ -31,7 +30,6 @@ @bp.route('/payment-requests//transactions', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'POST']) -@_tracing.trace() @_jwt.requires_auth def get_transactions(invoice_id): """Get all transaction records for a invoice.""" @@ -44,7 +42,6 @@ def get_transactions(invoice_id): @bp.route('/payment-requests//transactions', methods=['POST']) @bp.route('/payments//transactions', methods=['POST', 'OPTIONS']) @cross_origin(origins='*', methods=['POST']) -@_tracing.trace() def post_transaction(invoice_id: int = None, payment_id: int = None): """Create the Transaction records.""" current_app.logger.info('/transactions/', methods=['GET', 'OPTIONS']) @bp.route('/payments//transactions/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET', 'PATCH']) -@_tracing.trace() @_jwt.requires_auth def get_transaction(invoice_id: int = None, payment_id: int = None, transaction_id=None): """Get the Transaction record.""" @@ -93,7 +89,6 @@ def get_transaction(invoice_id: int = None, payment_id: int = None, transaction_ @bp.route('/payment-requests//transactions/', methods=['PATCH']) @bp.route('/payments//transactions/', methods=['PATCH']) @cross_origin(origins='*') -@_tracing.trace() def patch_transaction(invoice_id: int = None, payment_id: int = None, transaction_id=None): """Update the transaction record by querying payment system.""" current_app.logger.info(' None: # pylint:disable=unused-argum def _release_payment(invoice: Invoice): """Release record.""" from .payment_transaction import PaymentTransaction # pylint:disable=import-outside-toplevel,cyclic-import - from .payment_transaction import publish_response # pylint:disable=import-outside-toplevel,cyclic-import if invoice.corp_type_code in [CorpType.CSO.value, CorpType.RPT.value, CorpType.PPR.value, CorpType.VS.value]: return @@ -150,7 +150,7 @@ def _release_payment(invoice: Invoice): payload = PaymentTransaction.create_event_payload(invoice, TransactionStatus.COMPLETED.value) try: current_app.logger.info(f'Releasing record for invoice {invoice.id}') - publish_response(payload=payload, subject=get_pay_subject_name(invoice.corp_type_code)) + gcp_queue_publisher.publish_to_queue(payload) except Exception as e: # NOQA pylint: disable=broad-except current_app.logger.error(e) current_app.logger.error('Notification to Queue failed for the Payment Event %s', payload) @@ -198,7 +198,6 @@ def _refund_and_create_credit_memo(invoice: InvoiceModel): @staticmethod def _publish_refund_to_mailer(invoice: InvoiceModel): """Construct message and send to mailer queue.""" - from .payment_transaction import publish_response # pylint:disable=import-outside-toplevel,cyclic-import receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(invoice_id=invoice.id) invoice_ref: InvoiceReferenceModel = InvoiceReferenceModel.find_by_invoice_id_and_status( invoice_id=invoice.id, status_code=InvoiceReferenceStatus.COMPLETED.value) @@ -238,8 +237,7 @@ def _publish_refund_to_mailer(invoice: InvoiceModel): 'filingDescription': filing_description }) current_app.logger.debug(f'Publishing payment refund request to mailer for {invoice.id} : {q_payload}') - publish_response(payload=q_payload, client_name=current_app.config.get('NATS_MAILER_CLIENT_NAME'), - subject=current_app.config.get('NATS_MAILER_SUBJECT')) + gcp_queue_publisher.publish_to_queue(q_payload) def complete_payment(self, invoice, invoice_reference): """Create payment and related records as if the payment is complete.""" diff --git a/pay-api/src/pay_api/services/distribution_code.py b/pay-api/src/pay_api/services/distribution_code.py index 4a31d4e2b..0b0760ff8 100644 --- a/pay-api/src/pay_api/services/distribution_code.py +++ b/pay-api/src/pay_api/services/distribution_code.py @@ -338,7 +338,7 @@ def find_fee_schedules_by_distribution_id(distribution_id: int): fee_schedules = DistributionCodeLinkModel.find_fee_schedules_by_distribution_id( distribution_code_id=distribution_id) - fee_schedule_schema = FeeScheduleSchema(exclude=('distribution_codes',)) + fee_schedule_schema = FeeScheduleSchema() data['items'] = fee_schedule_schema.dump(fee_schedules, many=True) current_app.logger.debug('>find_fee_schedules_by_distribution_id') return data diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index 6316f8d6e..9d30e3677 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -329,9 +329,9 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = EFTShortnameModel.linked_by_name, EFTShortnameModel.linked_on, case( - [(PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), - func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') - )], + (PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), + func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') + ), else_=PaymentAccountModel.name ).label('account_name'), PaymentAccountModel.branch_name.label('account_branch')) diff --git a/pay-api/src/pay_api/services/fas/routing_slip.py b/pay-api/src/pay_api/services/fas/routing_slip.py index 1b361aaf5..c8eef8db6 100644 --- a/pay-api/src/pay_api/services/fas/routing_slip.py +++ b/pay-api/src/pay_api/services/fas/routing_slip.py @@ -219,12 +219,10 @@ def create_daily_reports(cls, date: str, **kwargs): total_cheque_usd = 0 total_cash_cad = 0 total_cheque_cad = 0 - # TODO Only CAD supported now, so just add up the total. for routing_slip in routing_slips: total += float(routing_slip.total) if routing_slip.payment_account.payment_method == PaymentMethod.CASH.value: no_of_cash += 1 - # TODO check if the payment is CAD or USD. total_cash_cad += routing_slip.total if routing_slip.total_usd is not None: total_cash_usd += routing_slip.total_usd diff --git a/pay-api/src/pay_api/services/fee_schedule.py b/pay-api/src/pay_api/services/fee_schedule.py index f3bd6cb71..87ef380d3 100644 --- a/pay-api/src/pay_api/services/fee_schedule.py +++ b/pay-api/src/pay_api/services/fee_schedule.py @@ -211,12 +211,12 @@ def service_fees(self, value: Decimal): @property def gst(self): """Return the fee amount.""" - return 0 # TODO + return 0 @property def pst(self): """Return the fee amount.""" - return 0 # TODO + return 0 @property def quantity(self): @@ -338,7 +338,7 @@ def find_all( } fee_schdules = FeeScheduleModel.find_all(corp_type_code=corp_type, filing_type_code=filing_type_code, description=description) - schdule_schema = FeeScheduleSchema(exclude=('distribution_codes',)) + schdule_schema = FeeScheduleSchema() data['items'] = schdule_schema.dump(fee_schdules, many=True) current_app.logger.debug('>find_all') return data diff --git a/pay-api/src/pay_api/services/gcp_queue_publisher.py b/pay-api/src/pay_api/services/gcp_queue_publisher.py index 8b2f8400a..0a4a8659f 100644 --- a/pay-api/src/pay_api/services/gcp_queue_publisher.py +++ b/pay-api/src/pay_api/services/gcp_queue_publisher.py @@ -10,16 +10,14 @@ from google.cloud import pubsub_v1 from simple_cloudevent import SimpleCloudEvent, to_queue_message -from .invoice import Invoice - -def publish_to_queue(payload: dict, invoice: Invoice): - """Publish a 'COMPLETED' invoice's info to the GCP PubSub Queue.""" +def publish_to_queue(payload: dict): + """Publish to GCP PubSub Queue.""" ce = SimpleCloudEvent() ce.id = payload.get('paymentToken', {}).get('id', str(uuid.uuid4())) ce.source = 'sbc-pay' - ce.subject = invoice.business_identifier - ce.time = invoice.payment_date + ce.subject = 'SUBJECT' # invoice.business_identifier + ce.time = 'TIME' # invoice.payment_date ce.type = 'payment' ce.data = payload @@ -32,10 +30,8 @@ def _send_to_queue(payload: bytes): (audience := current_app.config.get('AUDIENCE')) and (topic_name := current_app.config.get('TOPIC_NAME')) and (publisher_audience := current_app.config.get('PUBLISHER_AUDIENCE'))): - raise Exception('missing setup arguments') # pylint: disable=W0719 - # get authenticated publisher try: service_account_info = json.loads(base64.b64decode(gcp_auth_key).decode('utf-8')) credentials = jwt.Credentials.from_service_account_info( @@ -48,7 +44,6 @@ def _send_to_queue(payload: bytes): try: future = publisher.publish(topic_name, payload) - return future.result() except (CancelledError, TimeoutError) as error: raise Exception('Unable to post to queue', error) from error # pylint: disable=W0719 diff --git a/pay-api/src/pay_api/services/internal_pay_service.py b/pay-api/src/pay_api/services/internal_pay_service.py index b477e2fa3..4a5a0bec7 100644 --- a/pay-api/src/pay_api/services/internal_pay_service.py +++ b/pay-api/src/pay_api/services/internal_pay_service.py @@ -58,8 +58,7 @@ def create_invoice(self, payment_account: PaymentAccount, line_items: List[Payme if not is_zero_dollar_invoice and routing_slip is not None: # creating invoice in cfs is done in job current_app.logger.info(f'FAS Routing slip found with remaining amount : {routing_slip.remaining_amount}') - routing_slip.remaining_amount = routing_slip.remaining_amount - \ - get_quantized(invoice.total) + routing_slip.remaining_amount -= get_quantized(invoice.total) if routing_slip.status == RoutingSlipStatus.ACTIVE.value and routing_slip.remaining_amount < 0.01: routing_slip.status = RoutingSlipStatus.COMPLETE.value routing_slip.flush() diff --git a/pay-api/src/pay_api/services/non_sufficient_funds.py b/pay-api/src/pay_api/services/non_sufficient_funds.py index 925a4923f..289ce6c73 100644 --- a/pay-api/src/pay_api/services/non_sufficient_funds.py +++ b/pay-api/src/pay_api/services/non_sufficient_funds.py @@ -97,7 +97,7 @@ def query_all_non_sufficient_funds_invoices(account_id: str): InvoiceModel.id.label('invoice_id'), (InvoiceModel.total - InvoiceModel.paid).label('amount_remaining'), func.max(case( - [(PaymentLineItemModel.description == ReverseOperation.NSF.value, PaymentLineItemModel.total)], + (PaymentLineItemModel.description == ReverseOperation.NSF.value, PaymentLineItemModel.total), else_=0)).label('nsf_amount') ) .join(InvoiceReferenceModel, InvoiceReferenceModel.invoice_id == InvoiceModel.id) diff --git a/pay-api/src/pay_api/services/payment.py b/pay-api/src/pay_api/services/payment.py index 5bcad3169..7ced1f606 100644 --- a/pay-api/src/pay_api/services/payment.py +++ b/pay-api/src/pay_api/services/payment.py @@ -476,7 +476,7 @@ def _prepare_csv_data(results): ), total_fees, total_gst + total_pst, - total_fees - service_fee, # TODO + total_fees - service_fee, service_fee, invoice.get('status_code'), invoice.get('business_identifier'), diff --git a/pay-api/src/pay_api/services/payment_account.py b/pay-api/src/pay_api/services/payment_account.py index a6cca848a..fce65a7bf 100644 --- a/pay-api/src/pay_api/services/payment_account.py +++ b/pay-api/src/pay_api/services/payment_account.py @@ -38,8 +38,8 @@ from pay_api.models.payment_account import PaymentAccountSearchModel from pay_api.services.cfs_service import CFSService from pay_api.services.distribution_code import DistributionCode +from pay_api.services import gcp_queue_publisher from pay_api.services.oauth_service import OAuthService -from pay_api.services.queue_publisher import publish_response from pay_api.services.receipt import Receipt as ReceiptService from pay_api.services.statement import Statement from pay_api.services.statement_settings import StatementSettings @@ -436,7 +436,7 @@ def _save_account(cls, account_request: Dict[str, any], payment_account: Payment # pylint:disable=cyclic-import, import-outside-toplevel from pay_api.factory.payment_system_factory import PaymentSystemFactory - payment_account.auth_account_id = account_request.get('accountId') + payment_account.auth_account_id = str(account_request.get('accountId')) # If the payment method is CC, set the payment_method as DIRECT_PAY if payment_method := get_str_by_path(account_request, 'paymentInfo/methodOfPayment'): @@ -606,7 +606,7 @@ def _get_payment_based_on_pad_activation(account: PaymentAccountModel) -> Tuple[ else: # Handle repeated changing of pad to bcol ;then to pad again etc new_activation_date = account.pad_activation_date # was already in pad ;no need to extend - is_previous_pad_activated = account.pad_activation_date < datetime.now() + is_previous_pad_activated = new_activation_date < datetime.now(new_activation_date.tzinfo) if is_previous_pad_activated: # was in PAD ; so no need of activation period wait time and no need to be in bcol..so use PAD again new_payment_method = PaymentMethod.PAD.value @@ -796,7 +796,8 @@ def asdict(self, **kwargs): def _is_pad_in_pending_activation(self): """Find if PAD is awaiting activation.""" - return self.pad_activation_date and self.pad_activation_date > datetime.now() and self.cfs_account_status in \ + return self.pad_activation_date and self.pad_activation_date > datetime.now(self.pad_activation_date.tzinfo) \ + and self.cfs_account_status in \ (CfsAccountStatus.PENDING.value, CfsAccountStatus.PENDING_PAD_ACTIVATION.value) def publish_account_mailer_event_on_creation(self): @@ -808,9 +809,7 @@ def publish_account_mailer_event_on_creation(self): def _publish_queue_message(self, payload): """Publish to account mailer to send out confirmation email or notification email.""" try: - publish_response(payload=payload, - client_name=current_app.config['NATS_MAILER_CLIENT_NAME'], - subject=current_app.config['NATS_MAILER_SUBJECT']) + gcp_queue_publisher.publish_to_queue(payload) except Exception as e: # NOQA pylint: disable=broad-except current_app.logger.error(e) current_app.logger.error( @@ -873,9 +872,7 @@ def unlock_frozen_accounts(payment: Payment): ) try: - publish_response(payload=payload, - client_name=current_app.config['NATS_ACCOUNT_CLIENT_NAME'], - subject=current_app.config['NATS_ACCOUNT_SUBJECT']) + gcp_queue_publisher.publish_to_queue(payload=payload) except Exception as e: # NOQA pylint: disable=broad-except current_app.logger.error(e) current_app.logger.error( diff --git a/pay-api/src/pay_api/services/payment_transaction.py b/pay-api/src/pay_api/services/payment_transaction.py index 60039674f..641bdd189 100644 --- a/pay-api/src/pay_api/services/payment_transaction.py +++ b/pay-api/src/pay_api/services/payment_transaction.py @@ -16,7 +16,6 @@ from __future__ import annotations import uuid -from contextlib import suppress from datetime import datetime from typing import Dict, List @@ -28,17 +27,16 @@ from pay_api.models import PaymentTransaction as PaymentTransactionModel from pay_api.models import PaymentTransactionSchema from pay_api.services.base_payment_system import PaymentSystemService -from pay_api.services.gcp_queue_publisher import publish_to_queue +from pay_api.services import gcp_queue_publisher from pay_api.services.invoice import Invoice from pay_api.services.invoice_reference import InvoiceReference from pay_api.services.payment_account import PaymentAccount from pay_api.services.receipt import Receipt from pay_api.utils.enums import InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, TransactionStatus from pay_api.utils.errors import Error -from pay_api.utils.util import get_pay_subject_name, is_valid_redirect_url +from pay_api.utils.util import is_valid_redirect_url from .payment import Payment -from .queue_publisher import publish_response class PaymentTransaction: # pylint: disable=too-many-instance-attributes, too-many-public-methods @@ -498,12 +496,7 @@ def publish_status(transaction_dao: PaymentTransactionModel, invoice: Invoice): payload = PaymentTransaction.create_event_payload(invoice, status_code) try: - publish_response(payload=payload, subject=get_pay_subject_name(invoice.corp_type_code)) - - # First stage in rolling in the new queue services - # It'll not block or disrupt any flows (in theory) - with suppress(Exception): - publish_to_queue(payload=payload, invoice=invoice) + gcp_queue_publisher.publish_to_queue(payload=payload) except Exception as e: # NOQA pylint: disable=broad-except current_app.logger.error(e) diff --git a/pay-api/src/pay_api/services/queue_publisher.py b/pay-api/src/pay_api/services/queue_publisher.py deleted file mode 100644 index eb1e0d5fe..000000000 --- a/pay-api/src/pay_api/services/queue_publisher.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Service class to control all the operations related to Payment.""" - -import asyncio -import json -import random -from typing import Dict - -from flask import current_app -from nats.aio.client import Client as NATS # noqa N814; by convention the name is NATS -from stan.aio.client import Client as STAN # noqa N814; by convention the name is STAN - -from pay_api.utils.handlers import closed_cb, error_cb # noq I001; conflict with flake8 - - -def publish_response(payload: Dict[str, any], subject: str, client_name: str = None): - """Publish payment response to async nats.""" - asyncio.run(publish(payload=payload, client_name=client_name, subject=subject)) - - -# Connection and Queue configuration. -def nats_connection_options(client_name: str): - """Return nats connection options.""" - return { - 'servers': current_app.config.get('NATS_SERVERS'), - 'error_cb': error_cb, - 'closed_cb': closed_cb, - 'name': client_name or current_app.config.get('NATS_PAYMENT_CLIENT_NAME'), - } - - -def stan_connection_options(nats_con: NATS): - """Return stan connection options.""" - return { - 'cluster_id': current_app.config.get('NATS_CLUSTER_ID'), - 'client_id': str(random.SystemRandom().getrandbits(0x58)), - 'nats': nats_con - } - - -async def publish(payload, subject: str, client_name: str = None): # pylint: disable=too-few-public-methods - """Service to manage Queue publish operations.""" - current_app.logger.debug('publish') diff --git a/pay-api/src/pay_api/services/statement.py b/pay-api/src/pay_api/services/statement.py index 98a30559f..5a23b00c5 100644 --- a/pay-api/src/pay_api/services/statement.py +++ b/pay-api/src/pay_api/services/statement.py @@ -210,8 +210,9 @@ def populate_overdue_from_invoices(statements: List[StatementModel]): """Populate is_overdue field for statements.""" # Invoice status can change after a statement has been generated. statement_ids = [statements.id for statements in statements] - overdue_statements = db.session.query(func.count(InvoiceModel.id).label('overdue_invoices'), - StatementInvoicesModel.statement_id) \ + overdue_statements = db.session.query( + func.count(InvoiceModel.id).label('overdue_invoices'), # pylint:disable=not-callable + StatementInvoicesModel.statement_id) \ .join(StatementInvoicesModel) \ .filter(InvoiceModel.invoice_status_code == InvoiceStatus.OVERDUE.value) \ .filter(StatementInvoicesModel.invoice_id == InvoiceModel.id) \ diff --git a/pay-api/src/pay_api/services/statement_settings.py b/pay-api/src/pay_api/services/statement_settings.py index d9be9f61d..602c943ce 100644 --- a/pay-api/src/pay_api/services/statement_settings.py +++ b/pay-api/src/pay_api/services/statement_settings.py @@ -168,7 +168,7 @@ def update_statement_settings(auth_account_id: str, frequency: str): max_frequency = StatementSettings._find_longest_frequency(current_statements_settings.frequency, frequency) last_date = StatementSettings._get_end_of(max_frequency) - current_statements_settings.to_date = last_date + current_statements_settings.to_date = last_date # TODO Should be date not date time? current_statements_settings.save() new_statements_settings = StatementSettingsModel(frequency=frequency, diff --git a/pay-api/src/pay_api/utils/handlers.py b/pay-api/src/pay_api/utils/handlers.py deleted file mode 100644 index ed26e0f21..000000000 --- a/pay-api/src/pay_api/utils/handlers.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Callbacks and signal trapping used in the main loop.""" -from flask import current_app - - -async def error_cb(e): - """Emit error message to the log stream.""" - current_app.logger.error(e) - raise e - - -async def closed_cb(): - """Exit the session after the NATS connection is closed.""" - current_app.logger.info('Connection to NATS is closed.') - # my_loop = asyncio.get_running_loop() - # await asyncio.sleep(0.1, loop=my_loop) - # my_loop.stop() diff --git a/pay-api/src/pay_api/utils/logging.py b/pay-api/src/pay_api/utils/logging.py index 8b88ddcf2..8568f87dd 100755 --- a/pay-api/src/pay_api/utils/logging.py +++ b/pay-api/src/pay_api/utils/logging.py @@ -18,12 +18,7 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) print(f'Configure logging, from conf:{conf}', file=sys.stdout) diff --git a/pay-api/src/pay_api/utils/trace.py b/pay-api/src/pay_api/utils/trace.py deleted file mode 100644 index 0b6297af3..000000000 --- a/pay-api/src/pay_api/utils/trace.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Bring in the Tracer.""" -from sbc_common_components.tracing.api_tracer import ApiTracer -from sbc_common_components.tracing.api_tracing import ApiTracing - - -# initialize tracer -API_TRACER = ApiTracer('Payment Services') -tracing = ApiTracing( # pylint: disable=invalid-name; lower case name as used by convention in most Flask apps - API_TRACER.tracer) diff --git a/pay-api/src/pay_api/utils/util.py b/pay-api/src/pay_api/utils/util.py index 641a44fc7..62aa51569 100755 --- a/pay-api/src/pay_api/utils/util.py +++ b/pay-api/src/pay_api/utils/util.py @@ -29,7 +29,7 @@ from flask import current_app from .constants import DT_SHORT_FORMAT -from .enums import CorpType, StatementFrequency +from .enums import StatementFrequency def cors_preflight(methods: str = 'GET'): @@ -184,22 +184,6 @@ def mask(val: str, preserve_length: int = 0) -> str: return val[-preserve_length:].rjust(len(val), replace_char) -def get_pay_subject_name(corp_type: str, subject_format: str = None): - """Return payment subject name.""" - # TODO Refactor later - subject_format = subject_format or current_app.config.get('NATS_PAYMENT_SUBJECT') - - if corp_type == CorpType.NRO.value: - pay_subject = 'name-request' - elif corp_type == CorpType.PPR.value: - pay_subject = 'ppr' - elif corp_type == CorpType.VS.value: - pay_subject = 'vs' - else: - pay_subject = 'filing' - return subject_format.format(product=pay_subject) - - def get_nearest_business_day(date_val: datetime, include_today: bool = True) -> datetime: """Return nearest business day to the date. diff --git a/pay-api/tests/conftest.py b/pay-api/tests/conftest.py index 7970baa73..4de3465b0 100755 --- a/pay-api/tests/conftest.py +++ b/pay-api/tests/conftest.py @@ -17,14 +17,11 @@ import asyncio import os import random -import time import pytest from flask_migrate import Migrate, upgrade -from nats.aio.client import Client as Nats from sqlalchemy import event, text -from sqlalchemy.schema import DropConstraint, MetaData -from stan.aio.client import Client as Stan +from sqlalchemy_utils import create_database, database_exists, drop_database from pay_api import create_app from pay_api import jwt as _jwt @@ -36,10 +33,15 @@ def app(): """Return a session-wide application configured in TEST mode.""" _app = create_app('testing') - return _app +@pytest.fixture(autouse=True) +def mock_queue_publish(monkeypatch): + """Mock queue publish.""" + monkeypatch.setattr('pay_api.services.gcp_queue_publisher.publish_to_queue', lambda *args, **kwargs: None) + + @pytest.fixture(scope='function') def app_request(): """Return a session-wide application configured in TEST mode.""" @@ -67,160 +69,63 @@ def client_ctx(app): yield _client -@pytest.fixture(scope='session') +@pytest.fixture(scope='session', autouse=True) def db(app): # pylint: disable=redefined-outer-name, invalid-name - """Return a session-wide initialised database. - - Drops all existing tables - Meta follows Postgres FKs - """ + """Return a session-wide initialised database.""" with app.app_context(): - # Clear out views - view_sql = """SELECT table_name FROM information_schema.views - WHERE table_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(view_sql))]: - try: - sess.execute(text('DROP VIEW public.%s ;' % seq)) - print('DROP VIEW public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # Clear out any existing tables - metadata = MetaData(_db.engine) - metadata.reflect() - for table in metadata.tables.values(): - for fk in table.foreign_keys: # pylint: disable=invalid-name - _db.engine.execute(DropConstraint(fk.constraint)) - metadata.drop_all() - _db.drop_all() - - sequence_sql = """SELECT sequence_name FROM information_schema.sequences - WHERE sequence_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(sequence_sql))]: - try: - sess.execute(text('DROP SEQUENCE public.%s ;' % seq)) - print('DROP SEQUENCE public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # ############################################ - # There are 2 approaches, an empty database, or the same one that the app will use - # create the tables - # _db.create_all() - # or - # Use Alembic to load all of the DB revisions including supporting lookup data - # This is the path we'll use in legal_api!! - - # even though this isn't referenced directly, it sets up the internal configs that upgrade needs + if database_exists(_db.engine.url): + drop_database(_db.engine.url) + create_database(_db.engine.url) + _db.session().execute(text('SET TIME ZONE "UTC";')) Migrate(app, _db) upgrade() - return _db @pytest.fixture(scope='function', autouse=True) -def session(app, db): # pylint: disable=redefined-outer-name, invalid-name +def session(db, app): # pylint: disable=redefined-outer-name, invalid-name """Return a function-scoped session.""" with app.app_context(): - conn = db.engine.connect() - txn = conn.begin() + with db.engine.connect() as conn: + transaction = conn.begin() + sess = db._make_scoped_session(dict(bind=conn)) # pylint: disable=protected-access + # Establish SAVEPOINT (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) + nested = sess.begin_nested() + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback + + @event.listens_for(sess, 'after_transaction_end') + def restart_savepoint(sess2, trans): # pylint: disable=unused-variable + nonlocal nested + if trans.nested: + # Handle where test DOESN'T session.commit() + sess2.expire_all() + nested = sess.begin_nested() + # When using a SAVEPOINT via the Session.begin_nested() or Connection.begin_nested() methods, + # the transaction object returned must be used to commit or rollback the SAVEPOINT. + # Calling the Session.commit() or Connection.commit() methods will always commit the + # outermost transaction; this is a SQLAlchemy 2.0 specific behavior that is + # reversed from the 1.x series + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback - options = dict(bind=conn, binds={}) - sess = db.create_scoped_session(options=options) - - # establish a SAVEPOINT just before beginning the test - # (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) - sess.begin_nested() - - @event.listens_for(sess(), 'after_transaction_end') - def restart_savepoint(sess2, trans): # pylint: disable=unused-variable - # Detecting whether this is indeed the nested transaction of the test - if trans.nested and not trans._parent.nested: # pylint: disable=protected-access - # Handle where test DOESN'T session.commit(), - sess2.expire_all() - sess.begin_nested() - - db.session = sess - - sql = text('select 1') - sess.execute(sql) - - yield sess - - # Cleanup - sess.remove() - # This instruction rollsback any commit that were executed in the tests. - txn.rollback() - conn.close() + try: + yield db.session + finally: + db.session.remove() + transaction.rollback() @pytest.fixture(scope='function') def client_id(): """Return a unique client_id that can be used in tests.""" _id = random.SystemRandom().getrandbits(0x58) - # _id = (base64.urlsafe_b64encode(uuid.uuid4().bytes)).replace('=', '') return f'client-{_id}' -@pytest.fixture(scope='session') -def stan_server(docker_services): - """Create the nats / stan services that the integration tests will use.""" - if os.getenv('TEST_NATS_DOCKER'): - docker_services.start('nats') - time.sleep(2) - - -@pytest.fixture(scope='function') -@pytest.mark.asyncio -async def stan(event_loop, client_id): - """Create a stan connection for each function, to be used in the tests.""" - nc = Nats() - sc = Stan() - cluster_name = 'test-cluster' - - await nc.connect(io_loop=event_loop, name='entity.filing.tester') - - await sc.connect(cluster_name, client_id, nats=nc) - - yield sc - - await sc.close() - await nc.close() - - -@pytest.fixture(scope='function') -@pytest.mark.asyncio -async def entity_stan(app, event_loop, client_id): - """Create a stan connection for each function. - - Uses environment variables for the cluster name. - """ - nc = Nats() - sc = Stan() - - await nc.connect(io_loop=event_loop) - - cluster_name = os.getenv('NATS_CLUSTER_ID') - - if not cluster_name: - raise ValueError('Missing env variable: NATS_CLUSTER_ID') - - await sc.connect(cluster_name, client_id, nats=nc) - - yield sc - - await sc.close() - await nc.close() - - @pytest.fixture(scope='function') def future(event_loop): """Return a future that is used for managing function tests.""" @@ -333,7 +238,6 @@ def auto(docker_services, app): @pytest.fixture(scope='session') def docker_compose_files(pytestconfig): """Get the docker-compose.yml absolute path.""" - import os return [ os.path.join(str(pytestconfig.rootdir), 'tests/docker', 'docker-compose.yml') ] diff --git a/pay-api/tests/docker/docker-compose.yml b/pay-api/tests/docker/docker-compose.yml index 01cbe711a..db19b0b01 100644 --- a/pay-api/tests/docker/docker-compose.yml +++ b/pay-api/tests/docker/docker-compose.yml @@ -22,18 +22,6 @@ services: retries: 10 volumes: - ./setup:/tmp/keycloak/test/ - nats: - image: nats-streaming - restart: always - expose: - - 4222 - - 8222 - labels: - - entity.services=nats - ports: - - 4222:4222 - - 8222:8222 - tty: true proxy: image: nginx:alpine diff --git a/pay-api/tests/docker/nginx.conf b/pay-api/tests/docker/nginx.conf index 8a41cc603..a10266c82 100644 --- a/pay-api/tests/docker/nginx.conf +++ b/pay-api/tests/docker/nginx.conf @@ -37,7 +37,7 @@ http { set $last_path_component example1; rewrite ^/reports-api/api/v1/(.*) /$1 break; proxy_set_header Prefer example=$last_path_component; - proxy_set_header Accept "application/json"; #TODO + proxy_set_header Accept "application/json"; proxy_pass http://reports:4010/; } diff --git a/pay-api/tests/unit/api/test_fee.py b/pay-api/tests/unit/api/test_fee.py index 4bb900c12..594aaa503 100755 --- a/pay-api/tests/unit/api/test_fee.py +++ b/pay-api/tests/unit/api/test_fee.py @@ -18,6 +18,7 @@ """ import json from datetime import date, timedelta +from decimal import Decimal from pay_api.models import CorpType, FeeCode, FeeSchedule, FilingType from pay_api.schemas import utils as schema_utils @@ -287,10 +288,10 @@ def factory_filing_type_model( def factory_fee_model( fee_code: str, - amount: int): + amount: float): """Return the fee code model.""" fee_code_master = FeeCode(code=fee_code, - amount=amount) + amount=Decimal(str(amount))) fee_code_master.save() return fee_code_master diff --git a/pay-api/tests/unit/api/test_ops.py b/pay-api/tests/unit/api/test_ops.py index 3a3e17b8e..ca4473f74 100755 --- a/pay-api/tests/unit/api/test_ops.py +++ b/pay-api/tests/unit/api/test_ops.py @@ -17,6 +17,8 @@ Test-Suite to ensure that the /ops endpoint is working as expected. """ +from sqlalchemy.exc import SQLAlchemyError +from pay_api.models import db def test_ops_healthz_success(client): @@ -27,12 +29,13 @@ def test_ops_healthz_success(client): assert rv.json == {'message': 'api is healthy'} -def test_ops_healthz_fail(app_request): - """Assert that the service is unhealthy if a connection toThe database cannot be made.""" - app_request.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://does:not@exist:5432/nada' +def test_ops_healthz_fail(app_request, monkeypatch): + """Assert that the service is unhealthy if a connection to the database cannot be made.""" + def db_error(_): + raise SQLAlchemyError(1, 2, code='42') + monkeypatch.setattr(db.session, 'execute', db_error) with app_request.test_client() as client: rv = client.get('/ops/healthz') - assert rv.status_code == 500 assert rv.json == {'message': 'api is down'} diff --git a/pay-api/tests/unit/api/test_partial_refund.py b/pay-api/tests/unit/api/test_partial_refund.py index 58c156224..551c0f477 100644 --- a/pay-api/tests/unit/api/test_partial_refund.py +++ b/pay-api/tests/unit/api/test_partial_refund.py @@ -35,7 +35,7 @@ from tests.utilities.base_test import get_claims, get_payment_request, token_header -def test_create_refund(session, client, jwt, app, stan_server, monkeypatch): +def test_create_refund(session, client, jwt, app, monkeypatch): """Assert that the endpoint returns 202.""" token = jwt.create_jwt(get_claims(app_request=app), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -106,7 +106,7 @@ def test_create_refund(session, client, jwt, app, stan_server, monkeypatch): assert refund.refund_type == RefundsPartialType.BASE_FEES.value -def test_create_refund_fails(session, client, jwt, app, stan_server, monkeypatch): +def test_create_refund_fails(session, client, jwt, app, monkeypatch): """Assert that the endpoint returns 400.""" token = jwt.create_jwt(get_claims(app_request=app), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} diff --git a/pay-api/tests/unit/api/test_receipt.py b/pay-api/tests/unit/api/test_receipt.py index 05d4a4a0b..f4ad0a0ee 100644 --- a/pay-api/tests/unit/api/test_receipt.py +++ b/pay-api/tests/unit/api/test_receipt.py @@ -140,18 +140,19 @@ def test_receipt_creation_with_invalid_request(session, client, jwt, app): headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} rv = client.post('/api/v1/payment-requests', data=json.dumps(get_payment_request()), headers=headers) - inovice_id = rv.json.get('id') + invoice_id = rv.json.get('id') redirect_uri = 'http%3A//localhost%3A8080/coops-web/transactions%3Ftransaction_id%3Dabcd' receipt_number = '123451' - rv = client.post(f'/api/v1/payment-requests/{inovice_id}/transactions?redirect_uri={redirect_uri}', data=None, + rv = client.post(f'/api/v1/payment-requests/{invoice_id}/transactions?redirect_uri={redirect_uri}', + data=json.dumps({}), headers=headers) txn_id = rv.json.get('id') - client.patch(f'/api/v1/payment-requests/{inovice_id}/transactions/{txn_id}', + client.patch(f'/api/v1/payment-requests/{invoice_id}/transactions/{txn_id}', data=json.dumps({'receipt_number': receipt_number}), headers=headers) filing_data = { 'corpName': 'CP0001234' } - rv = client.post(f'/api/v1/payment-requests/{inovice_id}/receipts', data=json.dumps(filing_data), + rv = client.post(f'/api/v1/payment-requests/{invoice_id}/receipts', data=json.dumps(filing_data), headers=headers) assert rv.status_code == 400 assert rv.json.get('type') == 'INVALID_REQUEST' diff --git a/pay-api/tests/unit/api/test_refund.py b/pay-api/tests/unit/api/test_refund.py index 76d1b559a..69b15993c 100644 --- a/pay-api/tests/unit/api/test_refund.py +++ b/pay-api/tests/unit/api/test_refund.py @@ -33,7 +33,7 @@ get_routing_slip_request, get_unlinked_pad_account_payload, token_header) -def test_create_refund(session, client, jwt, app, stan_server, monkeypatch): +def test_create_refund(session, client, jwt, app, monkeypatch): """Assert that the endpoint returns 202.""" token = jwt.create_jwt(get_claims(app_request=app), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -61,7 +61,7 @@ def test_create_refund(session, client, jwt, app, stan_server, monkeypatch): assert RefundModel.find_by_invoice_id(inv_id) is not None -def test_create_drawdown_refund(session, client, jwt, app, stan_server): +def test_create_drawdown_refund(session, client, jwt, app): """Assert that the endpoint returns 202.""" token = jwt.create_jwt(get_claims(), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -83,7 +83,7 @@ def test_create_drawdown_refund(session, client, jwt, app, stan_server): assert rv.json.get('message') == REFUND_SUCCESS_MESSAGES['DIRECT_PAY.PAID'] -def test_create_pad_refund(session, client, jwt, app, stan_server): +def test_create_pad_refund(session, client, jwt, app): """Assert that the endpoint returns 202 and creates a credit on the account.""" # 1. Create a PAD payment_account and cfs_account. # 2. Create a PAD invoice and mark as PAID. @@ -172,7 +172,7 @@ def test_create_pad_refund(session, client, jwt, app, stan_server): assert pay_account.credit == 0 -def test_create_duplicate_refund_fails(session, client, jwt, app, stan_server): +def test_create_duplicate_refund_fails(session, client, jwt, app): """Assert that the endpoint returns 400.""" token = jwt.create_jwt(get_claims(app_request=app), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -201,7 +201,7 @@ def test_create_duplicate_refund_fails(session, client, jwt, app, stan_server): def test_create_refund_with_existing_routing_slip(session, client, - jwt, app, stan_server): + jwt, app): """Assert that the endpoint returns 202.""" claims = get_claims( roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value, Role.FAS_REFUND.value, Role.STAFF.value, 'make_payment']) @@ -242,7 +242,7 @@ def test_create_refund_with_existing_routing_slip(session, client, def test_create_refund_with_legacy_routing_slip(session, client, - jwt, app, stan_server): + jwt, app): """Assert that the endpoint returns 400.""" claims = get_claims( roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value, Role.FAS_REFUND.value, Role.STAFF.value, 'make_payment']) @@ -261,7 +261,7 @@ def test_create_refund_with_legacy_routing_slip(session, client, assert rv.json.get('type') == 'ROUTING_SLIP_REFUND' -def test_create_refund_fails(session, client, jwt, app, stan_server, monkeypatch): +def test_create_refund_fails(session, client, jwt, app, monkeypatch): """Assert that the endpoint returns 400.""" token = jwt.create_jwt(get_claims(app_request=app), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} diff --git a/pay-api/tests/unit/api/test_transaction.py b/pay-api/tests/unit/api/test_transaction.py index 36c027484..f1315518a 100755 --- a/pay-api/tests/unit/api/test_transaction.py +++ b/pay-api/tests/unit/api/test_transaction.py @@ -195,7 +195,7 @@ def test_transaction_put_with_no_receipt(session, client, jwt, app): @skip_in_pod -def test_transaction_put_completed_payment(session, client, jwt, app, stan_server): +def test_transaction_put_completed_payment(session, client, jwt, app): """Assert that the endpoint returns 200.""" token = jwt.create_jwt(get_claims(), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -254,7 +254,7 @@ def test_transactions_get(session, client, jwt, app): @skip_in_pod -def test_transaction_patch_completed_payment_and_transaction_status(session, client, jwt, app, stan_server): +def test_transaction_patch_completed_payment_and_transaction_status(session, client, jwt, app): """Assert that payment tokens can be retrieved and decoded from the Queue.""" token = jwt.create_jwt(get_claims(), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} diff --git a/pay-api/tests/unit/services/test_distribution_code.py b/pay-api/tests/unit/services/test_distribution_code.py index d847f607d..26dd69c5c 100644 --- a/pay-api/tests/unit/services/test_distribution_code.py +++ b/pay-api/tests/unit/services/test_distribution_code.py @@ -63,7 +63,7 @@ def test_create_distribution_to_fee_link(session, public_user_mock): assert len(schedules.get('items')) == 3 -def test_update_distribution(session, public_user_mock, stan_server, monkeypatch): +def test_update_distribution(session, public_user_mock, monkeypatch): """Assert that the invoice status is updated when the distribution is updated.""" # 1. Create a distribution code # 2. Attach a fee schedule to the distribution diff --git a/pay-api/tests/unit/services/test_payment_transaction.py b/pay-api/tests/unit/services/test_payment_transaction.py index c71a989d3..8abc3b3d5 100644 --- a/pay-api/tests/unit/services/test_payment_transaction.py +++ b/pay-api/tests/unit/services/test_payment_transaction.py @@ -140,7 +140,7 @@ def test_transaction_create_from_invalid_payment(session): @skip_in_pod -def test_transaction_update(session, stan_server, public_user_mock): +def test_transaction_update(session, public_user_mock): """Assert that the payment is saved to the table.""" payment_account = factory_payment_account() payment_account.save() @@ -170,7 +170,7 @@ def test_transaction_update(session, stan_server, public_user_mock): @skip_in_pod -def test_transaction_update_with_no_receipt(session, stan_server): +def test_transaction_update_with_no_receipt(session): """Assert that the payment is saved to the table.""" payment_account = factory_payment_account() payment_account.save() @@ -199,7 +199,7 @@ def test_transaction_update_with_no_receipt(session, stan_server): @skip_in_pod -def test_transaction_update_completed(session, stan_server, public_user_mock): +def test_transaction_update_completed(session, public_user_mock): """Assert that the payment is saved to the table.""" payment_account = factory_payment_account() payment_account.save() @@ -365,7 +365,7 @@ def test_no_existing_transaction(session): @skip_in_pod -def test_transaction_update_on_paybc_connection_error(session, stan_server): +def test_transaction_update_on_paybc_connection_error(session): """Assert that the payment is saved to the table.""" payment_account = factory_payment_account() payment = factory_payment() @@ -458,7 +458,7 @@ def test_update_transaction_for_direct_pay_without_response_url(session): @skip_in_pod -def test_event_failed_transactions(session, public_user_mock, stan_server, monkeypatch): +def test_event_failed_transactions(session, public_user_mock, monkeypatch): """Assert that the transaction status is EVENT_FAILED when Q is not available.""" # 1. Create payment records # 2. Create a transaction @@ -482,7 +482,8 @@ def get_receipt(cls, payment_account, pay_response_url: str, monkeypatch.setattr('pay_api.services.direct_pay_service.DirectPayService.get_receipt', get_receipt) - with patch('pay_api.services.payment_transaction.publish_response', side_effect=ConnectionError('mocked error')): + with patch('pay_api.services.payment_transaction.gcp_queue_publisher.publish_to_queue', + side_effect=ConnectionError('mocked error')): transaction = PaymentTransactionService.update_transaction(transaction.id, pay_response_url='?key=value') @@ -616,7 +617,6 @@ def get_receipt(cls, payment_account, pay_response_url: str, return '1234567890', datetime.now(), 100.00 monkeypatch.setattr('pay_api.services.paybc_service.PaybcService.get_receipt', get_receipt) - txn = PaymentTransactionService.create_transaction_for_payment(payment_2.id, get_paybc_transaction_request()) txn = PaymentTransactionService.update_transaction(txn.id, pay_response_url='receipt_number=123451') diff --git a/pay-api/tests/unit/services/test_queue_publisher.py b/pay-api/tests/unit/services/test_queue_publisher.py deleted file mode 100644 index ba71479da..000000000 --- a/pay-api/tests/unit/services/test_queue_publisher.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests to assure the Queue integration layer. - -Test-Suite to ensure that the Queue publishing is working as expected. -""" -import json -from unittest.mock import patch - -import pytest -from flask import current_app - -from tests import skip_in_pod - -from .utils import subscribe_to_queue - - -@pytest.mark.asyncio -@skip_in_pod -async def test_publish(app, stan_server, client_id, stan, future): - """Assert that payment tokens can be retrieved and decoded from the Queue.""" - with app.app_context(): - # Call back for the subscription - async def subscriber_callback(msg): - current_app.logger.debug('Inside Call Back') - current_app.logger.debug(msg.data.decode('utf-8')) - message_dict = json.loads(msg.data.decode('utf-8')) - assert 'paymentToken' in message_dict - current_app.logger.debug(message_dict['paymentToken']['statusCode']) - assert 'COMPLETED' == message_dict['paymentToken']['statusCode'] - - await subscribe_to_queue(stan, subscriber_callback) - - # publish message - from pay_api.services.queue_publisher import publish - - payload = {'paymentToken': {'id': 1, 'statusCode': 'COMPLETED'}} - - await publish(payload=payload, subject='filing') - - -@pytest.mark.asyncio -@skip_in_pod -async def test_publish_transaction_failed(app, client_id, stan, future, stan_server): - """Assert that payment tokens can be retrieved and decoded from the Queue.""" - with app.app_context(): - # Call back for the subscription - async def subscriber_callback(msg): - current_app.logger.debug('Inside Call Back') - current_app.logger.debug(msg.data.decode('utf-8')) - message_dict = json.loads(msg.data.decode('utf-8')) - assert 'paymentToken' in message_dict - current_app.logger.debug(message_dict['paymentToken']['statusCode']) - assert 'TRANSACTION_FAILED' == message_dict['paymentToken']['statusCode'] - - await subscribe_to_queue(stan, subscriber_callback) - - # publish message - from pay_api.services.queue_publisher import publish - - payload = {'paymentToken': {'id': 100, 'statusCode': 'TRANSACTION_FAILED'}} - - await publish(payload=payload, subject='filing') - - -@pytest.mark.asyncio -@skip_in_pod -async def test_publish_transaction_bulk_load(app, client_id, stan, future, stan_server): - """Assert that payment tokens can be retrieved and decoded from the Queue.""" - with app.app_context(): - # Call back for the subscription - async def subscriber_callback(msg): - current_app.logger.debug('Inside Call Back') - current_app.logger.debug(msg.data.decode('utf-8')) - message_dict = json.loads(msg.data.decode('utf-8')) - assert 'paymentToken' in message_dict - current_app.logger.debug(message_dict['paymentToken']['statusCode']) - assert 'COMPLETED' == message_dict['paymentToken']['statusCode'] - - await subscribe_to_queue(stan, subscriber_callback) - - # publish message - from pay_api.services.queue_publisher import publish - - for i in range(10): - payload = {'paymentToken': {'id': i, 'statusCode': 'COMPLETED'}} - await publish(payload=payload, subject='filing') - - -@pytest.mark.asyncio -@skip_in_pod -async def test_publish_transaction_nats_down(app, client_id, stan, future, stan_server): - """Assert that payment tokens can be retrieved and decoded from the Queue.""" - with app.app_context(): - # Call back for the subscription - nats_connect = patch('nats.aio.client.Client.connect') - - mock = nats_connect.start() - mock.side_effect = Exception() - - # publish message - from pay_api.services.queue_publisher import publish - - payload = {'paymentToken': {'id': 10, 'statusCode': 'COMPLETED'}} - with pytest.raises(Exception): - await publish(payload=payload, subject='filing') - mock.stop() diff --git a/pay-api/tests/unit/services/test_refund.py b/pay-api/tests/unit/services/test_refund.py index 8d7f49e17..d28e305e1 100644 --- a/pay-api/tests/unit/services/test_refund.py +++ b/pay-api/tests/unit/services/test_refund.py @@ -81,8 +81,6 @@ def test_create_refund_for_paid_invoice(session, monkeypatch, payment_method, in factory_receipt(invoice_id=i.id, receipt_number='1234569546456').save() - monkeypatch.setattr('pay_api.services.payment_transaction.publish_response', lambda *args, **kwargs: None) - message = RefundService.create_refund(invoice_id=i.id, request={'reason': 'Test'}) i = InvoiceModel.find_by_id(i.id) @@ -110,7 +108,6 @@ def test_create_duplicate_refund_for_paid_invoice(session, monkeypatch): i.save() factory_receipt(invoice_id=i.id, receipt_number='953959345343').save() - monkeypatch.setattr('pay_api.services.payment_transaction.publish_response', lambda *args, **kwargs: None) RefundService.create_refund(invoice_id=i.id, request={'reason': 'Test'}) i = InvoiceModel.find_by_id(i.id) diff --git a/pay-api/tests/unit/services/test_statement.py b/pay-api/tests/unit/services/test_statement.py index 4e33f8cdc..73f4acd7c 100644 --- a/pay-api/tests/unit/services/test_statement.py +++ b/pay-api/tests/unit/services/test_statement.py @@ -16,7 +16,7 @@ Test-Suite to ensure that the Statement Service is working as expected. """ -from datetime import datetime, timezone +from datetime import datetime import pytz from freezegun import freeze_time @@ -172,8 +172,6 @@ def test_get_weekly_interim_statement(session, admin_users_mock): total=50).save() assert weekly_invoice is not None - assert weekly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) - update_date = localize_date(datetime(2023, 10, 12, 12, 0)) with freeze_time(update_date): account = PaymentAccountService.update(account.auth_account_id, @@ -232,7 +230,6 @@ def test_get_monthly_interim_statement(session, admin_users_mock): total=50).save() assert monthly_invoice is not None - assert monthly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) update_date = localize_date(datetime(2023, 10, 12, 12, 0)) with freeze_time(update_date): diff --git a/pay-api/tests/unit/services/utils.py b/pay-api/tests/unit/services/utils.py deleted file mode 100644 index c0f13bde8..000000000 --- a/pay-api/tests/unit/services/utils.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Utilities used by the integration tests.""" -import os -from typing import Callable - -import stan - - -async def subscribe_to_queue(stan_client: stan.aio.client.Client, - call_back: Callable[[stan.aio.client.Msg], None]) \ - -> str: - """Subscribe to the Queue using the environment setup. - - Args: - stan_client: the stan connection - call_back: a callback function that accepts 1 parameter, a Msg - Returns: - str: the name of the queue - """ - entity_subject = os.getenv('NATS_SUBJECT') - entity_queue = os.getenv('NATS_QUEUE') - entity_durable_name = entity_queue + '_durable' - - await stan_client.subscribe(subject=entity_subject, - queue=entity_queue, - durable_name=entity_durable_name, - cb=call_back) - return entity_subject diff --git a/pay-api/tests/utilities/base_test.py b/pay-api/tests/utilities/base_test.py index faa174f84..d9cd157d6 100644 --- a/pay-api/tests/utilities/base_test.py +++ b/pay-api/tests/utilities/base_test.py @@ -17,7 +17,7 @@ Test-Suite to ensure that the /payments endpoint is working as expected. """ -from datetime import datetime +from datetime import datetime, timezone from decimal import Decimal from random import randrange from typing import Dict, List, Tuple @@ -387,7 +387,7 @@ def factory_payment_account(payment_system_code: str = 'PAYBC', payment_method_c name=name, branch_name=branch_name, payment_method=payment_method_code, - pad_activation_date=datetime.now(), + pad_activation_date=datetime.now(tz=timezone.utc), eft_enable=False ).save() @@ -463,7 +463,7 @@ def factory_routing_slip( status: str = RoutingSlipStatus.ACTIVE.value, total: int = 0, remaining_amount: Decimal = 0.0, - routing_slip_date=datetime.now() + routing_slip_date=datetime.now(tz=timezone.utc) ): """Return Factory.""" routing_slip: RoutingSlip = RoutingSlip( @@ -471,7 +471,7 @@ def factory_routing_slip( payment_account_id=payment_account_id, status=status, total=total, - remaining_amount=remaining_amount, + remaining_amount=Decimal(str(remaining_amount)), created_by='test', routing_slip_date=routing_slip_date ) @@ -484,7 +484,7 @@ def factory_routing_slip_usd( status: str = RoutingSlipStatus.ACTIVE.value, total: int = 0, remaining_amount: int = 0, - routing_slip_date=datetime.now(), + routing_slip_date=datetime.now(tz=timezone.utc), total_usd=0 ): """Return Factory.""" @@ -493,7 +493,7 @@ def factory_routing_slip_usd( payment_account_id=payment_account_id, status=status, total=total, - remaining_amount=remaining_amount, + remaining_amount=Decimal(str(remaining_amount)), created_by='test', routing_slip_date=routing_slip_date, total_usd=total_usd @@ -508,7 +508,7 @@ def factory_invoice(payment_account, status_code: str = InvoiceStatus.CREATED.va total=0, paid=None, payment_method_code: str = PaymentMethod.DIRECT_PAY.value, - created_on: datetime = datetime.now(), + created_on: datetime = datetime.now(tz=timezone.utc), routing_slip=None, folio_number=1234567890, created_name='test name', @@ -554,8 +554,8 @@ def factory_payment_transaction( status_code: str = 'CREATED', client_system_url: str = 'http://google.com/', pay_system_url: str = 'http://google.com', - transaction_start_time: datetime = datetime.now(), - transaction_end_time: datetime = datetime.now(), + transaction_start_time: datetime = datetime.now(tz=timezone.utc), + transaction_end_time: datetime = datetime.now(tz=timezone.utc), ): """Return Factory.""" return PaymentTransaction( @@ -578,7 +578,7 @@ def factory_invoice_reference(invoice_id: int, invoice_number: str = '10021'): def factory_receipt( invoice_id: int, receipt_number: str = 'TEST1234567890', - receipt_date: datetime = datetime.now(), + receipt_date: datetime = datetime.now(tz=timezone.utc), receipt_amount: float = 10.0 ): """Return Factory.""" @@ -593,7 +593,7 @@ def factory_receipt( def factory_statement_settings( frequency: str = 'WEEKLY', payment_account_id: str = None, - from_date: datetime = datetime.now(), + from_date: datetime = datetime.now(tz=timezone.utc), to_date: datetime = None): """Return Factory.""" return StatementSettings(frequency=frequency, @@ -605,10 +605,10 @@ def factory_statement_settings( def factory_statement( frequency: str = 'WEEKLY', payment_account_id: str = None, - from_date: datetime = datetime.now(), - to_date: datetime = datetime.now(), + from_date: datetime = datetime.now(tz=timezone.utc), + to_date: datetime = datetime.now(tz=timezone.utc), statement_settings_id: str = None, - created_on: datetime = datetime.now()): + created_on: datetime = datetime.now(tz=timezone.utc)): """Return Factory.""" return Statement(frequency=frequency, statement_settings_id=statement_settings_id, @@ -629,7 +629,7 @@ def factory_statement_invoices( def activate_pad_account(auth_account_id: str): """Activate the pad account.""" payment_account: PaymentAccount = PaymentAccount.find_by_auth_account_id(auth_account_id) - payment_account.pad_activation_date = datetime.now() + payment_account.pad_activation_date = datetime.now(tz=timezone.utc) payment_account.save() cfs_account: CfsAccount = CfsAccount.find_effective_by_account_id(payment_account.id) cfs_account.status = 'ACTIVE' @@ -860,7 +860,7 @@ def get_routing_slip_request( """Return a routing slip request dictionary.""" routing_slip_payload: Dict[str, any] = { 'number': number, - 'routingSlipDate': datetime.now().strftime(DT_SHORT_FORMAT), + 'routingSlipDate': datetime.now(tz=timezone.utc).strftime(DT_SHORT_FORMAT), 'paymentAccount': { 'accountName': 'TEST' }, @@ -869,7 +869,7 @@ def get_routing_slip_request( for cheque_detail in cheque_receipt_numbers: routing_slip_payload['payments'].append({ 'paymentMethod': cheque_detail[1], - 'paymentDate': datetime.now().strftime(DT_SHORT_FORMAT), + 'paymentDate': datetime.now(tz=timezone.utc).strftime(DT_SHORT_FORMAT), 'chequeReceiptNumber': cheque_detail[0], 'paidAmount': cheque_detail[2] }) diff --git a/queue_services/events-listener/Dockerfile b/queue_services/events-listener/Dockerfile index 56f55b55e..3747d9aa5 100644 --- a/queue_services/events-listener/Dockerfile +++ b/queue_services/events-listener/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye ARG VCS_REF="missing" ARG BUILD_DATE="missing" diff --git a/queue_services/payment-reconciliations/Dockerfile b/queue_services/payment-reconciliations/Dockerfile index e7e7f46d3..0b09413fa 100644 --- a/queue_services/payment-reconciliations/Dockerfile +++ b/queue_services/payment-reconciliations/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.5-buster +FROM python:3.12.2-bullseye ARG VCS_REF="missing" ARG BUILD_DATE="missing" diff --git a/report-api/Dockerfile b/report-api/Dockerfile index 757b62c72..ce53064b3 100644 --- a/report-api/Dockerfile +++ b/report-api/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8-bullseye +FROM python:3.12.2-bullseye #FROM python:.6-stretch #RUN echo "deb http://ftp.debian.org/debian stretch main contrib" > /etc/apt/sources.list From 43f1cc06d3d5c0aa5980777a73dd34fc2de26bf4 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 5 Mar 2024 14:19:35 -0800 Subject: [PATCH 02/87] up google-auth version --- pay-api/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-api/requirements.txt b/pay-api/requirements.txt index 08bc10d9c..fef42554e 100644 --- a/pay-api/requirements.txt +++ b/pay-api/requirements.txt @@ -28,7 +28,7 @@ ecdsa==0.18.0 expiringdict==1.2.2 flask-marshmallow==1.2.0 google-api-core==2.17.1 -google-auth==2.18.1 +google-auth==2.28.1 google-cloud-pubsub==2.17.0 googleapis-common-protos==1.62.0 greenlet==3.0.3 From eba1dc28a5896cf641ebe459954675d79b33dc9b Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 5 Mar 2024 16:12:40 -0800 Subject: [PATCH 03/87] Update prod.txt --- pay-api/requirements/prod.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-api/requirements/prod.txt b/pay-api/requirements/prod.txt index d84882ac4..6bcf759ab 100644 --- a/pay-api/requirements/prod.txt +++ b/pay-api/requirements/prod.txt @@ -26,5 +26,5 @@ itsdangerous Jinja2 launchdarkly-server-sdk holidays==0.37 -google-auth==2.18.1 +google-auth==2.28.1 google-cloud-pubsub==2.17.0 From e853e7d7183ca4d36d18d39725ddd377e0e56c24 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 5 Mar 2024 16:15:20 -0800 Subject: [PATCH 04/87] Update requirements.txt --- pay-api/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-api/requirements.txt b/pay-api/requirements.txt index fef42554e..62323aa6c 100644 --- a/pay-api/requirements.txt +++ b/pay-api/requirements.txt @@ -29,7 +29,7 @@ expiringdict==1.2.2 flask-marshmallow==1.2.0 google-api-core==2.17.1 google-auth==2.28.1 -google-cloud-pubsub==2.17.0 +google-cloud-pubsub==2.20.0 googleapis-common-protos==1.62.0 greenlet==3.0.3 grpc-google-iam-v1==0.13.0 From b33d8e36435060a30f3cdd14c3d05a3a949d3b5a Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 5 Mar 2024 16:15:34 -0800 Subject: [PATCH 05/87] Update prod.txt --- pay-api/requirements/prod.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-api/requirements/prod.txt b/pay-api/requirements/prod.txt index 6bcf759ab..3e5534840 100644 --- a/pay-api/requirements/prod.txt +++ b/pay-api/requirements/prod.txt @@ -27,4 +27,4 @@ Jinja2 launchdarkly-server-sdk holidays==0.37 google-auth==2.28.1 -google-cloud-pubsub==2.17.0 +google-cloud-pubsub==2.20.0 From 4a3dbc9dab45438d8e86d1195206b9099f638a76 Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Wed, 6 Mar 2024 12:43:34 -0800 Subject: [PATCH 06/87] fix migration bug (#1440) --- pay-api/manage.py | 7 +------ pay-api/wsgi.py | 8 +++++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/pay-api/manage.py b/pay-api/manage.py index fd1bb6f96..8e1952a5c 100755 --- a/pay-api/manage.py +++ b/pay-api/manage.py @@ -16,8 +16,7 @@ """ import logging -from flask_migrate import Migrate, MigrateCommand -from flask_script import Manager # class for handling a set of commands +from flask_migrate import Migrate # models included so that migrate can build the database migrations from pay_api import models # pylint: disable=unused-import @@ -27,10 +26,6 @@ APP = create_app(run_mode='migration') MIGRATE = Migrate(APP, db) -MANAGER = Manager(APP) - -MANAGER.add_command('db', MigrateCommand) if __name__ == '__main__': logging.log(logging.INFO, 'Running the Manager') - MANAGER.run() diff --git a/pay-api/wsgi.py b/pay-api/wsgi.py index a05f2f6b3..55ac54a0e 100755 --- a/pay-api/wsgi.py +++ b/pay-api/wsgi.py @@ -13,11 +13,13 @@ # limitations under the License. """Provides the WSGI entry point for running the application """ -from pay_api import create_app +from pay_api import create_app, db +from flask_migrate import Migrate # Openshift s2i expects a lower case name of application -application = create_app() # pylint: disable=invalid-name +app = create_app() # pylint: disable=invalid-name +migrate = Migrate(app, db) if __name__ == "__main__": - application.run() + app.run() From 46b03ddad370b14cfb3899bc5e5a6a3973dd0ea0 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Wed, 6 Mar 2024 14:23:07 -0800 Subject: [PATCH 07/87] Changes to upgrade to Python 3.12 --- .github/workflows/bcol-api-ci.yml | 1 + bcol-api/Makefile | 17 ++++-- bcol-api/requirements.txt | 71 ++++++++++++------------ bcol-api/requirements/dev.txt | 2 +- bcol-api/requirements/prod.txt | 8 +-- bcol-api/requirements/repo-libraries.txt | 1 + bcol-api/tests/docker/docker-compose.yml | 2 +- 7 files changed, 57 insertions(+), 45 deletions(-) diff --git a/.github/workflows/bcol-api-ci.yml b/.github/workflows/bcol-api-ci.yml index 19a136f4e..24bbc9143 100644 --- a/.github/workflows/bcol-api-ci.yml +++ b/.github/workflows/bcol-api-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - queue_python_upgrade paths: - "bcol-api/**" diff --git a/bcol-api/Makefile b/bcol-api/Makefile index 66d72527e..6d77e1353 100755 --- a/bcol-api/Makefile +++ b/bcol-api/Makefile @@ -36,19 +36,26 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -install: clean ## Install python virtrual environment +build-req: clean ## Upgrade requirements test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ . venv/bin/activate ;\ pip install --upgrade pip ;\ pip install -Ur requirements/prod.txt ;\ pip freeze | sort > requirements.txt ;\ cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt ;\ - pip install -Ur requirements/dev.txt ;\ - touch venv/bin/activate # update so it's as new as requirements/prod.txt + pip install -Ur requirements/repo-libraries.txt + +install: clean ## Install python virtrual environment + test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ + . venv/bin/activate ;\ + pip install --upgrade pip ;\ + pip install -Ur requirements.txt install-dev: ## Install local application - . venv/bin/activate && pip install -e . + . venv/bin/activate ; \ + pip install -Ur requirements/dev.txt; \ + pip install -e . + ################################################################################# # COMMANDS - CI # diff --git a/bcol-api/requirements.txt b/bcol-api/requirements.txt index 264caf270..1105bc669 100644 --- a/bcol-api/requirements.txt +++ b/bcol-api/requirements.txt @@ -1,51 +1,54 @@ Flask-Moment==1.0.5 Flask-Script==2.0.6 -Flask==1.1.2 -Jinja2==3.0.3 -MarkupSafe==2.1.1 -Werkzeug==1.0.1 +Flask==3.0.2 +Jinja2==3.1.3 +MarkupSafe==2.1.5 +Werkzeug==3.0.1 aniso8601==9.0.1 -attrs==22.1.0 -blinker==1.5 -cachelib==0.9.0 -certifi==2023.7.22 -charset-normalizer==2.1.1 -click==8.1.3 +attrs==23.2.0 +blinker==1.7.0 +cachelib==0.12.0 +certifi==2024.2.2 +charset-normalizer==3.3.2 +click==8.1.7 ecdsa==0.18.0 flask-jwt-oidc==0.3.0 -flask-restx==1.0.3 -gunicorn==20.1.0 -idna==3.4 -importlib-resources==5.10.1 +flask-restx==1.3.0 +gunicorn==21.2.0 +idna==3.6 +importlib-metadata==7.0.1 +importlib-resources==5.13.0 isodate==0.6.1 -itsdangerous==2.0.1 +itsdangerous==2.1.2 jaeger-client==4.8.0 -jsonschema==4.17.3 -lxml==4.9.1 +jsonschema-specifications==2023.12.1 +jsonschema==4.21.1 +lxml==5.1.0 opentracing==2.4.0 -packaging==21.3 +packaging==23.2 pkgutil_resolve_name==1.3.10 -platformdirs==2.5.4 -psycopg2-binary==2.9.5 -pyasn1-modules==0.2.8 -pyasn1==0.4.8 -pycountry==22.3.5 -pyparsing==3.0.9 -pyrsistent==0.19.2 -python-dotenv==0.21.0 +platformdirs==4.2.0 +psycopg2-binary==2.9.9 +pyasn1-modules==0.3.0 +pyasn1==0.5.1 +pycountry==23.12.11 +python-dotenv==1.0.1 python-jose==3.3.0 -python-ldap==3.4.3 -pytz==2022.6 -requests-file==1.5.1 -requests-toolbelt==0.10.1 +python-ldap==3.4.4 +pytz==2024.1 +referencing==0.33.0 +requests-file==2.0.0 +requests-toolbelt==1.0.0 requests==2.31.0 +rpds-py==0.18.0 rsa==4.9 -sentry-sdk==1.14.0 +sentry-sdk==1.40.6 six==1.16.0 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.3 -urllib3==1.26.17 +tornado==6.4 +urllib3==2.2.1 zeep==4.2.1 -zipp==3.11.0 +zipp==3.17.0 -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python +git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/bcol-api/requirements/dev.txt b/bcol-api/requirements/dev.txt index 098dd2013..9016fe016 100755 --- a/bcol-api/requirements/dev.txt +++ b/bcol-api/requirements/dev.txt @@ -9,7 +9,7 @@ pyhamcrest pytest-cov # Lint and code style -flake8==5.0.4 +flake8 flake8-blind-except flake8-debugger flake8-docstrings diff --git a/bcol-api/requirements/prod.txt b/bcol-api/requirements/prod.txt index 1f92220e0..a0544367d 100755 --- a/bcol-api/requirements/prod.txt +++ b/bcol-api/requirements/prod.txt @@ -6,14 +6,14 @@ flask-restx flask-jwt-oidc python-dotenv psycopg2-binary -jsonschema==4.17.3 +jsonschema requests zeep python-ldap sentry-sdk[flask] attrs -Werkzeug<2 +Werkzeug jaeger-client pycountry -itsdangerous==2.0.1 -Jinja2==3.0.3 +itsdangerous +Jinja2 diff --git a/bcol-api/requirements/repo-libraries.txt b/bcol-api/requirements/repo-libraries.txt index 3b0252bf2..36fdc23c0 100644 --- a/bcol-api/requirements/repo-libraries.txt +++ b/bcol-api/requirements/repo-libraries.txt @@ -1 +1,2 @@ -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python +git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/bcol-api/tests/docker/docker-compose.yml b/bcol-api/tests/docker/docker-compose.yml index 053667323..770a2904c 100644 --- a/bcol-api/tests/docker/docker-compose.yml +++ b/bcol-api/tests/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: "3" services: keycloak: - image: quay.io/keycloak/keycloak:12.0.2 + image: quay.io/keycloak/keycloak:15.0.0 ports: - "8081:8081" environment: From 2f82107be3905275e8fadd8b61e6ad9deb89a2ba Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Wed, 6 Mar 2024 14:32:42 -0800 Subject: [PATCH 08/87] lint fix --- bcol-api/requirements.txt | 6 ++---- bcol-api/requirements/prod.txt | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/bcol-api/requirements.txt b/bcol-api/requirements.txt index 1105bc669..18e12c0c5 100644 --- a/bcol-api/requirements.txt +++ b/bcol-api/requirements.txt @@ -21,8 +21,7 @@ importlib-resources==5.13.0 isodate==0.6.1 itsdangerous==2.1.2 jaeger-client==4.8.0 -jsonschema-specifications==2023.12.1 -jsonschema==4.21.1 +jsonschema==4.17.3 lxml==5.1.0 opentracing==2.4.0 packaging==23.2 @@ -32,15 +31,14 @@ psycopg2-binary==2.9.9 pyasn1-modules==0.3.0 pyasn1==0.5.1 pycountry==23.12.11 +pyrsistent==0.20.0 python-dotenv==1.0.1 python-jose==3.3.0 python-ldap==3.4.4 pytz==2024.1 -referencing==0.33.0 requests-file==2.0.0 requests-toolbelt==1.0.0 requests==2.31.0 -rpds-py==0.18.0 rsa==4.9 sentry-sdk==1.40.6 six==1.16.0 diff --git a/bcol-api/requirements/prod.txt b/bcol-api/requirements/prod.txt index a0544367d..4a8fe04dc 100755 --- a/bcol-api/requirements/prod.txt +++ b/bcol-api/requirements/prod.txt @@ -6,7 +6,7 @@ flask-restx flask-jwt-oidc python-dotenv psycopg2-binary -jsonschema +jsonschema==4.17.3 requests zeep python-ldap From ca8f334d5e7da2290dedc0821e2e38370a3bb2b1 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Wed, 6 Mar 2024 15:09:03 -0800 Subject: [PATCH 09/87] Small unit test fixes --- bcol-api/tests/unit/services/test_bcol_payment.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bcol-api/tests/unit/services/test_bcol_payment.py b/bcol-api/tests/unit/services/test_bcol_payment.py index ed3648807..07ec69e06 100644 --- a/bcol-api/tests/unit/services/test_bcol_payment.py +++ b/bcol-api/tests/unit/services/test_bcol_payment.py @@ -36,10 +36,5 @@ def test_payment(app, payment_mock): has_non_match_error = True assert has_non_match_error # Success case. - with test_case.assertLogs(level='ERROR') as log: + with test_case.assertNoLogs(level='ERROR'): BcolPayment().create_payment({'serviceFees': 1.50}, False) - has_non_match_error = False - for message in log.output: - if "from BCOL doesn\'t match" in message: - has_non_match_error = True - assert has_non_match_error is False From a6611358c4b7ec7edc0ccaa9191c3602371da122 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Wed, 6 Mar 2024 16:24:46 -0800 Subject: [PATCH 10/87] Fix pay-admin --- .github/workflows/pay-admin-ci.yml | 7 ++- pay-admin/Makefile | 17 +++++-- pay-admin/admin/config.py | 1 - pay-admin/requirements.txt | 58 ++++++++++++----------- pay-admin/requirements/dev.txt | 2 +- pay-admin/requirements/prod.txt | 8 ++-- pay-admin/requirements/repo-libraries.txt | 3 +- pay-admin/secrets/keycloak.json | 9 ++-- pay-admin/tests/conftest.py | 29 +++++------- pay-api/requirements/repo-libraries.txt | 1 + 10 files changed, 72 insertions(+), 63 deletions(-) diff --git a/.github/workflows/pay-admin-ci.yml b/.github/workflows/pay-admin-ci.yml index ec28da2ad..0a6b544f2 100644 --- a/.github/workflows/pay-admin-ci.yml +++ b/.github/workflows/pay-admin-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - queue_python_upgrade paths: - "pay-admin/**" - "pay-api/src/pay_api/models/**" @@ -53,7 +54,8 @@ jobs: needs: setup-job env: FLASK_ENV: "testing" - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + # Needs different database than POSTGRES otherwise dropping database doesn't work + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" runs-on: ubuntu-20.04 @@ -68,7 +70,8 @@ jobs: env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + # Needs different database than POSTGRES otherwise dropping database doesn't work + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck diff --git a/pay-admin/Makefile b/pay-admin/Makefile index b6ed852ea..237377444 100755 --- a/pay-admin/Makefile +++ b/pay-admin/Makefile @@ -36,19 +36,26 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -install: clean ## Install python virtrual environment +build-req: clean ## Upgrade requirements test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ . venv/bin/activate ;\ pip install --upgrade pip ;\ pip install -Ur requirements/prod.txt ;\ pip freeze | sort > requirements.txt ;\ cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt ;\ - pip install -Ur requirements/dev.txt ;\ - touch venv/bin/activate # update so it's as new as requirements/prod.txt + pip install -Ur requirements/repo-libraries.txt + +install: clean ## Install python virtrual environment + test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ + . venv/bin/activate ;\ + pip install --upgrade pip ;\ + pip install -Ur requirements.txt install-dev: ## Install local application - . venv/bin/activate && pip install -e . + . venv/bin/activate ; \ + pip install -Ur requirements/dev.txt; \ + pip install -e . + ################################################################################# # COMMANDS - CI # diff --git a/pay-admin/admin/config.py b/pay-admin/admin/config.py index 5caa3687a..707552ecf 100755 --- a/pay-admin/admin/config.py +++ b/pay-admin/admin/config.py @@ -83,7 +83,6 @@ class _Config(): # pylint: disable=too-few-public-methods # Normal Keycloak parameters. OIDC_CLIENT_SECRETS = os.getenv('PAY_OIDC_CLIENT_SECRETS', 'secrets/keycloak.json') OIDC_SCOPES = ['openid', 'email', 'profile'] - OIDC_VALID_ISSUERS = [os.getenv('PAY_OIDC_VALID_ISSUERS')] # Undocumented Keycloak parameter: allows sending cookies without the secure flag, which we need for the local # non-TLS HTTP server. Set this to non-"True" for local development, and use the default everywhere else. OIDC_ID_TOKEN_COOKIE_SECURE = os.getenv('PAY_OIDC_ID_TOKEN_COOKIE_SECURE', 'True').lower() == 'true' diff --git a/pay-admin/requirements.txt b/pay-admin/requirements.txt index 2a887eadd..b3c14e933 100644 --- a/pay-admin/requirements.txt +++ b/pay-admin/requirements.txt @@ -1,30 +1,34 @@ -Flask-Admin==1.6.0 -Flask-SQLAlchemy==2.5.1 -Flask==1.1.2 -Jinja2==3.0.3 -MarkupSafe==2.1.1 -SQLAlchemy==1.3.24 -WTForms==3.0.1 -Werkzeug==1.0.1 -certifi==2023.7.22 -charset-normalizer==2.0.12 -click==8.1.3 -flask-oidc==1.4.0 -gunicorn==20.1.0 -httplib2==0.20.4 -idna==3.3 -itsdangerous==2.0.1 -oauth2client==4.1.3 -psycopg2-binary==2.9.3 -pyasn1-modules==0.2.8 -pyasn1==0.4.8 -pyparsing==3.0.8 -python-dotenv==0.20.0 -requests-futures==1.0.0 +Authlib==1.3.0 +Flask-Admin==1.6.1 +Flask-SQLAlchemy==3.1.1 +Flask==3.0.2 +Jinja2==3.1.3 +MarkupSafe==2.1.5 +SQLAlchemy==2.0.28 +WTForms==3.1.2 +Werkzeug==3.0.1 +blinker==1.7.0 +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +cryptography==42.0.5 +flask-oidc==2.1.1 +greenlet==3.0.3 +gunicorn==21.2.0 +httplib2==0.22.0 +idna==3.6 +itsdangerous==2.1.2 +packaging==23.2 +psycopg2-binary==2.9.9 +pycparser==2.21 +pyparsing==3.1.2 +python-dotenv==1.0.1 +requests-futures==1.0.1 requests==2.31.0 -rsa==4.8 -six==1.16.0 -urllib3==1.26.17 --e git+https://github.com/bcgov/sbc-pay.git@main#egg=pay-api&subdirectory=pay-api +typing_extensions==4.10.0 +urllib3==2.2.1 +-e git+https://github.com/bcgov/sbc-pay.git@queue_python_upgrade#egg=pay-api&subdirectory=pay-api -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python git+https://github.com/daxiom/simple-cloudevent.py.git +git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-admin/requirements/dev.txt b/pay-admin/requirements/dev.txt index 51d7a0057..b40ddf1cb 100644 --- a/pay-admin/requirements/dev.txt +++ b/pay-admin/requirements/dev.txt @@ -9,7 +9,7 @@ pytest-cov FreezeGun # Lint and code style -flake8==5.0.4 +flake8 flake8-blind-except flake8-debugger flake8-docstrings diff --git a/pay-admin/requirements/prod.txt b/pay-admin/requirements/prod.txt index 58a9a14bd..20354853f 100644 --- a/pay-admin/requirements/prod.txt +++ b/pay-admin/requirements/prod.txt @@ -9,7 +9,7 @@ python-dotenv requests requests-futures wtforms -Werkzeug<2 -sqlalchemy<1.4 -itsdangerous==2.0.1 -Jinja2==3.0.3 \ No newline at end of file +Werkzeug +sqlalchemy +itsdangerous +Jinja2 diff --git a/pay-admin/requirements/repo-libraries.txt b/pay-admin/requirements/repo-libraries.txt index 146c61f8c..8aaeaf504 100644 --- a/pay-admin/requirements/repo-libraries.txt +++ b/pay-admin/requirements/repo-libraries.txt @@ -1,3 +1,4 @@ --e git+https://github.com/bcgov/sbc-pay.git@main#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/bcgov/sbc-pay.git@queue_python_upgrade#egg=pay-api&subdirectory=pay-api -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python git+https://github.com/daxiom/simple-cloudevent.py.git +git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-admin/secrets/keycloak.json b/pay-admin/secrets/keycloak.json index f1a775134..51a30edd9 100644 --- a/pay-admin/secrets/keycloak.json +++ b/pay-admin/secrets/keycloak.json @@ -1,10 +1,11 @@ { "web": { - "auth_uri": "https://dev.oidc.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/auth", + "auth_uri": "https://dev.loginproxy.gov.bc.ca/auth/admin/bcregistry/protocol/openid-connect/auth", "client_id": "pay-admin", "client_secret": "****", - "userinfo_uri": "https://dev.oidc.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/userinfo", - "token_uri": "https://dev.oidc.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/token", - "token_introspection_uri": "https://dev.oidc.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/token/introspect" + "issuer": "https://dev.loginproxy.gov.bc.ca/auth/realms/bcregistry", + "userinfo_uri": "https://dev.loginproxy.gov.bc.ca/auth/admin/bcregistry/protocol/openid-connect/userinfo", + "token_uri": "https://dev.loginproxy.gov.bc.ca/auth/admin/bcregistry/protocol/openid-connect/token", + "token_introspection_uri": "https://dev.loginproxy.gov.bc.ca/auth/admin/bcregistry/protocol/openid-connect/token/introspect" } } diff --git a/pay-admin/tests/conftest.py b/pay-admin/tests/conftest.py index beda94298..50235bdaa 100644 --- a/pay-admin/tests/conftest.py +++ b/pay-admin/tests/conftest.py @@ -4,26 +4,19 @@ """ import pytest +from flask_sqlalchemy import SQLAlchemy -@pytest.fixture(scope='function') -def clean_db(): - """Clean DB.""" - from flask_sqlalchemy import SQLAlchemy - - from admin import create_app - from admin.keycloak import Keycloak - from tests.fake_oidc import FakeOidc - - Keycloak._oidc = FakeOidc() - app = create_app(run_mode='testing') - - db = SQLAlchemy(app) - - return db +from admin import create_app +from admin.keycloak import Keycloak +from tests.fake_oidc import FakeOidc @pytest.fixture(scope='function') -def db(clean_db): +def db(): """DB.""" - yield clean_db - clean_db.session.close() + Keycloak._oidc = FakeOidc() # pylint-disable=protected-access + app = create_app(run_mode='testing') + with app.app_context(): + _db = SQLAlchemy() + _db.app = app + return _db diff --git a/pay-api/requirements/repo-libraries.txt b/pay-api/requirements/repo-libraries.txt index 310ca9e9d..c1903a023 100644 --- a/pay-api/requirements/repo-libraries.txt +++ b/pay-api/requirements/repo-libraries.txt @@ -1,2 +1,3 @@ -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python git+https://github.com/daxiom/simple-cloudevent.py.git +git+https://github.com/thorwolpert/flask-jwt-oidc.git From c339b3ba6a50304c0edd2002185a9109f10c7ae0 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Wed, 6 Mar 2024 16:37:59 -0800 Subject: [PATCH 11/87] Update ftp-poller's requirements --- .github/workflows/ftp-poller-ci.yml | 5 +- jobs/ftp-poller/requirements.txt | 61 ++++++++++--------- jobs/ftp-poller/requirements/prod.txt | 8 +-- .../requirements/repo-libraries.txt | 3 +- 4 files changed, 42 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ftp-poller-ci.yml b/.github/workflows/ftp-poller-ci.yml index 35e0be77b..14a9c3c09 100644 --- a/.github/workflows/ftp-poller-ci.yml +++ b/.github/workflows/ftp-poller-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - queue_python_upgrade paths: - "jobs/ftp-poller/**" @@ -51,7 +52,7 @@ jobs: testing: needs: setup-job env: - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" NATS_QUEUE: "account-worker" NATS_CLUSTER_ID: "test-cluster" NATS_CLIENT_NAME: "account.events.worker" @@ -73,7 +74,7 @@ jobs: env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck diff --git a/jobs/ftp-poller/requirements.txt b/jobs/ftp-poller/requirements.txt index 76a6a5573..f2e779e08 100644 --- a/jobs/ftp-poller/requirements.txt +++ b/jobs/ftp-poller/requirements.txt @@ -1,45 +1,50 @@ -Flask==1.1.2 -Jinja2==3.0.3 -MarkupSafe==2.1.3 +Flask==3.0.2 +Jinja2==3.1.3 +MarkupSafe==2.1.5 PyNaCl==1.5.0 -Werkzeug==1.0.1 +Werkzeug==3.0.1 aniso8601==9.0.1 -attrs==23.1.0 -bcrypt==4.0.1 -certifi==2023.7.22 -cffi==1.15.1 -charset-normalizer==3.1.0 -click==8.1.3 -cryptography==42.0.2 +argon2-cffi-bindings==21.2.0 +argon2-cffi==23.1.0 +attrs==23.2.0 +bcrypt==4.1.2 +blinker==1.7.0 +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +cryptography==42.0.5 expiringdict==1.2.2 flask-restplus==0.13.0 -gunicorn==20.1.0 -idna==3.4 -importlib-resources==5.12.0 -itsdangerous==2.0.1 +gunicorn==21.2.0 +idna==3.6 +itsdangerous==2.1.2 jaeger-client==4.8.0 jsonschema==4.17.3 -launchdarkly-server-sdk==8.1.4 -minio==7.1.15 +launchdarkly-eventsource==1.1.1 +launchdarkly-server-sdk==9.2.1 +minio==7.2.5 opentracing==2.4.0 +packaging==23.2 paramiko==3.4.0 -pkgutil_resolve_name==1.3.10 -protobuf==3.19.6 -psycopg2-binary==2.9.6 +protobuf==4.25.3 +psycopg2-binary==2.9.9 pyRFC3339==1.1 pycparser==2.21 -pyrsistent==0.19.3 +pycryptodome==3.20.0 +pyrsistent==0.20.0 pysftp==0.2.9 -python-dotenv==1.0.0 -pytz==2023.3 +python-dotenv==1.0.1 +pytz==2024.1 requests==2.31.0 -semver==2.13.0 +semver==3.0.2 six==1.16.0 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.3 -urllib3==1.26.17 -zipp==3.15.0 +tornado==6.4 +typing_extensions==4.10.0 +urllib3==2.2.1 -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/bcgov/sbc-pay.git@queue_python_upgrade#egg=pay-api&subdirectory=pay-api git+https://github.com/daxiom/simple-cloudevent.py.git +git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/jobs/ftp-poller/requirements/prod.txt b/jobs/ftp-poller/requirements/prod.txt index d6b1fb8a7..e9bfc70fc 100644 --- a/jobs/ftp-poller/requirements/prod.txt +++ b/jobs/ftp-poller/requirements/prod.txt @@ -5,12 +5,12 @@ python-dotenv psycopg2-binary jsonschema==4.17.3 requests -Werkzeug<2 +Werkzeug pysftp minio jaeger-client -itsdangerous==2.0.1 -Jinja2==3.0.3 -protobuf~=3.19.5 +itsdangerous +Jinja2 +protobuf launchdarkly-server-sdk diff --git a/jobs/ftp-poller/requirements/repo-libraries.txt b/jobs/ftp-poller/requirements/repo-libraries.txt index 9980a73e5..615ab1796 100644 --- a/jobs/ftp-poller/requirements/repo-libraries.txt +++ b/jobs/ftp-poller/requirements/repo-libraries.txt @@ -1,3 +1,4 @@ -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/bcgov/sbc-pay.git@queue_python_upgrade#egg=pay-api&subdirectory=pay-api git+https://github.com/daxiom/simple-cloudevent.py.git +git+https://github.com/thorwolpert/flask-jwt-oidc.git From 3ca21a459e79994fde14242b5a0a4dd4d31ab4da Mon Sep 17 00:00:00 2001 From: Rodrigo Barraza Date: Thu, 7 Mar 2024 07:47:40 -0800 Subject: [PATCH 12/87] 19939 - EFT - Invoice Reference (#1436) * Squashed commit of the following: commit e1c58f92eb12e9a6c4644f89279c1f877ca83a35 Author: Rodrigo Barraza Date: Tue Mar 5 08:42:01 2024 -0800 Cleanup auto_save commit 9b8a9a05a4ca4baa5716485885f56abcb988f48e Author: Rodrigo Barraza Date: Tue Mar 5 08:33:27 2024 -0800 Remove committing commit a05e210e2af0a72bd397721ce07c2bbabbd1f0c2 Author: Rodrigo Barraza Date: Tue Mar 5 08:30:07 2024 -0800 Reference and Receipt creation commit edb6e51edf1ff24b4e1e263484a474cfef440aff Author: Rodrigo Barraza Date: Tue Mar 5 08:18:47 2024 -0800 Updates to EFT Service Handling invoice reference creation * Reverting reconciliation changes * PR feedback updates --- pay-api/src/pay_api/services/eft_service.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pay-api/src/pay_api/services/eft_service.py b/pay-api/src/pay_api/services/eft_service.py index 88d4401ad..f9eb7d31e 100644 --- a/pay-api/src/pay_api/services/eft_service.py +++ b/pay-api/src/pay_api/services/eft_service.py @@ -21,7 +21,7 @@ from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus +from pay_api.utils.enums import InvoiceReferenceStatus, PaymentMethod, PaymentStatus from .deposit_service import DepositService from .invoice import Invoice @@ -40,7 +40,12 @@ def get_payment_method_code(self): def create_invoice(self, payment_account: PaymentAccount, line_items: [PaymentLineItem], invoice: Invoice, **kwargs) -> InvoiceReference: """Return a static invoice number for direct pay.""" - # Do nothing here as the invoice references will be created later for eft payment reconciliations (TDI17). + payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) + invoice_reference = self.create_invoice_reference(invoice=invoice, payment=payment) + + invoice_reference.save() + + return invoice_reference def apply_credit(self, invoice: Invoice, @@ -59,15 +64,13 @@ def apply_credit(self, payment_date=payment_date, paid_amount=invoice_balance - new_invoice_balance) - invoice_ref = self.create_invoice_reference(invoice=invoice_model, payment=payment) receipt = self.create_receipt(invoice=invoice_model, payment=payment) if auto_save: payment.save() - invoice_ref.save() receipt.save() - return payment, invoice_ref, receipt + return payment, receipt def complete_post_invoice(self, invoice: Invoice, invoice_reference: InvoiceReference) -> None: """Complete any post invoice activities if needed.""" @@ -97,9 +100,7 @@ def create_invoice_reference(invoice: InvoiceModel, payment: PaymentModel) -> In invoice_reference.invoice_id = invoice.id invoice_reference.invoice_number = payment.invoice_number - invoice_reference.status_code = InvoiceReferenceStatus.COMPLETED.value \ - if invoice.invoice_status_code == InvoiceStatus.PAID.value \ - else InvoiceReferenceStatus.ACTIVE.value + invoice_reference.status_code = InvoiceReferenceStatus.ACTIVE.value return invoice_reference From e0bc6979b761347b32628a28fa1489e919e5d806 Mon Sep 17 00:00:00 2001 From: Rodrigo Barraza Date: Thu, 7 Mar 2024 07:48:01 -0800 Subject: [PATCH 13/87] 19939 - EFT Reconciliation - Invoice Reference (#1437) * Updates to EFT Service Handling invoice reference creation * Reference and Receipt creation * Remove committing * Cleanup auto_save * Revert EFT service * PR feedback updates Bringing back auto_save --- .../src/reconciliations/eft/eft_reconciliation.py | 2 +- .../tests/integration/test_eft_reconciliation.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_reconciliation.py b/queue_services/payment-reconciliations/src/reconciliations/eft/eft_reconciliation.py index c17cb06ac..d22c316bc 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_reconciliation.py +++ b/queue_services/payment-reconciliations/src/reconciliations/eft/eft_reconciliation.py @@ -449,7 +449,7 @@ def _pay_invoice(invoice: InvoiceModel, shortname_balance: Dict): payment, invoice_reference, receipt = eft_payment_service.apply_credit(invoice=invoice, payment_date=payment_date, - auto_save=False) + auto_save=True) db.session.add(payment) db.session.add(invoice_reference) diff --git a/queue_services/payment-reconciliations/tests/integration/test_eft_reconciliation.py b/queue_services/payment-reconciliations/tests/integration/test_eft_reconciliation.py index 5f02f7b9f..5ae62ac3e 100644 --- a/queue_services/payment-reconciliations/tests/integration/test_eft_reconciliation.py +++ b/queue_services/payment-reconciliations/tests/integration/test_eft_reconciliation.py @@ -430,13 +430,13 @@ async def test_eft_tdi17_process(session, app, stan_server, event_loop, client_i assert payment.paid_amount == expected_amount invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel\ - .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.COMPLETED.value) + .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) assert invoice_reference is not None assert invoice_reference.invoice_id == invoice.id assert invoice_reference.invoice_number == payment.invoice_number assert invoice_reference.invoice_number == expected_invoice_number - assert invoice_reference.status_code == InvoiceReferenceStatus.COMPLETED.value + assert invoice_reference.status_code == InvoiceReferenceStatus.ACTIVE.value eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel).order_by(EFTCreditModel.created_on.asc()).all() assert eft_credits is not None @@ -597,13 +597,13 @@ async def test_eft_tdi17_rerun(session, app, stan_server, event_loop, client_id, assert payment.invoice_amount == expected_amount invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel \ - .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.COMPLETED.value) + .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) assert invoice_reference is not None assert invoice_reference.invoice_id == invoice.id assert invoice_reference.invoice_number == payment.invoice_number assert invoice_reference.invoice_number == expected_invoice_number - assert invoice_reference.status_code == InvoiceReferenceStatus.COMPLETED.value + assert invoice_reference.status_code == InvoiceReferenceStatus.ACTIVE.value eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel).order_by(EFTCreditModel.created_on.asc()).all() assert eft_credits is not None From 6390faaf60902ba862ce2875211eceaa0f491dc8 Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Fri, 8 Mar 2024 13:29:43 -0800 Subject: [PATCH 14/87] 19724 - EFT - Create CFS account job modification (#1441) * update api and payment-jobs for EFT cfs_account create * add updated requirements.txt from PR#18263 * isort fix --- .github/workflows/payment-jobs-ci.yml | 4 - jobs/payment-jobs/requirements.txt | 133 +++++++++--------- .../tasks/cfs_create_account_task.py | 8 +- .../jobs/test_cfs_create_account_task.py | 13 +- pay-api/src/pay_api/services/eft_service.py | 13 +- .../src/pay_api/services/payment_account.py | 1 + pay-api/src/pay_api/utils/constants.py | 2 + 7 files changed, 98 insertions(+), 76 deletions(-) diff --git a/.github/workflows/payment-jobs-ci.yml b/.github/workflows/payment-jobs-ci.yml index 048cb1b74..213027b6e 100644 --- a/.github/workflows/payment-jobs-ci.yml +++ b/.github/workflows/payment-jobs-ci.yml @@ -54,10 +54,6 @@ jobs: needs: setup-job env: DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" - NATS_QUEUE: "account-worker" - NATS_CLUSTER_ID: "test-cluster" - NATS_CLIENT_NAME: "account.events.worker" - NATS_SUBJECT: "account.events" USE_DOCKER_MOCK: "YES" JWT_OIDC_ISSUER: "http://localhost:8081/auth/realms/demo" SBC_AUTH_ADMIN_CLIENT_ID: "sbc-auth-admin" diff --git a/jobs/payment-jobs/requirements.txt b/jobs/payment-jobs/requirements.txt index 5250d3df7..c36f3d540 100644 --- a/jobs/payment-jobs/requirements.txt +++ b/jobs/payment-jobs/requirements.txt @@ -1,96 +1,93 @@ --e git+https://github.com/bcgov/sbc-common-components.git@1aa7dc8ed3897ad4bf679c789ac6c66c88550bfe#egg=sbc_common_components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git@a598989a6de74206ab26024d037d0128919e540e#egg=pay_api&subdirectory=pay-api -Flask-Caching==2.0.2 +-e git+https://github.com/bcgov/sbc-common-components.git@5807c201b057875f9061f60b408ab765ebaa4235#egg=sbc_common_components&subdirectory=python +-e git+https://github.com/bcgov/sbc-pay.git@e0bc6979b761347b32628a28fa1489e919e5d806#egg=pay_api&subdirectory=pay-api +-e git+https://github.com/thorwolpert/flask-jwt-oidc.git@40cc811ccf70e838c5f7522fe8d83b7e58853539#egg=flask_jwt_oidc +Flask-Caching==2.1.0 +Flask-Cors==4.0.0 Flask-Migrate==2.7.0 Flask-Moment==1.0.5 -Flask-OpenTracing==1.1.0 -Flask-SQLAlchemy==2.5.1 +Flask-OpenTracing==2.0.0 +Flask-SQLAlchemy==3.1.1 Flask-Script==2.0.6 -Flask==1.1.2 -Jinja2==3.0.3 -Mako==1.2.4 -MarkupSafe==2.1.3 -PyMeeus==0.5.12 +Flask==3.0.2 +Jinja2==3.1.3 +Mako==1.3.2 +MarkupSafe==2.1.5 PyNaCl==1.5.0 -SQLAlchemy-Continuum==1.3.15 SQLAlchemy-Utils==0.41.1 -SQLAlchemy==1.3.24 -Werkzeug==1.0.1 -alembic==1.11.1 -aniso8601==9.0.1 -asyncio-nats-client==0.11.5 -asyncio-nats-streaming==0.4.0 -attrs==23.1.0 -bcrypt==4.0.1 -blinker==1.6.2 +SQLAlchemy==2.0.28 +Werkzeug==3.0.1 +alembic==1.13.1 +argon2-cffi-bindings==21.2.0 +argon2-cffi==23.1.0 +attrs==23.2.0 +bcrypt==4.1.2 +blinker==1.7.0 cachelib==0.9.0 -cachetools==5.3.1 -cattrs==23.1.2 -certifi==2023.7.22 -cffi==1.15.1 -charset-normalizer==3.1.0 -click==8.1.3 -convertdate==2.4.0 -croniter==1.4.1 -cryptography==42.0.2 +cachetools==5.3.3 +cattrs==23.2.3 +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +croniter==2.0.2 +cryptography==42.0.5 cx-Oracle==8.3.0 -dataclass-wizard==0.22.2 +dataclass-wizard==0.22.3 dpath==2.1.6 ecdsa==0.18.0 -exceptiongroup==1.1.1 +exceptiongroup==1.2.0 expiringdict==1.2.2 -flask-jwt-oidc==0.3.0 -flask-marshmallow==0.11.0 -flask-restx==1.1.0 -google-api-core==2.11.1 -google-auth==2.18.1 -google-cloud-pubsub==2.17.0 -googleapis-common-protos==1.59.1 -grpc-google-iam-v1==0.12.6 -grpcio-status==1.48.2 -grpcio==1.56.0 -gunicorn==20.1.0 -hijri-converter==2.3.1 +flask-marshmallow==1.2.0 +google-api-core==2.17.1 +google-auth==2.28.1 +google-cloud-pubsub==2.20.0 +googleapis-common-protos==1.62.0 +greenlet==3.0.3 +grpc-google-iam-v1==0.13.0 +grpcio-status==1.62.0 +grpcio==1.62.0 +gunicorn==21.2.0 holidays==0.37 -idna==3.4 -importlib-metadata==6.7.0 -importlib-resources==5.12.0 -itsdangerous==2.0.1 +idna==3.6 +importlib_metadata==7.0.2 +importlib_resources==6.1.3 +itsdangerous==2.1.2 jaeger-client==4.8.0 jsonschema==4.17.3 -korean-lunar-calendar==0.3.1 -launchdarkly-server-sdk==8.1.4 -marshmallow-sqlalchemy==0.25.0 -marshmallow==3.19.0 -minio==7.1.15 -more-itertools==9.1.0 +launchdarkly-eventsource==1.1.1 +launchdarkly-server-sdk==9.2.2 +marshmallow-sqlalchemy==1.0.0 +marshmallow==3.21.1 +minio==7.2.5 +more-itertools==10.2.0 opentracing==2.4.0 -packaging==23.1 +packaging==23.2 paramiko==3.4.0 pkgutil_resolve_name==1.3.10 -proto-plus==1.22.3 -protobuf==3.19.6 -psycopg2-binary==2.9.6 +proto-plus==1.23.0 +protobuf==4.25.3 +psycopg2-binary==2.9.9 pyRFC3339==1.1 pyasn1-modules==0.3.0 -pyasn1==0.5.0 +pyasn1==0.5.1 pycparser==2.21 -pyrsistent==0.19.3 +pycryptodome==3.20.0 +pyrsistent==0.20.0 pysftp==0.2.9 -python-dateutil==2.8.2 -python-dotenv==1.0.0 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 python-jose==3.3.0 -pytz==2023.3 +pytz==2024.1 requests==2.31.0 rsa==4.9 -semver==2.13.0 -sentry-sdk==1.26.0 +semver==3.0.2 +sentry-sdk==1.41.0 simple-cloudevent @ git+https://github.com/daxiom/simple-cloudevent.py.git@447cabb988202206ac69e71177d7cd11b6c0b002 six==1.16.0 strict-rfc3339==0.7 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.3 -typing_extensions==4.6.3 -urllib3==1.26.17 -zipp==3.15.0 +tornado==6.4 +typing_extensions==4.10.0 +urllib3==2.2.1 +zipp==3.17.0 diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index 7d594a5ec..939e87b0f 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -21,7 +21,7 @@ from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.services.cfs_service import CFSService from pay_api.services.oauth_service import OAuthService -from pay_api.utils.constants import RECEIPT_METHOD_PAD_DAILY +from pay_api.utils.constants import RECEIPT_METHOD_PAD_DAILY, RECEIPT_METHOD_EFT_MONTHLY from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, PaymentMethod from sentry_sdk import capture_message from services import routing_slip @@ -111,8 +111,12 @@ def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: Paym payment_info=payment_info) pending_account.payment_instrument_number = bank_details.get('payment_instrument_number', None) else: # It's a new account, now create + if pay_account.payment_method == PaymentMethod.EFT.value: + cfs_account_details = CFSService.create_cfs_account(identifier=pay_account.auth_account_id, + contact_info=contact_info, + receipt_method=RECEIPT_METHOD_EFT_MONTHLY) # If the account have banking information, then create a PAD account else a regular account. - if pending_account.bank_number and pending_account.bank_branch_number \ + elif pending_account.bank_number and pending_account.bank_branch_number \ and pending_account.bank_account_number: cfs_account_details = CFSService.create_cfs_account(identifier=pay_account.auth_account_id, contact_info=contact_info, diff --git a/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py b/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py index e27435880..af72cf3ba 100644 --- a/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py +++ b/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py @@ -28,7 +28,7 @@ from tasks.cfs_create_account_task import CreateAccountTask from utils import mailer -from .factory import factory_create_online_banking_account, factory_create_pad_account +from .factory import factory_create_online_banking_account, factory_create_pad_account, factory_create_eft_account def test_create_account_setup(session): @@ -50,6 +50,17 @@ def test_create_pad_account(session): assert cfs_account.cfs_site assert cfs_account.cfs_account assert cfs_account.payment_instrument_number + + +def test_create_eft_account(session): + """Test create account.""" + # Create a pending account first, then call the job + account = factory_create_eft_account(auth_account_id='1') + CreateAccountTask.create_accounts() + account = PaymentAccount.find_by_id(account.id) + cfs_account: CfsAccount = CfsAccount.find_effective_by_account_id(account.id) + assert cfs_account.status == CfsAccountStatus.ACTIVE.value + assert cfs_account.payment_instrument_number is None def test_create_pad_account_user_error(session): diff --git a/pay-api/src/pay_api/services/eft_service.py b/pay-api/src/pay_api/services/eft_service.py index f9eb7d31e..e3abab9be 100644 --- a/pay-api/src/pay_api/services/eft_service.py +++ b/pay-api/src/pay_api/services/eft_service.py @@ -13,15 +13,17 @@ # limitations under the License. """Service to manage CFS EFT Payments.""" from datetime import datetime +from typing import Any, Dict from flask import current_app +from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import InvoiceReferenceStatus, PaymentMethod, PaymentStatus +from pay_api.utils.enums import CfsAccountStatus, InvoiceReferenceStatus, PaymentMethod, PaymentStatus from .deposit_service import DepositService from .invoice import Invoice @@ -37,6 +39,15 @@ def get_payment_method_code(self): """Return EFT as the system code.""" return PaymentMethod.EFT.value + def create_account(self, identifier: str, contact_info: Dict[str, Any], payment_info: Dict[str, Any], + **kwargs) -> CfsAccountModel: + """Create an account for the EFT transactions.""" + # Create CFS Account model instance, set the status as PENDING + current_app.logger.info(f'Creating EFT account details in PENDING status for {identifier}') + cfs_account = CfsAccountModel() + cfs_account.status = CfsAccountStatus.PENDING.value + return cfs_account + def create_invoice(self, payment_account: PaymentAccount, line_items: [PaymentLineItem], invoice: Invoice, **kwargs) -> InvoiceReference: """Return a static invoice number for direct pay.""" diff --git a/pay-api/src/pay_api/services/payment_account.py b/pay-api/src/pay_api/services/payment_account.py index fce65a7bf..c3e34a509 100644 --- a/pay-api/src/pay_api/services/payment_account.py +++ b/pay-api/src/pay_api/services/payment_account.py @@ -469,6 +469,7 @@ def _save_account(cls, account_request: Dict[str, any], payment_account: Payment # 2. Existing payment account: # - If the account was on DIRECT_PAY and switching to Online Banking, and active CFS account is not present. # - If the account was on DRAWDOWN and switching to PAD, and active CFS account is not present + # - If the account was on PAD and switching to EFT, and active CFS account is not present if payment_method: pay_system = PaymentSystemFactory.create_from_payment_method(payment_method=payment_method) diff --git a/pay-api/src/pay_api/utils/constants.py b/pay-api/src/pay_api/utils/constants.py index 087365bc3..0cab472ce 100644 --- a/pay-api/src/pay_api/utils/constants.py +++ b/pay-api/src/pay_api/utils/constants.py @@ -21,6 +21,8 @@ DEFAULT_CURRENCY = 'CAD' RECEIPT_METHOD_PAD_DAILY = 'BCR-PAD Daily' RECEIPT_METHOD_PAD_STOP = 'BCR-PAD Stop' +RECEIPT_METHOD_EFT_MONTHLY = 'BCR-EFT MONTHLY' +RECEIPT_METHOD_EFT_STOP = 'BCR-PAD Stop' CFS_BATCH_SOURCE = 'BC REG MANUAL_OTHER' CFS_CM_BATCH_SOURCE = 'MANUAL-OTHER' From 06673215d07ac896fdc84e134f424de4d89c87b5 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Wed, 13 Mar 2024 11:32:55 -0700 Subject: [PATCH 15/87] 18263 - New queues use PUB/SUB (#1438) * Update requirements.txt * Fix CI * Changes to convert project to new queues * Remove nats from workflows * remove nats from docker compose * remove q_cli * Remove more NATS * Remove more nats * Gun events-listener, combining the queues for less tech debt * More nats removal * Move payment-reconciliations into pay-queue * Change project structure of queues * Add in tests from events listener * Getting rid of some infrastructure and tech debt we don't need * Enhance gcp_queue_publisher * Wire up ftp poller to new queues * Add in enum, change util func * Small tweaks for gcp_queue_publisher * Put in adhoc test for publishing to queue * Add in PaymentToken dataclass shared across apps * shorten enums * Replace queue messages in mailer in payment-jobs * Add more MessageTypes to enum * More enum * Convert to PUB * missing comma * Add in pub * Rework entire PUB * Clean up clean up everybody clean up! * Small fix for ftp-poller * Fix ref * Holy moly third try it must be friday * fix lint * remove unused receipt method * payment jobs updates, makefile for pay-queue * linting fixes, requirements fixes * Dependencies are being funny with this one - will need to relook at it. This should work for now. * Update old references * Add in pay jobs CI * directory change * Use pip 24.0.0 * Flask-Migrate upgrade + disable compare_type * Small tweaks for jobs, still some failing tests * Use scalars to fix where conditions when there are tuples, as those cause exceptions * Fix CI * Fix unit test for pay-api * Lint fixes and others * Update CI * fix deps * update reqs * Take out tracing for BCOL-API * more tracing removal * 1 more spot for tracing * Fix linting, move pubsub to local, had issues referencing it as a library * Update requirements * More test / lint fixes * Cleanup conftest * more cleanup * Some unit tests working, more to go * More cleanup * 4 unit tests to go * Fix eft reconciliation tests * Lint fixes more unit test fixes * Unit tests should be all passing * Remove redundant comment * Cleanup * Lint fix * Add in code coverage for pay-queue * Put in event listener topic * Some cleanup * Update requirements --- .github/workflows/bcol-api-ci.yml | 2 +- .github/workflows/events-listener-ci.yml | 111 ----- .github/workflows/ftp-poller-ci.yml | 6 +- .github/workflows/pay-admin-ci.yml | 2 +- .github/workflows/pay-api-ci.yml | 6 +- ...vents-listener-cd.yml => pay-queue-cd.yml} | 17 +- ...econciliations-ci.yml => pay-queue-ci.yml} | 15 +- .github/workflows/payment-jobs-ci.yml | 5 +- .../workflows/payment-reconciliations-cd.yml | 115 ----- .gitignore | 6 +- bcol-api/requirements.txt | 9 +- bcol-api/setup.cfg | 2 +- bcol-api/src/bcol_api/resources/__init__.py | 1 - .../src/bcol_api/resources/bcol_payment.py | 2 - .../src/bcol_api/resources/bcol_profile.py | 3 - bcol-api/src/bcol_api/utils/logging.py | 7 +- bcol-api/src/bcol_api/utils/trace.py | 10 +- codecov.yaml | 11 +- docker/docker-compose.yml | 15 - jobs/ftp-poller/README.md | 4 +- jobs/ftp-poller/config.py | 16 +- jobs/ftp-poller/devops/vaults.json | 7 - jobs/ftp-poller/invoke_jobs.py | 1 - jobs/ftp-poller/requirements.txt | 2 +- jobs/ftp-poller/requirements/dev.txt | 1 - .../requirements/repo-libraries.txt | 2 +- jobs/ftp-poller/services/sftp.py | 6 +- jobs/ftp-poller/setup.cfg | 2 +- .../tasks/cgi_feeder_poller_task.py | 11 +- jobs/ftp-poller/tasks/eft_poller_ftp.py | 5 +- .../tests/docker/docker-compose.yml | 13 - jobs/ftp-poller/tests/jobs/conftest.py | 81 +--- jobs/ftp-poller/tests/jobs/test_sftp.py | 23 +- jobs/ftp-poller/utils/logger.py | 11 +- jobs/ftp-poller/utils/utils.py | 30 +- jobs/payment-jobs/config.py | 20 +- jobs/payment-jobs/devops/vaults.json | 8 - jobs/payment-jobs/invoke_jobs.py | 3 +- jobs/payment-jobs/requirements.txt | 24 +- .../requirements/bcregistry-libraries.txt | 3 +- jobs/payment-jobs/requirements/dev.txt | 2 +- jobs/payment-jobs/requirements/prod.txt | 12 +- jobs/payment-jobs/services/oracle.py | 8 +- jobs/payment-jobs/setup.cfg | 2 +- .../tasks/cfs_create_account_task.py | 9 +- jobs/payment-jobs/tasks/eft_transfer_task.py | 15 +- .../tasks/ejv_partner_distribution_task.py | 20 +- jobs/payment-jobs/tasks/ejv_payment_task.py | 8 +- .../tests/docker/docker-compose.yml | 13 - jobs/payment-jobs/tests/docker/nginx.conf | 2 +- jobs/payment-jobs/tests/jobs/conftest.py | 133 ++---- .../jobs/test_cfs_create_account_task.py | 4 +- .../tests/jobs/test_eft_transfer_task.py | 22 +- .../tests/jobs/test_generate_statements.py | 6 +- .../tests/jobs/test_statement_due_task.py | 67 ++- .../jobs/test_statement_notification_task.py | 393 +++++++++--------- jobs/payment-jobs/utils/logger.py | 7 +- jobs/payment-jobs/utils/mailer.py | 96 ++--- pay-admin/admin/config.py | 1 - pay-admin/requirements.txt | 2 +- pay-admin/requirements/repo-libraries.txt | 2 +- pay-admin/setup.cfg | 2 +- pay-api/migrations/env.py | 1 - pay-api/requirements.txt | 22 +- pay-api/requirements/prod.txt | 3 +- pay-api/setup.cfg | 2 +- pay-api/src/pay_api/config.py | 5 +- .../pay_api/services/base_payment_system.py | 53 ++- pay-api/src/pay_api/services/eft_service.py | 14 +- pay-api/src/pay_api/services/fee_schedule.py | 1 - .../pay_api/services/gcp_queue_publisher.py | 41 +- .../src/pay_api/services/payment_account.py | 48 ++- .../pay_api/services/payment_transaction.py | 44 +- pay-api/src/pay_api/utils/constants.py | 1 - pay-api/src/pay_api/utils/enums.py | 34 +- pay-api/src/pay_api/utils/util.py | 14 +- pay-api/tests/conftest.py | 36 +- .../tests/unit/api/test_eft_short_names.py | 22 +- .../events-listener => pay-queue}/.envrc | 0 .../Dockerfile | 2 +- .../events-listener => pay-queue}/LICENSE | 0 .../events-listener => pay-queue}/MANIFEST.in | 0 .../Makefile | 4 +- pay-queue/README.md | 28 ++ .../utils/constants.py => pay-queue/app.py | 17 +- .../devops/vaults.json | 10 - pay-queue/flags.json | 8 + .../logging.conf | 0 .../payment-reconciliations-build.json | 0 .../payment-reconciliations-deploy.json | 0 pay-queue/requirements.txt | 85 ++++ .../requirements/bcregistry-libraries.txt | 5 + .../requirements/dev.txt | 2 +- .../requirements/prod.txt | 13 +- .../scripts/verify_license_headers.sh | 0 .../setup.cfg | 6 +- .../setup.py | 2 +- pay-queue/src/pay_queue/__init__.py | 57 +++ .../src/pay_queue}/config.py | 53 +-- .../src/pay_queue}/enums.py | 9 - pay-queue/src/pay_queue/external/gcp_auth.py | 40 ++ pay-queue/src/pay_queue/external/pubsub.py | 196 +++++++++ pay-queue/src/pay_queue/external/readme.txt | 1 + .../src/pay_queue}/minio.py | 0 .../src/pay_queue/resources/__init__.py | 30 +- pay-queue/src/pay_queue/resources/worker.py | 52 +++ pay-queue/src/pay_queue/services/__init__.py | 41 ++ .../services}/cgi_reconciliations.py | 120 +++--- .../src/pay_queue/services}/eft/__init__.py | 0 .../src/pay_queue/services}/eft/eft_base.py | 11 +- .../src/pay_queue/services}/eft/eft_enums.py | 0 .../src/pay_queue/services}/eft/eft_errors.py | 0 .../src/pay_queue/services}/eft/eft_header.py | 8 +- .../services}/eft/eft_parse_error.py | 2 +- .../services}/eft/eft_reconciliation.py | 62 ++- .../src/pay_queue/services}/eft/eft_record.py | 8 +- .../pay_queue/services}/eft/eft_trailer.py | 8 +- .../pay_queue/services/identifier_updater.py | 34 ++ .../services}/payment_reconciliations.py | 263 ++++++------ .../src/pay_queue}/version.py | 2 +- .../tests/__init__.py | 0 pay-queue/tests/conftest.py | 115 +++++ .../tests/docker-compose.yml | 13 - .../tests/integration/__init__.py | 3 +- .../tests/integration/factory.py | 0 .../integration/test_cgi_reconciliations.py | 201 ++------- .../integration/test_eft_reconciliation.py | 338 ++++++--------- .../test_payment_reconciliations.py | 194 ++------- .../tests/integration/test_worker_queue.py | 48 +++ .../tests/integration/utils.py | 117 +++--- .../tests/nginx.conf | 0 .../tests/unit/__init__.py | 0 .../tests/unit/test_data/tdi17_sample.txt | 0 .../tests/unit/test_eft_file_parser.py | 6 +- .../tests/utilities/__init__.py | 0 .../tests/utilities/factory_utils.py | 2 +- queue_services/events-listener/Dockerfile | 35 -- queue_services/events-listener/Makefile | 144 ------- queue_services/events-listener/README.md | 75 ---- queue_services/events-listener/app.py | 35 -- queue_services/events-listener/coverage.xml | 133 ------ .../events-listener/devops/vaults.json | 28 -- queue_services/events-listener/logging.conf | 28 -- .../templates/events-listener-build.json | 111 ----- .../templates/events-listener-deploy.json | 200 --------- queue_services/events-listener/q_cli.py | 128 ------ .../events-listener/requirements.txt | 36 -- .../requirements/repo-libraries.txt | 4 - queue_services/events-listener/setup.cfg | 119 ------ queue_services/events-listener/setup.py | 70 ---- .../src/events_listener/__init__.py | 17 - .../src/events_listener/config.py | 131 ------ .../src/events_listener/utils.py | 32 -- .../src/events_listener/version.py | 25 -- .../src/events_listener/worker.py | 84 ---- .../events-listener/tests/conftest.py | 244 ----------- .../events-listener/tests/docker-compose.yml | 15 - .../tests/integration/__init__.py | 87 ---- .../tests/integration/test_worker_queue.py | 141 ------- .../tests/integration/utils.py | 43 -- queue_services/payment-reconciliations/.envrc | 6 - .../payment-reconciliations/LICENSE | 13 - .../payment-reconciliations/MANIFEST.in | 5 - .../payment-reconciliations/README.md | 75 ---- queue_services/payment-reconciliations/app.py | 35 -- .../payment-reconciliations/q_cli.py | 132 ------ .../payment-reconciliations/requirements.txt | 93 ----- .../requirements/bcregistry-libraries.txt | 5 - .../requirements/dev.txt | 31 -- .../requirements/prod.txt | 16 - .../src/reconciliations/__init__.py | 17 - .../src/reconciliations/utils.py | 32 -- .../src/reconciliations/worker.py | 81 ---- .../payment-reconciliations/tests/__init__.py | 32 -- .../payment-reconciliations/tests/conftest.py | 278 ------------- report-api/src/api/utils/logging.py | 7 +- 176 files changed, 1895 insertions(+), 4929 deletions(-) delete mode 100644 .github/workflows/events-listener-ci.yml rename .github/workflows/{events-listener-cd.yml => pay-queue-cd.yml} (89%) rename .github/workflows/{payment-reconciliations-ci.yml => pay-queue-ci.yml} (88%) delete mode 100644 .github/workflows/payment-reconciliations-cd.yml rename {queue_services/events-listener => pay-queue}/.envrc (100%) rename {queue_services/payment-reconciliations => pay-queue}/Dockerfile (95%) rename {queue_services/events-listener => pay-queue}/LICENSE (100%) rename {queue_services/events-listener => pay-queue}/MANIFEST.in (100%) rename {queue_services/payment-reconciliations => pay-queue}/Makefile (98%) create mode 100755 pay-queue/README.md rename jobs/ftp-poller/utils/constants.py => pay-queue/app.py (65%) mode change 100644 => 100755 rename {queue_services/payment-reconciliations => pay-queue}/devops/vaults.json (74%) create mode 100644 pay-queue/flags.json rename {queue_services/payment-reconciliations => pay-queue}/logging.conf (100%) rename {queue_services/payment-reconciliations => pay-queue}/openshift/templates/payment-reconciliations-build.json (100%) rename {queue_services/payment-reconciliations => pay-queue}/openshift/templates/payment-reconciliations-deploy.json (100%) create mode 100644 pay-queue/requirements.txt create mode 100644 pay-queue/requirements/bcregistry-libraries.txt rename {queue_services/events-listener => pay-queue}/requirements/dev.txt (96%) rename {queue_services/events-listener => pay-queue}/requirements/prod.txt (51%) rename {queue_services/events-listener => pay-queue}/scripts/verify_license_headers.sh (100%) rename {queue_services/payment-reconciliations => pay-queue}/setup.cfg (93%) rename {queue_services/payment-reconciliations => pay-queue}/setup.py (97%) create mode 100644 pay-queue/src/pay_queue/__init__.py rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue}/config.py (66%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue}/enums.py (86%) create mode 100644 pay-queue/src/pay_queue/external/gcp_auth.py create mode 100644 pay-queue/src/pay_queue/external/pubsub.py create mode 100644 pay-queue/src/pay_queue/external/readme.txt rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue}/minio.py (100%) rename queue_services/payment-reconciliations/scripts/verify_license_headers.sh => pay-queue/src/pay_queue/resources/__init__.py (50%) mode change 100755 => 100644 create mode 100644 pay-queue/src/pay_queue/resources/worker.py create mode 100644 pay-queue/src/pay_queue/services/__init__.py rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/cgi_reconciliations.py (80%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/eft/__init__.py (100%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/eft/eft_base.py (95%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/eft/eft_enums.py (100%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/eft/eft_errors.py (100%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/eft/eft_header.py (89%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/eft/eft_parse_error.py (94%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/eft/eft_reconciliation.py (89%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/eft/eft_record.py (94%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/eft/eft_trailer.py (88%) create mode 100644 pay-queue/src/pay_queue/services/identifier_updater.py rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue/services}/payment_reconciliations.py (80%) rename {queue_services/payment-reconciliations/src/reconciliations => pay-queue/src/pay_queue}/version.py (93%) rename {queue_services/events-listener => pay-queue}/tests/__init__.py (100%) create mode 100644 pay-queue/tests/conftest.py rename {queue_services/payment-reconciliations => pay-queue}/tests/docker-compose.yml (71%) rename {queue_services/payment-reconciliations => pay-queue}/tests/integration/__init__.py (97%) rename {queue_services/payment-reconciliations => pay-queue}/tests/integration/factory.py (100%) rename {queue_services/payment-reconciliations => pay-queue}/tests/integration/test_cgi_reconciliations.py (89%) rename {queue_services/payment-reconciliations => pay-queue}/tests/integration/test_eft_reconciliation.py (70%) rename {queue_services/payment-reconciliations => pay-queue}/tests/integration/test_payment_reconciliations.py (77%) create mode 100644 pay-queue/tests/integration/test_worker_queue.py rename {queue_services/payment-reconciliations => pay-queue}/tests/integration/utils.py (51%) rename {queue_services/payment-reconciliations => pay-queue}/tests/nginx.conf (100%) rename {queue_services/payment-reconciliations => pay-queue}/tests/unit/__init__.py (100%) rename {queue_services/payment-reconciliations => pay-queue}/tests/unit/test_data/tdi17_sample.txt (100%) rename {queue_services/payment-reconciliations => pay-queue}/tests/unit/test_eft_file_parser.py (99%) rename {queue_services/payment-reconciliations => pay-queue}/tests/utilities/__init__.py (100%) rename {queue_services/payment-reconciliations => pay-queue}/tests/utilities/factory_utils.py (98%) delete mode 100644 queue_services/events-listener/Dockerfile delete mode 100644 queue_services/events-listener/Makefile delete mode 100755 queue_services/events-listener/README.md delete mode 100755 queue_services/events-listener/app.py delete mode 100644 queue_services/events-listener/coverage.xml delete mode 100644 queue_services/events-listener/devops/vaults.json delete mode 100644 queue_services/events-listener/logging.conf delete mode 100755 queue_services/events-listener/openshift/templates/events-listener-build.json delete mode 100755 queue_services/events-listener/openshift/templates/events-listener-deploy.json delete mode 100755 queue_services/events-listener/q_cli.py delete mode 100644 queue_services/events-listener/requirements.txt delete mode 100644 queue_services/events-listener/requirements/repo-libraries.txt delete mode 100644 queue_services/events-listener/setup.cfg delete mode 100644 queue_services/events-listener/setup.py delete mode 100644 queue_services/events-listener/src/events_listener/__init__.py delete mode 100644 queue_services/events-listener/src/events_listener/config.py delete mode 100644 queue_services/events-listener/src/events_listener/utils.py delete mode 100644 queue_services/events-listener/src/events_listener/version.py delete mode 100644 queue_services/events-listener/src/events_listener/worker.py delete mode 100644 queue_services/events-listener/tests/conftest.py delete mode 100644 queue_services/events-listener/tests/docker-compose.yml delete mode 100644 queue_services/events-listener/tests/integration/__init__.py delete mode 100644 queue_services/events-listener/tests/integration/test_worker_queue.py delete mode 100644 queue_services/events-listener/tests/integration/utils.py delete mode 100644 queue_services/payment-reconciliations/.envrc delete mode 100644 queue_services/payment-reconciliations/LICENSE delete mode 100644 queue_services/payment-reconciliations/MANIFEST.in delete mode 100755 queue_services/payment-reconciliations/README.md delete mode 100755 queue_services/payment-reconciliations/app.py delete mode 100755 queue_services/payment-reconciliations/q_cli.py delete mode 100644 queue_services/payment-reconciliations/requirements.txt delete mode 100644 queue_services/payment-reconciliations/requirements/bcregistry-libraries.txt delete mode 100755 queue_services/payment-reconciliations/requirements/dev.txt delete mode 100644 queue_services/payment-reconciliations/requirements/prod.txt delete mode 100644 queue_services/payment-reconciliations/src/reconciliations/__init__.py delete mode 100644 queue_services/payment-reconciliations/src/reconciliations/utils.py delete mode 100644 queue_services/payment-reconciliations/src/reconciliations/worker.py delete mode 100644 queue_services/payment-reconciliations/tests/__init__.py delete mode 100644 queue_services/payment-reconciliations/tests/conftest.py diff --git a/.github/workflows/bcol-api-ci.yml b/.github/workflows/bcol-api-ci.yml index 24bbc9143..ad004ae26 100644 --- a/.github/workflows/bcol-api-ci.yml +++ b/.github/workflows/bcol-api-ci.yml @@ -4,7 +4,7 @@ on: pull_request: branches: - main - - queue_python_upgrade + - feature-queue-python-upgrade paths: - "bcol-api/**" diff --git a/.github/workflows/events-listener-ci.yml b/.github/workflows/events-listener-ci.yml deleted file mode 100644 index 5ab9ccc96..000000000 --- a/.github/workflows/events-listener-ci.yml +++ /dev/null @@ -1,111 +0,0 @@ -name: Events Listener Queue CI - -on: - pull_request: - branches: - - main - paths: - - "queue_services/events-listener/**" - - "pay-api/src/pay_api/models/**" - -defaults: - run: - shell: bash - working-directory: ./queue_services/events-listener - -jobs: - setup-job: - runs-on: ubuntu-20.04 - - if: github.repository == 'bcgov/sbc-pay' - - steps: - - uses: actions/checkout@v3 - - run: "true" - - linting: - needs: setup-job - runs-on: ubuntu-20.04 - - strategy: - matrix: - python-version: [3.12] - - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - make setup - - name: Lint with pylint - id: pylint - run: | - make pylint - - name: Lint with flake8 - id: flake8 - run: | - make flake8 - - testing: - needs: setup-job - env: - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" - TEST_NATS_DOCKER: "YES" - STAN_CLUSTER_NAME: "test-cluster" - - runs-on: ubuntu-20.04 - - strategy: - matrix: - python-version: [3.12] - - services: - postgres: - image: postgres:12 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres - ports: - - 5432:5432 - # needed because the postgres container does not provide a healthcheck - options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - make setup - - name: Test with pytest - id: test - run: | - make test - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - file: ./queue_services/events-listener/coverage.xml - flags: eventlistenerqueue - name: codecov-events-listener - fail_ci_if_error: true - - build-check: - needs: setup-job - runs-on: ubuntu-20.04 - - strategy: - matrix: - python-version: [3.12] - - steps: - - uses: actions/checkout@v3 - - name: build to check strictness - id: build - run: | - make build-nc diff --git a/.github/workflows/ftp-poller-ci.yml b/.github/workflows/ftp-poller-ci.yml index 14a9c3c09..53e90abb1 100644 --- a/.github/workflows/ftp-poller-ci.yml +++ b/.github/workflows/ftp-poller-ci.yml @@ -4,7 +4,7 @@ on: pull_request: branches: - main - - queue_python_upgrade + - feature-queue-python-upgrade paths: - "jobs/ftp-poller/**" @@ -53,10 +53,6 @@ jobs: needs: setup-job env: DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" - NATS_QUEUE: "account-worker" - NATS_CLUSTER_ID: "test-cluster" - NATS_CLIENT_NAME: "account.events.worker" - NATS_SUBJECT: "account.events" USE_DOCKER_MOCK: "YES" JWT_OIDC_ISSUER: "http://localhost:8081/auth/realms/demo" SBC_AUTH_ADMIN_CLIENT_ID: "sbc-auth-admin" diff --git a/.github/workflows/pay-admin-ci.yml b/.github/workflows/pay-admin-ci.yml index 0a6b544f2..a7dac379c 100644 --- a/.github/workflows/pay-admin-ci.yml +++ b/.github/workflows/pay-admin-ci.yml @@ -4,7 +4,7 @@ on: pull_request: branches: - main - - queue_python_upgrade + - feature-queue-python-upgrade paths: - "pay-admin/**" - "pay-api/src/pay_api/models/**" diff --git a/.github/workflows/pay-api-ci.yml b/.github/workflows/pay-api-ci.yml index 0a6971992..b21549e25 100644 --- a/.github/workflows/pay-api-ci.yml +++ b/.github/workflows/pay-api-ci.yml @@ -4,7 +4,7 @@ on: pull_request: branches: - main - - queue_python_upgrade + - feature-queue-python-upgrade paths: - "pay-api/**" @@ -55,7 +55,6 @@ jobs: FLASK_ENV: "testing" # Needs different database than POSTGRES otherwise dropping database doesn't work DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" - TEST_NATS_DOCKER: "YES" USE_TEST_KEYCLOAK_DOCKER: "YES" USE_DOCKER_MOCK: "YES" @@ -66,9 +65,6 @@ jobs: JWT_OIDC_TEST_CLIENT_SECRET: "1111111111" JWT_OIDC_TEST_JWKS_CACHE_TIMEOUT: "6000" - NATS_QUEUE: "test-worker" - NATS_SUBJECT: "entity.payment.test" - SBC_AUTH_ADMIN_CLIENT_ID: "sbc-auth-admin" SBC_AUTH_ADMIN_CLIENT_SECRET: "2222222222" diff --git a/.github/workflows/events-listener-cd.yml b/.github/workflows/pay-queue-cd.yml similarity index 89% rename from .github/workflows/events-listener-cd.yml rename to .github/workflows/pay-queue-cd.yml index 9ae30be64..8461d258e 100644 --- a/.github/workflows/events-listener-cd.yml +++ b/.github/workflows/pay-queue-cd.yml @@ -1,12 +1,13 @@ -name: Events Listener Queue CD +name: Pay Queue CD on: push: branches: - main paths: - - "queue_services/events-listener/**" + - "pay-queue/**" - "pay-api/src/pay_api/models/**" + - "pay-api/src/pay_api/services/cfs_service.py" workflow_dispatch: inputs: environment: @@ -17,14 +18,14 @@ on: defaults: run: shell: bash - working-directory: ./queue_services/events-listener + working-directory: ./pay-queue env: - APP_NAME: "events-listener" + APP_NAME: "pay-queue" TAG_NAME: "dev" jobs: - events-listener-cd-by-push: + pay-queue-cd-by-push: runs-on: ubuntu-20.04 if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' @@ -61,13 +62,13 @@ jobs: if: failure() with: type: ${{ job.status }} - job_name: "*Events Listener Queue Built and Deployed to ${{env.TAG_NAME}}*" + job_name: "*Pay Queue Built and Deployed to ${{env.TAG_NAME}}*" channel: "#registries-bot" url: ${{ secrets.ROCKETCHAT_WEBHOOK }} commit: true token: ${{ secrets.GITHUB_TOKEN }} - events-listener-cd-by-dispatch: + pay-queue-cd-by-dispatch: runs-on: ubuntu-20.04 if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' @@ -107,7 +108,7 @@ jobs: if: failure() with: type: ${{ job.status }} - job_name: "*Events Listener Queue Built and Deployed to ${{env.TAG_NAME}}*" + job_name: "*Pay Queue Built and Deployed to ${{env.TAG_NAME}}*" channel: "#registries-bot" url: ${{ secrets.ROCKETCHAT_WEBHOOK }} commit: true diff --git a/.github/workflows/payment-reconciliations-ci.yml b/.github/workflows/pay-queue-ci.yml similarity index 88% rename from .github/workflows/payment-reconciliations-ci.yml rename to .github/workflows/pay-queue-ci.yml index 415889d0a..c399fae26 100644 --- a/.github/workflows/payment-reconciliations-ci.yml +++ b/.github/workflows/pay-queue-ci.yml @@ -1,18 +1,19 @@ -name: Payment Reconciliations Queue CI +name: Payment Queue CI on: pull_request: branches: - main + - feature-queue-python-upgrade paths: - - "queue_services/payment-reconciliations/**" + - "pay-queue/**" - "pay-api/src/pay_api/models/**" - "pay-api/src/pay_api/services/cfs_service.py" defaults: run: shell: bash - working-directory: ./queue_services/payment-reconciliations + working-directory: ./pay-queue jobs: setup-job: @@ -53,10 +54,8 @@ jobs: testing: needs: setup-job env: - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" - TEST_NATS_DOCKER: "YES" + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" USE_DOCKER_MOCK: "YES" - STAN_CLUSTER_NAME: "test-cluster" MINIO_ENDPOINT: "localhost:9000" MINIO_ACCESS_KEY: "minio" MINIO_ACCESS_SECRET: "minio123" @@ -80,7 +79,7 @@ jobs: env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck @@ -102,7 +101,7 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: - file: ./queue_services/payment-reconciliations/coverage.xml + file: ./pay-queue/coverage.xml flags: paymentreconciliationsqueue name: codecov-payment-reconciliations fail_ci_if_error: true diff --git a/.github/workflows/payment-jobs-ci.yml b/.github/workflows/payment-jobs-ci.yml index 213027b6e..2f3e58c13 100644 --- a/.github/workflows/payment-jobs-ci.yml +++ b/.github/workflows/payment-jobs-ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - feature-queue-python-upgrade paths: - "jobs/payment-jobs/**" - "pay-api/src/pay_api/models/**" @@ -53,7 +54,7 @@ jobs: testing: needs: setup-job env: - DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/pay-test" USE_DOCKER_MOCK: "YES" JWT_OIDC_ISSUER: "http://localhost:8081/auth/realms/demo" SBC_AUTH_ADMIN_CLIENT_ID: "sbc-auth-admin" @@ -71,7 +72,7 @@ jobs: env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres + POSTGRES_DB: pay-test ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck diff --git a/.github/workflows/payment-reconciliations-cd.yml b/.github/workflows/payment-reconciliations-cd.yml deleted file mode 100644 index 0914a299f..000000000 --- a/.github/workflows/payment-reconciliations-cd.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: Payment Reconciliations Queue CD - -on: - push: - branches: - - main - paths: - - "queue_services/payment-reconciliations/**" - - "pay-api/src/pay_api/models/**" - - "pay-api/src/pay_api/services/cfs_service.py" - workflow_dispatch: - inputs: - environment: - description: "Environment (dev/test/prod)" - required: true - default: "dev" - -defaults: - run: - shell: bash - working-directory: ./queue_services/payment-reconciliations - -env: - APP_NAME: "payment-reconciliations" - TAG_NAME: "dev" - -jobs: - payment-reconciliations-cd-by-push: - runs-on: ubuntu-20.04 - - if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' - environment: - name: "dev" - - steps: - - uses: actions/checkout@v3 - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Payment Reconciliations Queue Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} - - payment-reconciliations-cd-by-dispatch: - runs-on: ubuntu-20.04 - - if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' - environment: - name: "${{ github.event.inputs.environment }}" - - steps: - - uses: actions/checkout@v3 - - name: Set env by input - run: | - echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Payment Reconciliations Queue Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 27cfab6f5..b3f42e947 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ coverage.xml .hypothesis/ .pytest_cache/ pytest.xml +test_eft_tdi17.txt # Translations *.mo @@ -124,6 +125,7 @@ package-lock.json cas_settlement_file.csv jobs/payment-jobs/tests/docker/ftp ACK.INBOX.F12022020202 -queue_services/payment-reconciliations/ACK.* -queue_services/payment-reconciliations/FEEDBACK.* + +pay-queue/ACK.* +pay-queue/FEEDBACK.* jobs/notebook-report/data/ diff --git a/bcol-api/requirements.txt b/bcol-api/requirements.txt index 18e12c0c5..44c6e1330 100644 --- a/bcol-api/requirements.txt +++ b/bcol-api/requirements.txt @@ -16,16 +16,14 @@ flask-jwt-oidc==0.3.0 flask-restx==1.3.0 gunicorn==21.2.0 idna==3.6 -importlib-metadata==7.0.1 -importlib-resources==5.13.0 +importlib_resources==6.1.3 isodate==0.6.1 itsdangerous==2.1.2 jaeger-client==4.8.0 jsonschema==4.17.3 lxml==5.1.0 opentracing==2.4.0 -packaging==23.2 -pkgutil_resolve_name==1.3.10 +packaging==24.0 platformdirs==4.2.0 psycopg2-binary==2.9.9 pyasn1-modules==0.3.0 @@ -40,13 +38,12 @@ requests-file==2.0.0 requests-toolbelt==1.0.0 requests==2.31.0 rsa==4.9 -sentry-sdk==1.40.6 +sentry-sdk==1.41.0 six==1.16.0 threadloop==1.0.2 thrift==0.16.0 tornado==6.4 urllib3==2.2.1 zeep==4.2.1 -zipp==3.17.0 -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/bcol-api/setup.cfg b/bcol-api/setup.cfg index 1fc623352..7b0dffc81 100755 --- a/bcol-api/setup.cfg +++ b/bcol-api/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/bcol-api/src/bcol_api/resources/__init__.py b/bcol-api/src/bcol_api/resources/__init__.py index d094b94c9..28f382357 100755 --- a/bcol-api/src/bcol_api/resources/__init__.py +++ b/bcol-api/src/bcol_api/resources/__init__.py @@ -33,7 +33,6 @@ __all__ = ('API_BLUEPRINT', 'OPS_BLUEPRINT') # This will add the Authorize button to the swagger docs -# TODO oauth2 & openid may not yet be supported by restplus <- check on this AUTHORIZATIONS = {'apikey': {'type': 'apiKey', 'in': 'header', 'name': 'Authorization'}} OPS_BLUEPRINT = Blueprint('API_OPS', __name__, url_prefix='/ops') diff --git a/bcol-api/src/bcol_api/resources/bcol_payment.py b/bcol-api/src/bcol_api/resources/bcol_payment.py index dd8536bf7..ddd2d3669 100755 --- a/bcol-api/src/bcol_api/resources/bcol_payment.py +++ b/bcol-api/src/bcol_api/resources/bcol_payment.py @@ -24,7 +24,6 @@ from bcol_api.utils.auth import jwt as _jwt from bcol_api.utils.constants import Role from bcol_api.utils.errors import Error -from bcol_api.utils.trace import tracing as _tracing from bcol_api.utils.util import cors_preflight @@ -37,7 +36,6 @@ class AccountPayment(Resource): """Endpoint resource to manage BCOL Payments.""" @staticmethod - @_tracing.trace() @_jwt.requires_auth @cors.crossdomain(origin='*') def post(): diff --git a/bcol-api/src/bcol_api/resources/bcol_profile.py b/bcol-api/src/bcol_api/resources/bcol_profile.py index 36a61a7ff..5b1c565d9 100755 --- a/bcol-api/src/bcol_api/resources/bcol_profile.py +++ b/bcol-api/src/bcol_api/resources/bcol_profile.py @@ -23,7 +23,6 @@ from bcol_api.services.bcol_profile import BcolProfile as BcolProfileService from bcol_api.utils.auth import jwt as _jwt from bcol_api.utils.errors import Error -from bcol_api.utils.trace import tracing as _tracing from bcol_api.utils.util import cors_preflight @@ -36,7 +35,6 @@ class BcolProfiles(Resource): """Endpoint query bcol profile using user id and password.""" @staticmethod - @_tracing.trace() @_jwt.requires_auth @cors.crossdomain(origin='*') def post(): @@ -61,7 +59,6 @@ class BcolProfile(Resource): """Endpoint resource to get bcol profile by user id.""" @staticmethod - @_tracing.trace() @_jwt.has_one_of_roles(['system']) @cors.crossdomain(origin='*') def get(bcol_user_id: str): diff --git a/bcol-api/src/bcol_api/utils/logging.py b/bcol-api/src/bcol_api/utils/logging.py index 8b88ddcf2..8568f87dd 100755 --- a/bcol-api/src/bcol_api/utils/logging.py +++ b/bcol-api/src/bcol_api/utils/logging.py @@ -18,12 +18,7 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) print(f'Configure logging, from conf:{conf}', file=sys.stdout) diff --git a/bcol-api/src/bcol_api/utils/trace.py b/bcol-api/src/bcol_api/utils/trace.py index 31cc43080..ece264f83 100644 --- a/bcol-api/src/bcol_api/utils/trace.py +++ b/bcol-api/src/bcol_api/utils/trace.py @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. """Bring in the Tracer.""" -from sbc_common_components.tracing.api_tracer import ApiTracer -from sbc_common_components.tracing.api_tracing import ApiTracing +# from sbc_common_components.tracing.api_tracer import ApiTracer +# from sbc_common_components.tracing.api_tracing import ApiTracing # initialize tracer -API_TRACER = ApiTracer('BCOL API Services') -tracing = ApiTracing( # pylint: disable=invalid-name; lower case name as used by convention in most Flask apps - API_TRACER.tracer) +# API_TRACER = ApiTracer('BCOL API Services') +# tracing = ApiTracing( # pylint: disable=invalid-name; lower case name as used by convention in most Flask apps +# API_TRACER.tracer) diff --git a/codecov.yaml b/codecov.yaml index 174116e19..e7b9696ed 100644 --- a/codecov.yaml +++ b/codecov.yaml @@ -17,8 +17,8 @@ coverage: - payapi - bcolapi - reportapi - - eventlistenerqueue - paymentjobs + - payqueue ignore: - "^/tests/**/*" # ignore test harness code @@ -49,11 +49,12 @@ flags: paths: - report-api/src/api carryforward: true - eventlistenerqueue: - paths: - - queue_services/events-listener/src/events_listener - carryforward: true paymentjobs: paths: - jobs/payment-jobs/tasks carryforward: true + payqueue: + paths: + - pay-queue/src/pay_queue + carryforward: true + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 43baaeb8d..b1337ba8e 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -35,21 +35,6 @@ services: - "6831:6831/udp" - "6832:6832/udp" - #################### Nats Service Definition #################### - nats: - image: nats-streaming - restart: always - mem_limit: 512m - expose: - - 4222 - - 8222 - labels: - - entity.services=nats - ports: - - 4222:4222 - - 8222:8222 - tty: true - volumes: db-data: driver: local diff --git a/jobs/ftp-poller/README.md b/jobs/ftp-poller/README.md index bdc63f7ee..e73b38555 100644 --- a/jobs/ftp-poller/README.md +++ b/jobs/ftp-poller/README.md @@ -1,7 +1,7 @@ # SFTP Poller project -Polls the SFTP folder and if a settlement file is found , uploads to minio and sends a NATS message. +Polls the SFTP folder and if a settlement file is found , uploads to minio and sends a pubsub message. ## Openshift commands @@ -84,4 +84,4 @@ this will print the public key. Store the string after ssh-rsa to the ftp-poller https://hub.docker.com/r/atmoz/sftp -docker run -p 22:22 -d atmoz/sftp foo:pass:::upload \ No newline at end of file +docker run -p 22:22 -d atmoz/sftp foo:pass:::upload diff --git a/jobs/ftp-poller/config.py b/jobs/ftp-poller/config.py index 58fefa141..81a331765 100644 --- a/jobs/ftp-poller/config.py +++ b/jobs/ftp-poller/config.py @@ -89,16 +89,6 @@ class _Config(object): # pylint: disable=too-few-public-methods CGI_FEEDBACK_FILE_PREFIX = os.getenv('CGI_FEEDBACK_FILE_PREFIX', 'FEEDBACK') CGI_INBOX_FILE_PREFIX = os.getenv('CGI_FEEDBACK_FILE_PREFIX', 'INBOX') - # NATS Config - NATS_SERVERS = os.getenv('NATS_SERVERS', 'nats://127.0.0.1:4222').split(',') - NATS_CLUSTER_ID = os.getenv('NATS_CLUSTER_ID', 'test-cluster') - NATS_QUEUE = os.getenv('NATS_QUEUE', 'account-worker') - - # NATS Config for account events - NATS_PAYMENT_RECONCILIATIONS_CLIENT_NAME = os.getenv('NATS_PAYMENT_RECONCILIATIONS_CLIENT_NAME', - 'payment.reconciliations.worker') - NATS_PAYMENT_RECONCILIATIONS_SUBJECT = os.getenv('NATS_SUBJECT', 'payment.reconciliations') - SFTP_CONFIGS = { 'CAS': { 'SFTP_HOST': CAS_SFTP_HOST, @@ -146,6 +136,12 @@ class _Config(object): # pylint: disable=too-few-public-methods SENTRY_ENABLE = os.getenv('SENTRY_ENABLE', 'False') SENTRY_DSN = os.getenv('SENTRY_DSN', None) + # GCP PubSub + AUDIENCE = os.getenv('AUDIENCE', None) + GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) + PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) + FTP_POLLER_TOPIC = os.getenv('FTP_POLLER_TOPIC', None) + TESTING = False DEBUG = True diff --git a/jobs/ftp-poller/devops/vaults.json b/jobs/ftp-poller/devops/vaults.json index 1539ff97e..433ed9cbd 100644 --- a/jobs/ftp-poller/devops/vaults.json +++ b/jobs/ftp-poller/devops/vaults.json @@ -6,13 +6,6 @@ "payment-reconciliations", "ftp-poller" ] - }, - { - "vault": "nats", - "application": [ - "base", - "payment-reconciliations" - ] }, { "vault": "relationship", diff --git a/jobs/ftp-poller/invoke_jobs.py b/jobs/ftp-poller/invoke_jobs.py index 6581d2f94..5e18dcccb 100755 --- a/jobs/ftp-poller/invoke_jobs.py +++ b/jobs/ftp-poller/invoke_jobs.py @@ -44,7 +44,6 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): integrations=[FlaskIntegration()] ) app.logger.info(f'<<<< Starting Ftp Poller Job >>>>') - db.init_app(app) ma.init_app(app) register_shellcontext(app) diff --git a/jobs/ftp-poller/requirements.txt b/jobs/ftp-poller/requirements.txt index f2e779e08..44675066f 100644 --- a/jobs/ftp-poller/requirements.txt +++ b/jobs/ftp-poller/requirements.txt @@ -45,6 +45,6 @@ tornado==6.4 typing_extensions==4.10.0 urllib3==2.2.1 -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git@queue_python_upgrade#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/seeker25/sbc-pay.git@18263#egg=pay-api&subdirectory=pay-api git+https://github.com/daxiom/simple-cloudevent.py.git git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/jobs/ftp-poller/requirements/dev.txt b/jobs/ftp-poller/requirements/dev.txt index 696e51010..e98fcbca3 100644 --- a/jobs/ftp-poller/requirements/dev.txt +++ b/jobs/ftp-poller/requirements/dev.txt @@ -20,7 +20,6 @@ autopep8 coverage pylint pylint-flask -#attrs==19.1.0 # docker lovely-pytest-docker diff --git a/jobs/ftp-poller/requirements/repo-libraries.txt b/jobs/ftp-poller/requirements/repo-libraries.txt index 615ab1796..f71c16b32 100644 --- a/jobs/ftp-poller/requirements/repo-libraries.txt +++ b/jobs/ftp-poller/requirements/repo-libraries.txt @@ -1,4 +1,4 @@ -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git@queue_python_upgrade#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/seeker25/sbc-pay.git@18263#egg=pay-api&subdirectory=pay-api git+https://github.com/daxiom/simple-cloudevent.py.git git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/jobs/ftp-poller/services/sftp.py b/jobs/ftp-poller/services/sftp.py index 27b91144c..06fe984e0 100644 --- a/jobs/ftp-poller/services/sftp.py +++ b/jobs/ftp-poller/services/sftp.py @@ -24,10 +24,10 @@ class SFTPService: # pylint: disable=too-few-public-methods """SFTP Service class.""" - DEFAUILT_CONNECT_SERVER = 'CAS' + DEFAULT_CONNECT_SERVER = 'CAS' @staticmethod - def get_connection(server_name: str = DEFAUILT_CONNECT_SERVER) -> Connection: + def get_connection(server_name: str = DEFAULT_CONNECT_SERVER) -> Connection: """Return a SFTP connection.""" # pylint: disable=protected-access return SFTPService._connect(server_name) @@ -38,7 +38,7 @@ def _connect(server_name: str) -> Connection: sftp_configs = current_app.config.get('SFTP_CONFIGS') # if not passed , connect to CAS server always. to make the existing code work if not server_name or server_name not in sftp_configs.keys(): - server_name = SFTPService.DEFAUILT_CONNECT_SERVER + server_name = SFTPService.DEFAULT_CONNECT_SERVER connect_configs = sftp_configs.get(server_name) diff --git a/jobs/ftp-poller/setup.cfg b/jobs/ftp-poller/setup.cfg index 8eb46196f..c06c0dd9b 100755 --- a/jobs/ftp-poller/setup.cfg +++ b/jobs/ftp-poller/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py b/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py index f560a11ff..7542a4d84 100644 --- a/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py +++ b/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py @@ -17,8 +17,8 @@ from flask import current_app from paramiko.sftp_attr import SFTPAttributes +from pay_api.utils.enums import MessageType from services.sftp import SFTPService -from utils.constants import CGI_ACK_MESSAGE_TYPE, CGI_FEEDBACK_MESSAGE_TYPE from utils import utils @@ -48,18 +48,19 @@ def poll_ftp(cls): f'Skipping directory {file_name}.') continue if cls._is_ack_file(file_name): - utils.publish_to_queue([file_name], CGI_ACK_MESSAGE_TYPE) + utils.publish_to_queue([file_name], MessageType.CGI_ACK_RECEIVED.value) cls._move_file_to_backup(sftp_client, [file_name]) elif cls._is_feedback_file(file_name): bucket_name = current_app.config.get('MINIO_CGI_BUCKET_NAME') utils.upload_to_minio(file, file_full_name, sftp_client, bucket_name) - utils.publish_to_queue([file_name], CGI_FEEDBACK_MESSAGE_TYPE, location=bucket_name) + utils.publish_to_queue([file_name], MessageType.CGI_FEEDBACK_RECEIVED.value, + location=bucket_name) cls._move_file_to_backup(sftp_client, [file_name]) elif cls._is_a_trigger_file(file_name): cls._remove_file(sftp_client, file_name) else: current_app.logger.warning( - f'File found which is not trigger , ACK or feed back {file_name}.Ignoring') + f'Ignoring file found which is not trigger ACK or feedback {file_name}.') except Exception as e: # NOQA # pylint: disable=broad-except current_app.logger.error(e) @@ -74,7 +75,7 @@ def _move_file_to_backup(cls, sftp_client, backup_file_list): @classmethod def _remove_file(cls, sftp_client, file_name: str): ftp_dir: str = current_app.config.get('CGI_SFTP_DIRECTORY') - current_app.logger.info(f'Removing file:{ftp_dir}/{file_name}') + current_app.logger.info(f'Removing file: {ftp_dir}/{file_name}') sftp_client.remove(ftp_dir + '/' + file_name) @classmethod diff --git a/jobs/ftp-poller/tasks/eft_poller_ftp.py b/jobs/ftp-poller/tasks/eft_poller_ftp.py index 003987a28..10f59a4f4 100644 --- a/jobs/ftp-poller/tasks/eft_poller_ftp.py +++ b/jobs/ftp-poller/tasks/eft_poller_ftp.py @@ -17,8 +17,8 @@ from flask import current_app from paramiko.sftp_attr import SFTPAttributes +from pay_api.utils.enums import MessageType from services.sftp import SFTPService -from utils.constants import EFT_MESSAGE_TYPE from utils.utils import publish_to_queue, upload_to_minio @@ -66,7 +66,8 @@ def _post_process(cls, sftp_client, payment_file_list: List[str]): 2.Send a message to queue """ cls._move_file_to_backup(sftp_client, payment_file_list) - publish_to_queue(payment_file_list, EFT_MESSAGE_TYPE, location=current_app.config.get('MINIO_EFT_BUCKET_NAME')) + publish_to_queue(payment_file_list, MessageType.EFT_FILE_UPLOADED.value, + location=current_app.config.get('MINIO_EFT_BUCKET_NAME')) @classmethod def _move_file_to_backup(cls, sftp_client, payment_file_list): diff --git a/jobs/ftp-poller/tests/docker/docker-compose.yml b/jobs/ftp-poller/tests/docker/docker-compose.yml index 253f6c0ee..8fb738b62 100644 --- a/jobs/ftp-poller/tests/docker/docker-compose.yml +++ b/jobs/ftp-poller/tests/docker/docker-compose.yml @@ -1,19 +1,6 @@ version: "3" services: - nats: - image: nats-streaming - restart: always - expose: - - 4222 - - 8222 - labels: - - entity.services=nats - ports: - - 4222:4222 - - 8222:8222 - tty: true - proxy: image: nginx:alpine volumes: diff --git a/jobs/ftp-poller/tests/jobs/conftest.py b/jobs/ftp-poller/tests/jobs/conftest.py index 4e134a17a..fd4717049 100644 --- a/jobs/ftp-poller/tests/jobs/conftest.py +++ b/jobs/ftp-poller/tests/jobs/conftest.py @@ -14,95 +14,19 @@ """Common setup and fixtures for the py-test suite used by this service.""" -import sys import time import pytest -from flask_migrate import Migrate, upgrade -from pay_api.models import db as _db -from sqlalchemy import text -from sqlalchemy.schema import DropConstraint, MetaData - from invoke_jobs import create_app @pytest.fixture(scope='session') def app(): """Return a session-wide application configured in TEST mode.""" - _app = create_app('testing') - - return _app - - -@pytest.fixture(scope='function') -def app_request(): - """Return a session-wide application configured in TEST mode.""" - _app = create_app('testing') - - return _app - - -@pytest.fixture(scope='session') -def client(app): # pylint: disable=redefined-outer-name - """Return a session-wide Flask test client.""" - return app.test_client() - - -@pytest.fixture(scope='session') -def client_ctx(app): # pylint: disable=redefined-outer-name - """Return session-wide Flask test client.""" - with app.test_client() as _client: - yield _client - - -@pytest.fixture(scope='session') -def db(app): # pylint: disable=redefined-outer-name, invalid-name - """Return a session-wide initialised database. - - Drops all existing tables - Meta follows Postgres FKs - """ - with app.app_context(): - # Clear out any existing tables - metadata = MetaData(_db.engine) - metadata.reflect() - for table in metadata.tables.values(): - for fk in table.foreign_keys: # pylint: disable=invalid-name - _db.engine.execute(DropConstraint(fk.constraint)) - metadata.drop_all() - _db.drop_all() - - sequence_sql = """SELECT sequence_name FROM information_schema.sequences - WHERE sequence_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(sequence_sql))]: - try: - sess.execute(text(f'DROP SEQUENCE public.{seq} ;')) - print(f'DROP SEQUENCE public.{seq} ') - except Exception as err: # NOQA # pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # ############################################ - # There are 2 approaches, an empty database, or the same one that the app will use - # create the tables - # _db.create_all() - # or - # Use Alembic to load all of the DB revisions including supporting lookup data - # This is the path we'll use in legal_api!! - - # even though this isn't referenced directly, it sets up the internal configs that upgrade needs - migrations_path = [folder for folder in sys.path if 'pay-api/pay-api' in folder][0] \ - .replace('/pay-api/src', '/pay-api/migrations') - - Migrate(app, _db, directory=migrations_path) - upgrade() - - return _db + return create_app('testing') -@pytest.fixture(scope='function') +@pytest.fixture(scope='function', autouse=True) def session(app): # pylint: disable=redefined-outer-name, invalid-name """Return a function-scoped session.""" with app.app_context(): @@ -114,7 +38,6 @@ def auto(docker_services, app): # pylint: disable=redefined-outer-name """Spin up docker instances.""" if app.config['USE_DOCKER_MOCK']: docker_services.start('proxy') - docker_services.start('nats') docker_services.start('sftp') time.sleep(2) diff --git a/jobs/ftp-poller/tests/jobs/test_sftp.py b/jobs/ftp-poller/tests/jobs/test_sftp.py index 4dcd6e2f6..d88470a8d 100644 --- a/jobs/ftp-poller/tests/jobs/test_sftp.py +++ b/jobs/ftp-poller/tests/jobs/test_sftp.py @@ -16,23 +16,20 @@ Test-Suite to ensure that the CreateAccountTask is working as expected. """ -# import os -# from typing import List - +import pytest from flask import current_app from services.sftp import SFTPService -# from utils.minio import get_object -# from tasks.poll_ftp_task import PollFtpTask +from utils.utils import publish_to_queue -def test_cget_sftp_connection(session): # pylint:disable=unused-argument +def test_cget_sftp_connection(): """Test create account.""" con = SFTPService.get_connection() assert con -def test_poll_ftp_task(session): # pylint:disable=unused-argument +def test_poll_ftp_task(): """Test Poll.""" con = SFTPService.get_connection() @@ -40,10 +37,8 @@ def test_poll_ftp_task(session): # pylint:disable=unused-argument files = con.listdir(ftp_dir) assert len(files) == 1, 'Files exist in FTP folder' - # TODO fixed this test case - # payment_file_list: List[str] = PollFtpTask.poll_ftp() - # minio_file_content = get_object(payment_file_list[0]).read().decode() - # full_path = os.path.join(os.path.dirname(__file__), '../docker/ftp/test.txt') - # sftp_local_file_content = open(full_path, 'r').read() - # assert minio_file_content == sftp_local_file_content, 'minio upload works fine.' \ - # 'Contents of ftp drive and minio verified' + +@pytest.mark.skip(reason='leave this to manually verify pubsub connection; needs env vars') +def test_queue_message(): + """Test publishing to topic.""" + publish_to_queue(['file1.csv']) diff --git a/jobs/ftp-poller/utils/logger.py b/jobs/ftp-poller/utils/logger.py index 8b88ddcf2..afa83de96 100755 --- a/jobs/ftp-poller/utils/logger.py +++ b/jobs/ftp-poller/utils/logger.py @@ -18,14 +18,9 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) - print(f'Configure logging, from conf:{conf}', file=sys.stdout) + print(f'Configure logging, from conf: {conf}', file=sys.stdout) else: - print(f'Unable to configure logging, attempted conf:{conf}', file=sys.stderr) + print(f'Unable to configure logging, attempted conf: {conf}', file=sys.stderr) diff --git a/jobs/ftp-poller/utils/utils.py b/jobs/ftp-poller/utils/utils.py index 1d6e10da2..aa452f5c4 100644 --- a/jobs/ftp-poller/utils/utils.py +++ b/jobs/ftp-poller/utils/utils.py @@ -12,19 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. """Service to manage PAYBC services.""" -from datetime import datetime from typing import List from flask import current_app from paramiko import SFTPFile -from pay_api.services.queue_publisher import publish_response +from pay_api.services import gcp_queue_publisher +from pay_api.services.gcp_queue_publisher import QueueMessage +from pay_api.utils.enums import MessageType, QueueSources -from utils.constants import CAS_MESSAGE_TYPE from utils.minio import put_object -def publish_to_queue(payment_file_list: List[str], message_type=CAS_MESSAGE_TYPE, location: str = '' - ): +def publish_to_queue(payment_file_list: List[str], message_type=MessageType.CAS_UPLOADED.value, location: str = ''): """Publish message to the Queue, saying file has been uploaded. Using the event spec.""" queue_data = { 'fileSource': 'MINIO', @@ -33,20 +32,15 @@ def publish_to_queue(payment_file_list: List[str], message_type=CAS_MESSAGE_TYPE for file_name in payment_file_list: queue_data['fileName'] = file_name - payload = { - 'specversion': '1.x-wip', - 'type': message_type, - 'source': file_name, - 'id': file_name, - 'time': f'{datetime.now()}', - 'datacontenttype': 'application/json', - 'data': queue_data - } - try: - publish_response(payload=payload, - client_name=current_app.config.get('NATS_PAYMENT_RECONCILIATIONS_CLIENT_NAME'), - subject=current_app.config.get('NATS_PAYMENT_RECONCILIATIONS_SUBJECT')) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.FTP_POLLER.value, + message_type=message_type, + payload=queue_data, + topic=current_app.config.get('FTP_POLLER_TOPIC') + ) + ) except Exception as e: # NOQA # pylint: disable=broad-except current_app.logger.error(e) current_app.logger.warning( diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index e9dcebf61..842feb1ba 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -112,18 +112,6 @@ class _Config(object): # pylint: disable=too-few-public-methods AUTH_WEB_STATEMENT_URL = os.getenv('AUTH_WEB_STATEMENT_URL', 'account/orgId/settings/statements') REGISTRIES_LOGO_IMAGE_NAME = os.getenv('REGISTRIES_LOGO_IMAGE_NAME', 'bc_logo_for_email.png') - # NATS Config - NATS_SERVERS = os.getenv('NATS_SERVERS', 'nats://127.0.0.1:4222').split(',') - NATS_CLUSTER_ID = os.getenv('NATS_CLUSTER_ID', 'test-cluster') - - # NATS Config for account events - NATS_ACCOUNT_CLIENT_NAME = os.getenv('NATS_ACCOUNT_CLIENT_NAME', 'account.events.worker') - NATS_ACCOUNT_SUBJECT = os.getenv('NATS_ACCOUNT_SUBJECT', 'account.events') - - # NATS Config for transaction events - NATS_PAYMENT_CLIENT_NAME = os.getenv('NATS_PAYMENT_CLIENT_NAME', 'entity.filing.payment.worker') - NATS_PAYMENT_SUBJECT = os.getenv('NATS_PAYMENT_SUBJECT', 'entity.filing.payment') - # Auth API Endpoint AUTH_API_ENDPOINT = f'{os.getenv("AUTH_API_URL")}/' @@ -145,9 +133,6 @@ class _Config(object): # pylint: disable=too-few-public-methods DEBUG = True PAD_CONFIRMATION_PERIOD_IN_DAYS = int(os.getenv('PAD_CONFIRMATION_PERIOD_IN_DAYS', '3')) - NATS_MAILER_CLIENT_NAME = os.getenv('NATS_MAILER_CLIENT_NAME', 'account.mailer.worker') - NATS_MAILER_SUBJECT = os.getenv('NATS_MAILER_SUBJECT', 'account.mailer') - # Secret key for encrypting bank account ACCOUNT_SECRET_KEY = os.getenv('ACCOUNT_SECRET_KEY') @@ -206,6 +191,11 @@ class _Config(object): # pylint: disable=too-few-public-methods EFT_HOLDING_GL = os.getenv('EFT_HOLDING_GL', '') EFT_TRANSFER_DESC = os.getenv('EFT_TRANSFER_DESC', 'BCREGISTRIES {} {} EFT TRANSFER') + # GCP PubSub + AUDIENCE = os.getenv('AUDIENCE', None) + GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) + PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) class DevConfig(_Config): # pylint: disable=too-few-public-methods TESTING = False diff --git a/jobs/payment-jobs/devops/vaults.json b/jobs/payment-jobs/devops/vaults.json index e43ccdbeb..6cd1c8ebf 100644 --- a/jobs/payment-jobs/devops/vaults.json +++ b/jobs/payment-jobs/devops/vaults.json @@ -13,14 +13,6 @@ "sbc-auth-admin" ] }, - { - "vault": "nats", - "application": [ - "base", - "account-events-listener", - "payment" - ] - }, { "vault": "payment-external-services", "application": [ diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index edd377da7..47845a253 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -39,6 +39,7 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production'), job_name='unknown' from pay_api.models import db, ma app = Flask(__name__) + app.env = run_mode app.config.from_object(config.CONFIGURATION[run_mode]) # Configure Sentry @@ -49,7 +50,7 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production'), job_name='unknown' integrations=[FlaskIntegration()], release=f'payment-jobs-{job_name}@-', ) - app.logger.info(f'<<<< Starting Payment Jobs >>>>') + app.logger.info('<<<< Starting Payment Jobs >>>>') db.init_app(app) if init_oracle: oracle_db.init_app(app) diff --git a/jobs/payment-jobs/requirements.txt b/jobs/payment-jobs/requirements.txt index c36f3d540..0210163c4 100644 --- a/jobs/payment-jobs/requirements.txt +++ b/jobs/payment-jobs/requirements.txt @@ -1,11 +1,11 @@ --e git+https://github.com/bcgov/sbc-common-components.git@5807c201b057875f9061f60b408ab765ebaa4235#egg=sbc_common_components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git@e0bc6979b761347b32628a28fa1489e919e5d806#egg=pay_api&subdirectory=pay-api +-e git+https://github.com/bcgov/sbc-common-components.git@5f99e135214ae949c9af951d4aa0b88b1067d853#egg=sbc_common_components&subdirectory=python +-e git+https://github.com/seeker25/sbc-pay.git@5bf7ab87481a96a72c59db7d6a88c32f712f1ab6#egg=pay_api&subdirectory=pay-api -e git+https://github.com/thorwolpert/flask-jwt-oidc.git@40cc811ccf70e838c5f7522fe8d83b7e58853539#egg=flask_jwt_oidc Flask-Caching==2.1.0 Flask-Cors==4.0.0 -Flask-Migrate==2.7.0 +Flask-Migrate==4.0.7 Flask-Moment==1.0.5 -Flask-OpenTracing==2.0.0 +Flask-OpenTracing==1.1.0 Flask-SQLAlchemy==3.1.1 Flask-Script==2.0.6 Flask==3.0.2 @@ -31,26 +31,23 @@ charset-normalizer==3.3.2 click==8.1.7 croniter==2.0.2 cryptography==42.0.5 -cx-Oracle==8.3.0 +cx_Oracle==8.3.0 dataclass-wizard==0.22.3 dpath==2.1.6 ecdsa==0.18.0 -exceptiongroup==1.2.0 expiringdict==1.2.2 flask-marshmallow==1.2.0 google-api-core==2.17.1 google-auth==2.28.1 google-cloud-pubsub==2.20.0 -googleapis-common-protos==1.62.0 +googleapis-common-protos==1.63.0 greenlet==3.0.3 grpc-google-iam-v1==0.13.0 -grpcio-status==1.62.0 -grpcio==1.62.0 +grpcio-status==1.62.1 +grpcio==1.62.1 gunicorn==21.2.0 holidays==0.37 idna==3.6 -importlib_metadata==7.0.2 -importlib_resources==6.1.3 itsdangerous==2.1.2 jaeger-client==4.8.0 jsonschema==4.17.3 @@ -61,9 +58,8 @@ marshmallow==3.21.1 minio==7.2.5 more-itertools==10.2.0 opentracing==2.4.0 -packaging==23.2 +packaging==24.0 paramiko==3.4.0 -pkgutil_resolve_name==1.3.10 proto-plus==1.23.0 protobuf==4.25.3 psycopg2-binary==2.9.9 @@ -72,6 +68,7 @@ pyasn1-modules==0.3.0 pyasn1==0.5.1 pycparser==2.21 pycryptodome==3.20.0 +pyhumps==3.8.0 pyrsistent==0.20.0 pysftp==0.2.9 python-dateutil==2.9.0.post0 @@ -90,4 +87,3 @@ thrift==0.16.0 tornado==6.4 typing_extensions==4.10.0 urllib3==2.2.1 -zipp==3.17.0 diff --git a/jobs/payment-jobs/requirements/bcregistry-libraries.txt b/jobs/payment-jobs/requirements/bcregistry-libraries.txt index ba230981f..06d616dd6 100644 --- a/jobs/payment-jobs/requirements/bcregistry-libraries.txt +++ b/jobs/payment-jobs/requirements/bcregistry-libraries.txt @@ -1,3 +1,4 @@ -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git@main#egg=pay_api&subdirectory=pay-api +-e git+https://github.com/seeker25/sbc-pay.git@18263#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/thorwolpert/flask-jwt-oidc.git#egg=flask-jwt-oidc git+https://github.com/daxiom/simple-cloudevent.py.git diff --git a/jobs/payment-jobs/requirements/dev.txt b/jobs/payment-jobs/requirements/dev.txt index 0fe0a755f..f84689dbe 100644 --- a/jobs/payment-jobs/requirements/dev.txt +++ b/jobs/payment-jobs/requirements/dev.txt @@ -10,7 +10,7 @@ pytest-cov Faker # Lint and code style -flake8==5.0.4 +flake8 flake8-blind-except flake8-debugger flake8-docstrings diff --git a/jobs/payment-jobs/requirements/prod.txt b/jobs/payment-jobs/requirements/prod.txt index bbe762038..06a70bb0d 100644 --- a/jobs/payment-jobs/requirements/prod.txt +++ b/jobs/payment-jobs/requirements/prod.txt @@ -1,19 +1,19 @@ gunicorn Flask Flask-SQLAlchemy -SQLAlchemy<1.4 -flask-marshmallow==0.11.0 -marshmallow-sqlalchemy==0.25.0 +SQLAlchemy +flask-marshmallow +marshmallow-sqlalchemy python-dotenv psycopg2-binary jsonschema==4.17.3 requests -Werkzeug<2 +Werkzeug jaeger-client minio pysftp -Flask-Migrate<3 -itsdangerous==2.0.1 +Flask-Migrate +itsdangerous dataclass_wizard launchdarkly-server-sdk cx_Oracle diff --git a/jobs/payment-jobs/services/oracle.py b/jobs/payment-jobs/services/oracle.py index 08c52095a..6ffc99d80 100644 --- a/jobs/payment-jobs/services/oracle.py +++ b/jobs/payment-jobs/services/oracle.py @@ -16,8 +16,8 @@ These will get initialized by the application. """ import cx_Oracle -from flask import _app_ctx_stack, current_app - +from flask import current_app +from flask.globals import app_ctx class OracleDB: """Oracle database connection object for re-use in application.""" @@ -40,7 +40,7 @@ def init_app(self, app): def teardown(ctx=None): """Oracle session pool cleans up after itself.""" if not ctx: - ctx = _app_ctx_stack.top + ctx = app_ctx if hasattr(ctx, '_oracle_pool'): ctx._oracle_pool.close() # pylint: disable=protected-access @@ -81,7 +81,7 @@ def connection(self): # pylint: disable=inconsistent-return-statements and then return an acquired session :return: cx_Oracle.connection type """ - ctx = _app_ctx_stack.top + ctx = app_ctx if ctx is not None: if not hasattr(ctx, '_oracle_pool'): ctx._oracle_pool = self._create_pool() # pylint: disable = protected-access; need this method diff --git a/jobs/payment-jobs/setup.cfg b/jobs/payment-jobs/setup.cfg index fa9785f04..f4f4f4e4e 100755 --- a/jobs/payment-jobs/setup.cfg +++ b/jobs/payment-jobs/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index 939e87b0f..62e128ca3 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -21,7 +21,7 @@ from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.services.cfs_service import CFSService from pay_api.services.oauth_service import OAuthService -from pay_api.utils.constants import RECEIPT_METHOD_PAD_DAILY, RECEIPT_METHOD_EFT_MONTHLY +from pay_api.utils.constants import RECEIPT_METHOD_EFT_MONTHLY, RECEIPT_METHOD_PAD_DAILY from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, PaymentMethod from sentry_sdk import capture_message from services import routing_slip @@ -73,13 +73,6 @@ def _get_account_contact(cls, auth_token: str, auth_account_id: str): @classmethod def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: PaymentAccountModel, auth_token: str): - # If PAD Account creation in CFS is paused, then just continue - # TODO Remove once PAD account bugs are fixed and stable on CAS side. - if current_app.config.get('CFS_STOP_PAD_ACCOUNT_CREATION') and \ - pay_account.payment_method == PaymentMethod.PAD.value: - current_app.logger.info('Continuing to next record as CFS PAD account creation is stopped.') - return - current_app.logger.info( f'Creating pay system instance for {pay_account.payment_method} for account {pay_account.id}.') diff --git a/jobs/payment-jobs/tasks/eft_transfer_task.py b/jobs/payment-jobs/tasks/eft_transfer_task.py index 8b62c952e..d6f9bdfe9 100644 --- a/jobs/payment-jobs/tasks/eft_transfer_task.py +++ b/jobs/payment-jobs/tasks/eft_transfer_task.py @@ -84,18 +84,19 @@ def get_invoices_for_refund_reversal(payment_account_id: int): @staticmethod def get_account_ids() -> List[int]: """Return account IDs for EFT payments.""" - return db.session.query(func.DISTINCT(InvoiceModel.payment_account_id)) \ + query = db.session.query(func.DISTINCT(InvoiceModel.payment_account_id)) \ .filter(InvoiceModel.invoice_status_code == InvoiceStatus.PAID.value) \ .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ .filter(~exists().where((EFTGLTransferModel.invoice_id == InvoiceModel.id) & - (EFTGLTransferModel.transfer_type == EFTGlTransferType.TRANSFER.value))).all() + (EFTGLTransferModel.transfer_type == EFTGlTransferType.TRANSFER.value))) + return db.session.scalars(query).all() @staticmethod def create_eft_gl_transfer(eft_holding_gl: str, line_distribution_gl: str, transfer_type: str, line_item: PaymentLineItemModel, payment_account: PaymentAccountModel): """Create EFT GL Transfer record.""" short_name_id = db.session.query(EFTShortnameModel.id) \ - .filter(EFTShortnameModel.auth_account_id == payment_account.auth_account_id).one() + .filter(EFTShortnameModel.auth_account_id == payment_account.auth_account_id).one()[0] source_gl = eft_holding_gl if transfer_type == EFTGlTransferType.TRANSFER.value else line_distribution_gl target_gl = line_distribution_gl if transfer_type == EFTGlTransferType.TRANSFER.value else eft_holding_gl now = datetime.now() @@ -112,8 +113,8 @@ def create_eft_gl_transfer(eft_holding_gl: str, line_distribution_gl: str, trans ) @classmethod - def _process_eft_transfer_invoices(cls, invoices: [InvoiceModel], transfer_type: str, - eft_gl_transfers: dict = None) -> [EFTGLTransferModel]: + def _process_eft_transfer_invoices(cls, invoices: List[InvoiceModel], transfer_type: str, + eft_gl_transfers: dict = None) -> List[EFTGLTransferModel]: """Create EFT GL Transfer for invoice line items.""" eft_holding_gl = current_app.config.get('EFT_HOLDING_GL') eft_gl_transfers = eft_gl_transfers or {} @@ -165,7 +166,7 @@ def _process_eft_transfer_invoices(cls, invoices: [InvoiceModel], transfer_type: return eft_gl_transfers @staticmethod - def process_invoice_ejv_links(invoices: [InvoiceModel], ejv_header_model_id: int): + def process_invoice_ejv_links(invoices: List[InvoiceModel], ejv_header_model_id: int): """Create EJV Invoice Links.""" current_app.logger.info('Creating ejv invoice link records and setting invoice status.') sequence = 1 @@ -223,7 +224,7 @@ def _create_ejv_file_for_eft_transfer(cls): # pylint:disable=too-many-locals, t total: float = 0 current_app.logger.info(f'Processing EFT Transfers for account_id: {account_id}.') - account_transfers: List[EFTGLTransferModel] = transfers[account_id[0]] + account_transfers: List[EFTGLTransferModel] = transfers[account_id] for eft_transfer in account_transfers: invoice_number = f'#{eft_transfer.invoice_id}' diff --git a/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py b/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py index 0fde9b178..cdb0421b5 100644 --- a/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py +++ b/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py @@ -251,22 +251,24 @@ def _get_partners_by_batch_type(cls, batch_type) -> List[CorpTypeModel]: if batch_type == 'GA': # Rule for GA. Credit is 112 and debit is 112. - partner_distribution_code_ids: List[int] = query.filter( + partner_distribution_code_ids: List[int] = db.session.scalars(query.filter( DistributionCodeModel.client == bc_reg_client_code - ).all() + )).all() else: # Rule for GI. Debit is 112 and credit is not 112. - partner_distribution_code_ids: List[int] = query.filter( + partner_distribution_code_ids: List[int] = db.session.scalars(query.filter( DistributionCodeModel.client != bc_reg_client_code - ).all() + )).all() # Find all distribution codes who have these partner distribution codes as disbursement. - fee_distribution_codes: List[int] = db.session.query(DistributionCodeModel.distribution_code_id).filter( - DistributionCodeModel.disbursement_distribution_code_id.in_(partner_distribution_code_ids)).all() + fee_query = db.session.query(DistributionCodeModel.distribution_code_id).filter( + DistributionCodeModel.disbursement_distribution_code_id.in_(partner_distribution_code_ids)) + fee_distribution_codes: List[int] = db.session.scalars(fee_query).all() - corp_type_codes: List[str] = db.session.query(FeeScheduleModel.corp_type_code). \ + corp_type_query = db.session.query(FeeScheduleModel.corp_type_code). \ join(DistributionCodeLinkModel, - DistributionCodeLinkModel.fee_schedule_id == FeeScheduleModel.fee_schedule_id). \ - filter(DistributionCodeLinkModel.distribution_code_id.in_(fee_distribution_codes)).all() + DistributionCodeLinkModel.fee_schedule_id == FeeScheduleModel.fee_schedule_id).\ + filter(DistributionCodeLinkModel.distribution_code_id.in_(fee_distribution_codes)) + corp_type_codes: List[str] = db.session.scalars(corp_type_query).all() return db.session.query(CorpTypeModel).filter(CorpTypeModel.code.in_(corp_type_codes)).all() diff --git a/jobs/payment-jobs/tasks/ejv_payment_task.py b/jobs/payment-jobs/tasks/ejv_payment_task.py index 55c15f716..d53d2143b 100644 --- a/jobs/payment-jobs/tasks/ejv_payment_task.py +++ b/jobs/payment-jobs/tasks/ejv_payment_task.py @@ -226,18 +226,18 @@ def _get_account_ids_for_payment(cls, batch_type) -> List[int]: """Return account IDs for payment.""" # CREDIT : Distribution code against fee schedule # DEBIT : Distribution code against account. - bc_reg_client_code = current_app.config.get('CGI_BCREG_CLIENT_CODE') # 112 #TODO + bc_reg_client_code = current_app.config.get('CGI_BCREG_CLIENT_CODE') query = db.session.query(DistributionCodeModel.account_id) \ .filter(DistributionCodeModel.stop_ejv.is_(False) | DistributionCodeModel.stop_ejv.is_(None)) \ .filter(DistributionCodeModel.account_id.isnot(None)) if batch_type == 'GA': # Rule for GA. Credit is 112 and debit is 112. For BCREG client code is 112 - account_ids: List[int] = query.filter(DistributionCodeModel.client == bc_reg_client_code).all() + account_ids: List[int] = query.filter(DistributionCodeModel.client == bc_reg_client_code) else: # Rule for GI. Credit is 112 and debit is not 112. For BCREG client code is 112 - account_ids: List[int] = query.filter(DistributionCodeModel.client != bc_reg_client_code).all() - return account_ids + account_ids: List[int] = query.filter(DistributionCodeModel.client != bc_reg_client_code) + return db.session.scalars(account_ids).all() @classmethod def _get_invoices_for_payment(cls, account_id: int) -> List[InvoiceModel]: diff --git a/jobs/payment-jobs/tests/docker/docker-compose.yml b/jobs/payment-jobs/tests/docker/docker-compose.yml index 88aa6a242..dbffdb7ae 100644 --- a/jobs/payment-jobs/tests/docker/docker-compose.yml +++ b/jobs/payment-jobs/tests/docker/docker-compose.yml @@ -22,19 +22,6 @@ services: retries: 10 volumes: - ./setup:/tmp/keycloak/test/ - nats: - image: nats-streaming - restart: always - expose: - - 4222 - - 8222 - labels: - - entity.services=nats - ports: - - 4222:4222 - - 8222:8222 - tty: true - proxy: image: nginx:alpine volumes: diff --git a/jobs/payment-jobs/tests/docker/nginx.conf b/jobs/payment-jobs/tests/docker/nginx.conf index d88123d4f..ad848249b 100644 --- a/jobs/payment-jobs/tests/docker/nginx.conf +++ b/jobs/payment-jobs/tests/docker/nginx.conf @@ -24,7 +24,7 @@ http { set $last_path_component example1; rewrite ^/reports-api/api/v1/(.*) /$1 break; proxy_set_header Prefer example=$last_path_component; - proxy_set_header Accept "application/json"; #TODO + proxy_set_header Accept "application/json"; proxy_pass http://reports:4010/; } diff --git a/jobs/payment-jobs/tests/jobs/conftest.py b/jobs/payment-jobs/tests/jobs/conftest.py index c496ae842..cb217630a 100644 --- a/jobs/payment-jobs/tests/jobs/conftest.py +++ b/jobs/payment-jobs/tests/jobs/conftest.py @@ -22,7 +22,7 @@ from flask_migrate import Migrate, upgrade from pay_api.models import db as _db from sqlalchemy import event, text -from sqlalchemy.schema import DropConstraint, MetaData +from sqlalchemy_utils import create_database, database_exists, drop_database from invoke_jobs import create_app from utils.logger import setup_logging @@ -31,17 +31,13 @@ @pytest.fixture(scope='session') def app(): """Return a session-wide application configured in TEST mode.""" - _app = create_app('testing') - - return _app + return create_app('testing') @pytest.fixture(scope='function') def app_request(): """Return a session-wide application configured in TEST mode.""" - _app = create_app('testing') - - return _app + return create_app('testing') @pytest.fixture(scope='session') @@ -57,107 +53,60 @@ def client_ctx(app): yield _client -@pytest.fixture(scope='session') +@pytest.fixture(scope='session', autouse=True) def db(app): # pylint: disable=redefined-outer-name, invalid-name - """Return a session-wide initialised database. - - Drops all existing tables - Meta follows Postgres FKs - """ + """Return a session-wide initialised database.""" with app.app_context(): - # Clear out views - view_sql = """SELECT table_name FROM information_schema.views - WHERE table_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(view_sql))]: - try: - sess.execute(text('DROP VIEW public.%s ;' % seq)) - print('DROP VIEW public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # Clear out any existing tables - metadata = MetaData(_db.engine) - metadata.reflect() - for table in metadata.tables.values(): - for fk in table.foreign_keys: # pylint: disable=invalid-name - _db.engine.execute(DropConstraint(fk.constraint)) - metadata.drop_all() - _db.drop_all() - - sequence_sql = """SELECT sequence_name FROM information_schema.sequences - WHERE sequence_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(sequence_sql))]: - try: - sess.execute(text('DROP SEQUENCE public.%s ;' % seq)) - print('DROP SEQUENCE public.%s ' % seq) - except Exception as err: # NOQA # pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # ############################################ - # There are 2 approaches, an empty database, or the same one that the app will use - # create the tables - # _db.create_all() - # or - # Use Alembic to load all of the DB revisions including supporting lookup data - # This is the path we'll use in legal_api!! - # even though this isn't referenced directly, it sets up the internal configs that upgrade needs migrations_path = [folder for folder in sys.path if 'pay-api/pay-api' in folder] if len(migrations_path) > 0: migrations_path = migrations_path[0].replace('/pay-api/src', '/pay-api/migrations') - # Fix for windows. - else: - migrations_path = os.path.abspath('../../pay-api/migrations') + if database_exists(_db.engine.url): + drop_database(_db.engine.url) + create_database(_db.engine.url) + _db.session().execute(text('SET TIME ZONE "UTC";')) Migrate(app, _db, directory=migrations_path) upgrade() - # Restore the logging, alembic and sqlalchemy have their own logging from alembic.ini. setup_logging(os.path.abspath('logging.conf')) return _db -@pytest.fixture(scope='function') -def session(app, db): # pylint: disable=redefined-outer-name, invalid-name +@pytest.fixture(scope='function', autouse=True) +def session(db, app): # pylint: disable=redefined-outer-name, invalid-name """Return a function-scoped session.""" with app.app_context(): - conn = db.engine.connect() - txn = conn.begin() - - options = dict(bind=conn, binds={}) - sess = db.create_scoped_session(options=options) - - # establish a SAVEPOINT just before beginning the test - # (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) - sess.begin_nested() - - @event.listens_for(sess(), 'after_transaction_end') - def restart_savepoint(sess2, trans): # pylint: disable=unused-variable - # Detecting whether this is indeed the nested transaction of the test - if trans.nested and not trans._parent.nested: # pylint: disable=protected-access - # Handle where test DOESN'T session.commit(), - sess2.expire_all() - sess.begin_nested() + with db.engine.connect() as conn: + transaction = conn.begin() + sess = db._make_scoped_session(dict(bind=conn)) # pylint: disable=protected-access + # Establish SAVEPOINT (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) + nested = sess.begin_nested() + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback + + @event.listens_for(sess, 'after_transaction_end') + def restart_savepoint(sess2, trans): # pylint: disable=unused-variable + nonlocal nested + if trans.nested: + # Handle where test DOESN'T session.commit() + sess2.expire_all() + nested = sess.begin_nested() + # When using a SAVEPOINT via the Session.begin_nested() or Connection.begin_nested() methods, + # the transaction object returned must be used to commit or rollback the SAVEPOINT. + # Calling the Session.commit() or Connection.commit() methods will always commit the + # outermost transaction; this is a SQLAlchemy 2.0 specific behavior that is + # reversed from the 1.x series + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback - db.session = sess - - sql = text('select 1') - sess.execute(sql) - - yield sess - - # Cleanup - sess.remove() - # This instruction rollsback any commit that were executed in the tests. - txn.rollback() - conn.close() + try: + yield db.session + finally: + db.session.remove() + transaction.rollback() @pytest.fixture(scope='session', autouse=True) @@ -171,7 +120,6 @@ def auto(docker_services, app): docker_services.start('paybc') docker_services.start('reports') docker_services.start('proxy') - docker_services.start('nats') docker_services.start('sftp') time.sleep(2) @@ -179,7 +127,6 @@ def auto(docker_services, app): @pytest.fixture(scope='session') def docker_compose_files(pytestconfig): """Get the docker-compose.yml absolute path.""" - import os return [ os.path.join(str(pytestconfig.rootdir), 'tests/docker', 'docker-compose.yml') ] diff --git a/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py b/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py index af72cf3ba..6c551f1ec 100644 --- a/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py +++ b/jobs/payment-jobs/tests/jobs/test_cfs_create_account_task.py @@ -28,7 +28,7 @@ from tasks.cfs_create_account_task import CreateAccountTask from utils import mailer -from .factory import factory_create_online_banking_account, factory_create_pad_account, factory_create_eft_account +from .factory import factory_create_eft_account, factory_create_online_banking_account, factory_create_pad_account def test_create_account_setup(session): @@ -50,7 +50,7 @@ def test_create_pad_account(session): assert cfs_account.cfs_site assert cfs_account.cfs_account assert cfs_account.payment_instrument_number - + def test_create_eft_account(session): """Test create account.""" diff --git a/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py b/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py index 09a3d6254..0cd14d431 100644 --- a/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py +++ b/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py @@ -19,12 +19,9 @@ from datetime import datetime from typing import List -import pytest -from flask import Flask from pay_api.models import DistributionCode, EFTGLTransfer, EjvFile, EjvHeader, EjvInvoiceLink, FeeSchedule, Invoice, db from pay_api.utils.enums import DisbursementStatus, EFTGlTransferType, EjvFileType, InvoiceStatus, PaymentMethod -import config from tasks.eft_transfer_task import EftTransferTask from .factory import ( @@ -32,20 +29,7 @@ factory_payment_line_item) -app = None - - -@pytest.fixture -def setup(): - """Initialize app with test env for testing.""" - global app - app = Flask(__name__) - app.env = 'testing' - app.config.from_object(config.CONFIGURATION['testing']) - app.config['EFT_HOLDING_GL'] = '1128888888888888888000000000000000' - - -def test_eft_transfer(setup, session, monkeypatch): +def test_eft_transfer(app, session, monkeypatch): """Test EFT Holdings GL Transfer for EFT invoices. Steps: @@ -77,6 +61,7 @@ def test_eft_transfer(setup, session, monkeypatch): dist_code.service_fee_distribution_code_id = service_fee_dist_code.distribution_code_id dist_code.save() + app.config['EFT_HOLDING_GL'] = '1128888888888888888000000000000000' eft_holding_gl = app.config['EFT_HOLDING_GL'] distribution_gl = EftTransferTask.get_distribution_string(dist_code).strip() service_fee_gl = EftTransferTask.get_distribution_string(service_fee_dist_code).strip() @@ -100,8 +85,7 @@ def test_eft_transfer(setup, session, monkeypatch): fee_dist_id=dist_code.distribution_code_id) invoices.append(inv) - with app.app_context(): - EftTransferTask.create_ejv_file() + EftTransferTask.create_ejv_file() # Lookup invoice and assert disbursement status for invoice in invoices: diff --git a/jobs/payment-jobs/tests/jobs/test_generate_statements.py b/jobs/payment-jobs/tests/jobs/test_generate_statements.py index df9faeaa5..5f31a016b 100644 --- a/jobs/payment-jobs/tests/jobs/test_generate_statements.py +++ b/jobs/payment-jobs/tests/jobs/test_generate_statements.py @@ -16,7 +16,7 @@ Test-Suite to ensure that the UpdateStalePayment is working as expected. """ -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta import pytz from freezegun import freeze_time @@ -133,7 +133,6 @@ def test_bcol_weekly_to_eft_statement(session): total=50) assert weekly_invoice is not None - assert weekly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) statement_from_date = localize_date(datetime(2023, 10, 8, 12, 0)) statement_to_date = localize_date(datetime(2023, 10, 12, 12, 0)) @@ -179,7 +178,6 @@ def test_bcol_weekly_to_eft_statement(session): total=50) assert monthly_invoice is not None - assert monthly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) # Regenerate monthly statement using date override - it will clean up the previous empty monthly statement first StatementTask.generate_statements((generate_date - timedelta(days=1)).strftime('%Y-%m-%d')) @@ -212,7 +210,6 @@ def test_bcol_monthly_to_eft_statement(session): total=50) assert bcol_invoice is not None - assert bcol_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) statement_from_date = localize_date(datetime(2023, 10, 1, 12, 0)) statement_to_date = localize_date(datetime(2023, 10, 30, 12, 0)) @@ -259,7 +256,6 @@ def test_bcol_monthly_to_eft_statement(session): total=50) assert monthly_invoice is not None - assert monthly_invoice.created_on == invoice_create_date.astimezone(timezone.utc).replace(tzinfo=None) # Regenerate monthly statement using date override - it will clean up the previous empty monthly statement first StatementTask.generate_statements((generate_date - timedelta(days=1)).strftime('%Y-%m-%d')) diff --git a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py index 48dcc1ffe..9b3f596d8 100644 --- a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py +++ b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py @@ -113,39 +113,37 @@ def test_send_unpaid_statement_notification(setup, session): summary = Statement.get_summary(account.auth_account_id, statements[0][0].id) total_amount_owing = summary['total_due'] - with app.app_context(): - # Assert notification was published to the mailer queue - with patch('tasks.statement_due_task.publish_payment_notification') as mock_mailer: - # Freeze time to due date - trigger due notification - with freeze_time(last_day): - StatementDueTask.process_unpaid_statements() - mock_mailer.assert_called_with(StatementNotificationInfo(auth_account_id=account.auth_account_id, - statement=statements[0][0], - is_due=True, - due_date=last_day.date(), - emails=statement_recipient.email, - total_amount_owing=total_amount_owing)) - - # Freeze time to due date - trigger reminder notification - with freeze_time(last_day - timedelta(days=7)): - StatementDueTask.process_unpaid_statements() - mock_mailer.assert_called_with(StatementNotificationInfo(auth_account_id=account.auth_account_id, - statement=statements[0][0], - is_due=False, - due_date=last_day.date(), - emails=statement_recipient.email, - total_amount_owing=total_amount_owing)) + # Assert notification was published to the mailer queue + with patch('tasks.statement_due_task.publish_payment_notification') as mock_mailer: + # Freeze time to due date - trigger due notification + with freeze_time(last_day): + StatementDueTask.process_unpaid_statements() + mock_mailer.assert_called_with(StatementNotificationInfo(auth_account_id=account.auth_account_id, + statement=statements[0][0], + is_due=True, + due_date=last_day.date(), + emails=statement_recipient.email, + total_amount_owing=total_amount_owing)) + + # Freeze time to due date - trigger reminder notification + with freeze_time(last_day - timedelta(days=7)): + StatementDueTask.process_unpaid_statements() + mock_mailer.assert_called_with(StatementNotificationInfo(auth_account_id=account.auth_account_id, + statement=statements[0][0], + is_due=False, + due_date=last_day.date(), + emails=statement_recipient.email, + total_amount_owing=total_amount_owing)) def test_unpaid_statement_notification_not_sent(setup, session): """Assert payment reminder event is not being sent.""" - with app.app_context(): - # Assert notification was published to the mailer queue - with patch('tasks.statement_due_task.publish_payment_notification') as mock_mailer: - # Freeze time to 10th of the month - should not trigger any notification - with freeze_time(current_local_time().replace(day=10)): - StatementDueTask.process_unpaid_statements() - mock_mailer.assert_not_called() + # Assert notification was published to the mailer queue + with patch('tasks.statement_due_task.publish_payment_notification') as mock_mailer: + # Freeze time to 10th of the month - should not trigger any notification + with freeze_time(current_local_time().replace(day=10)): + StatementDueTask.process_unpaid_statements() + mock_mailer.assert_not_called() def test_overdue_invoices_updated(setup, session): @@ -172,9 +170,8 @@ def test_overdue_invoices_updated(setup, session): assert invoice2.invoice_status_code == InvoiceStatus.CREATED.value assert account.payment_method == PaymentMethod.EFT.value - with app.app_context(): - # Freeze time to 1st of the month - should trigger overdue status update for previous month invoices - with freeze_time(current_local_time().replace(day=1)): - StatementDueTask.process_unpaid_statements() - assert invoice.invoice_status_code == InvoiceStatus.OVERDUE.value - assert invoice2.invoice_status_code == InvoiceStatus.CREATED.value + # Freeze time to 1st of the month - should trigger overdue status update for previous month invoices + with freeze_time(current_local_time().replace(day=1)): + StatementDueTask.process_unpaid_statements() + assert invoice.invoice_status_code == InvoiceStatus.OVERDUE.value + assert invoice2.invoice_status_code == InvoiceStatus.CREATED.value diff --git a/jobs/payment-jobs/tests/jobs/test_statement_notification_task.py b/jobs/payment-jobs/tests/jobs/test_statement_notification_task.py index 4281dafac..750444122 100644 --- a/jobs/payment-jobs/tests/jobs/test_statement_notification_task.py +++ b/jobs/payment-jobs/tests/jobs/test_statement_notification_task.py @@ -91,46 +91,45 @@ def test_send_notifications(session): ]) def test_send_monthly_notifications(setup, session, payment_method_code): # pylint: disable=unused-argument """Test send monthly statement notifications.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(payment_method_code, - previous_month_year, - StatementFrequency.MONTHLY.value) - - assert invoice.payment_method_code == payment_method_code - assert account.payment_method == payment_method_code - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification send_email was invoked - with patch.object(StatementNotificationTask, 'send_email', return_value=True) as mock_mailer: - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - # Assert token and email recipient - mock any for HTML generated - mock_mailer.assert_called_with(mock_get_token.return_value, statement_recipient.email, ANY) - - # Assert statement notification code indicates success - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.SUCCESS.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(payment_method_code, + previous_month_year, + StatementFrequency.MONTHLY.value) + + assert invoice.payment_method_code == payment_method_code + assert account.payment_method == payment_method_code + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification send_email was invoked + with patch.object(StatementNotificationTask, 'send_email', return_value=True) as mock_mailer: + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + # Assert token and email recipient - mock any for HTML generated + mock_mailer.assert_called_with(mock_get_token.return_value, statement_recipient.email, ANY) + + # Assert statement notification code indicates success + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.SUCCESS.value @pytest.mark.parametrize('payment_method_code', [ @@ -144,174 +143,170 @@ def test_send_monthly_notifications(setup, session, payment_method_code): # pyl ]) def test_send_monthly_notifications_failed(setup, session, payment_method_code): # pylint: disable=unused-argument """Test send monthly statement notifications failure.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(payment_method_code, - previous_month_year, - StatementFrequency.MONTHLY.value) - - assert invoice.payment_method_code == payment_method_code - assert account.payment_method == payment_method_code - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification send_email was invoked - with patch.object(StatementNotificationTask, 'send_email', return_value=False) as mock_mailer: - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - # Assert token and email recipient - mock any for HTML generated - mock_mailer.assert_called_with(mock_get_token.return_value, statement_recipient.email, ANY) - - # Assert statement notification code indicates failed - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.FAILED.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(payment_method_code, + previous_month_year, + StatementFrequency.MONTHLY.value) + + assert invoice.payment_method_code == payment_method_code + assert account.payment_method == payment_method_code + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification send_email was invoked + with patch.object(StatementNotificationTask, 'send_email', return_value=False) as mock_mailer: + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + # Assert token and email recipient - mock any for HTML generated + mock_mailer.assert_called_with(mock_get_token.return_value, statement_recipient.email, ANY) + + # Assert statement notification code indicates failed + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.FAILED.value def test_send_eft_notifications(setup, session): # pylint: disable=unused-argument """Test send monthly EFT statement notifications.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, - previous_month_year, - StatementFrequency.MONTHLY.value, - 351.50) - - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert account.payment_method == PaymentMethod.EFT.value - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification was published to the mailer queue - with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - mock_mailer.assert_called_once_with(account, statements[0][0], 351.5, statement_recipient.email) - - # Assert statement notification code indicates success - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.SUCCESS.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, + previous_month_year, + StatementFrequency.MONTHLY.value, + 351.50) + + assert invoice.payment_method_code == PaymentMethod.EFT.value + assert account.payment_method == PaymentMethod.EFT.value + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification was published to the mailer queue + with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + mock_mailer.assert_called_once_with(account, statements[0][0], 351.5, statement_recipient.email) + + # Assert statement notification code indicates success + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.SUCCESS.value def test_send_eft_notifications_failure(setup, session): # pylint: disable=unused-argument """Test send monthly EFT statement notifications failure.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, - previous_month_year, - StatementFrequency.MONTHLY.value, - 351.50) - - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert account.payment_method == PaymentMethod.EFT.value - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification was published to the mailer queue - with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: - mock_mailer.side_effect = Exception('Mock Exception') - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - mock_mailer.assert_called_once_with(account, statements[0][0], 351.5, statement_recipient.email) - - # Assert statement notification code indicates failed - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.FAILED.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, + previous_month_year, + StatementFrequency.MONTHLY.value, + 351.50) + + assert invoice.payment_method_code == PaymentMethod.EFT.value + assert account.payment_method == PaymentMethod.EFT.value + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification was published to the mailer queue + with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: + mock_mailer.side_effect = Exception('Mock Exception') + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + mock_mailer.assert_called_once_with(account, statements[0][0], 351.5, statement_recipient.email) + + # Assert statement notification code indicates failed + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.FAILED.value def test_send_eft_notifications_ff_disabled(setup, session): # pylint: disable=unused-argument """Test send monthly EFT statement notifications failure.""" - with app.app_context(): - # create statement, invoice, payment data for previous month - last_month, last_year = get_previous_month_and_year() - previous_month_year = datetime(last_year, last_month, 5) - account, invoice, inv_ref, payment, \ - statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, - previous_month_year, - StatementFrequency.MONTHLY.value, - 351.50) - - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert account.payment_method == PaymentMethod.EFT.value - - # Generate statement for previous month - freeze time to the 1st of the current month - with freeze_time(datetime.now().replace(day=1)): - StatementTask.generate_statements() - - # Assert statements and invoice was created - statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, - limit=100) - assert statements is not None - assert len(statements) == 2 # items results and page total - assert len(statements[0]) == 1 # items - invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) - assert invoices is not None - assert invoices[0].invoice_id == invoice.id - - # Assert notification was published to the mailer queue - with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: - with patch('tasks.statement_notification_task.get_token') as mock_get_token: - with patch('tasks.statement_notification_task.flags.is_on', return_value=False): - mock_get_token.return_value = 'mock_token' - StatementNotificationTask.send_notifications() - mock_get_token.assert_called_once() - mock_mailer.assert_not_called() - - # Assert statement notification code indicates skipped - statement: Statement = Statement.find_by_id(statements[0][0].id) - assert statement is not None - assert statement.notification_status_code == NotificationStatus.SKIP.value + # create statement, invoice, payment data for previous month + last_month, last_year = get_previous_month_and_year() + previous_month_year = datetime(last_year, last_month, 5) + account, invoice, inv_ref, payment, \ + statement_recipient, statement_settings = create_test_data(PaymentMethod.EFT.value, + previous_month_year, + StatementFrequency.MONTHLY.value, + 351.50) + + assert invoice.payment_method_code == PaymentMethod.EFT.value + assert account.payment_method == PaymentMethod.EFT.value + + # Generate statement for previous month - freeze time to the 1st of the current month + with freeze_time(datetime.now().replace(day=1)): + StatementTask.generate_statements() + + # Assert statements and invoice was created + statements = Statement.find_all_statements_for_account(auth_account_id=account.auth_account_id, page=1, + limit=100) + assert statements is not None + assert len(statements) == 2 # items results and page total + assert len(statements[0]) == 1 # items + invoices = StatementInvoices.find_all_invoices_for_statement(statements[0][0].id) + assert invoices is not None + assert invoices[0].invoice_id == invoice.id + + # Assert notification was published to the mailer queue + with patch('tasks.statement_notification_task.publish_statement_notification') as mock_mailer: + with patch('tasks.statement_notification_task.get_token') as mock_get_token: + with patch('tasks.statement_notification_task.flags.is_on', return_value=False): + mock_get_token.return_value = 'mock_token' + StatementNotificationTask.send_notifications() + mock_get_token.assert_called_once() + mock_mailer.assert_not_called() + + # Assert statement notification code indicates skipped + statement: Statement = Statement.find_by_id(statements[0][0].id) + assert statement is not None + assert statement.notification_status_code == NotificationStatus.SKIP.value diff --git a/jobs/payment-jobs/utils/logger.py b/jobs/payment-jobs/utils/logger.py index 8b88ddcf2..8568f87dd 100755 --- a/jobs/payment-jobs/utils/logger.py +++ b/jobs/payment-jobs/utils/logger.py @@ -18,12 +18,7 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) print(f'Configure logging, from conf:{conf}', file=sys.stdout) diff --git a/jobs/payment-jobs/utils/mailer.py b/jobs/payment-jobs/utils/mailer.py index 439cfd740..1379db03c 100644 --- a/jobs/payment-jobs/utils/mailer.py +++ b/jobs/payment-jobs/utils/mailer.py @@ -20,7 +20,9 @@ from pay_api.models import FeeSchedule as FeeScheduleModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Statement as StatementModel -from pay_api.services.queue_publisher import publish_response +from pay_api.services import gcp_queue_publisher +from pay_api.services.gcp_queue_publisher import QueueMessage +from pay_api.utils.enums import QueueSources, MessageType from sentry_sdk import capture_message @@ -37,30 +39,26 @@ class StatementNotificationInfo: def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, - additional_params: Dict = {}): + additional_params: Dict = {}): """Publish payment message to the mailer queue.""" # Publish message to the Queue, saying account has been activated. Using the event spec. fee_schedule: FeeScheduleModel = FeeScheduleModel.find_by_filing_type_and_corp_type(corp_type_code='BCR', filing_type_code='NSF') - payload = { - 'specversion': '1.x-wip', - 'type': f'bc.registry.payment.{message_type}', - 'source': f'https://api.pay.bcregistry.gov.bc.ca/v1/accounts/{pay_account.auth_account_id}', - 'id': f'{pay_account.auth_account_id}', - 'time': f'{datetime.now()}', - 'datacontenttype': 'application/json', - 'data': { - 'accountId': pay_account.auth_account_id, - 'nsfFee': float(fee_schedule.fee.amount), - **additional_params - } + 'accountId': pay_account.auth_account_id, + 'nsfFee': float(fee_schedule.fee.amount), + **additional_params } try: - publish_response(payload=payload, - client_name=current_app.config.get('NATS_MAILER_CLIENT_NAME'), - subject=current_app.config.get('NATS_MAILER_SUBJECT')) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_JOBS.value, + message_type=message_type, + payload=payload, + topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') + ) + ) except Exception as e: # pylint: disable=broad-except current_app.logger.error(e) current_app.logger.warning('Notification to Queue failed for the Account Mailer %s - %s', @@ -74,25 +72,22 @@ def publish_statement_notification(pay_account: PaymentAccountModel, statement: total_amount_owing: float, emails: str) -> bool: """Publish payment statement notification message to the mailer queue.""" payload = { - 'specversion': '1.x-wip', - 'type': f'bc.registry.payment.statementNotification', - 'source': f'https://api.pay.bcregistry.gov.bc.ca/v1/accounts/{pay_account.auth_account_id}', - 'id': f'{pay_account.auth_account_id}', - 'time': f'{datetime.now()}', - 'datacontenttype': 'application/json', - 'data': { - 'emailAddresses': emails, - 'accountId': pay_account.auth_account_id, - 'fromDate': f'{statement.from_date}', - 'toDate': f'{statement.to_date}', - 'statementFrequency': statement.frequency, - 'totalAmountOwing': total_amount_owing - } + 'emailAddresses': emails, + 'accountId': pay_account.auth_account_id, + 'fromDate': f'{statement.from_date}', + 'toDate': f'{statement.to_date}', + 'statementFrequency': statement.frequency, + 'totalAmountOwing': total_amount_owing } try: - publish_response(payload=payload, - client_name=current_app.config.get('NATS_MAILER_CLIENT_NAME'), - subject=current_app.config.get('NATS_MAILER_SUBJECT')) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_JOBS.value, + message_type=MessageType.STATEMENT_NOTIFICATION.value, + payload=payload, + topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') + ) + ) except Exception as e: # pylint: disable=broad-except current_app.logger.error(e) current_app.logger.warning('Notification to Queue failed for the Account Mailer %s - %s', @@ -108,28 +103,25 @@ def publish_statement_notification(pay_account: PaymentAccountModel, statement: def publish_payment_notification(info: StatementNotificationInfo) -> bool: """Publish payment notification message to the mailer queue.""" - notification_type = 'bc.registry.payment.statementDueNotification' if info.is_due \ - else 'bc.registry.payment.statementReminderNotification' + notification_type = MessageType.STATEMENT_DUE_NOTIFICATION.value if info.is_due \ + else MessageType.STATEMENT_REMINDER_NOTIFICATION.value payload = { - 'specversion': '1.x-wip', - 'type': notification_type, - 'source': f'https://api.pay.bcregistry.gov.bc.ca/v1/accounts/{info.auth_account_id}', - 'id': info.auth_account_id, - 'time': f'{datetime.now()}', - 'datacontenttype': 'application/json', - 'data': { - 'emailAddresses': info.emails, - 'accountId': info.auth_account_id, - 'dueDate': f'{info.due_date}', - 'statementFrequency': info.statement.frequency, - 'totalAmountOwing': info.total_amount_owing - } + 'emailAddresses': info.emails, + 'accountId': info.auth_account_id, + 'dueDate': f'{info.due_date}', + 'statementFrequency': info.statement.frequency, + 'totalAmountOwing': info.total_amount_owing } try: - publish_response(payload=payload, - client_name=current_app.config.get('NATS_MAILER_CLIENT_NAME'), - subject=current_app.config.get('NATS_MAILER_SUBJECT')) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_JOBS.value, + message_type=notification_type, + payload=payload, + topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') + ) + ) except Exception as e: # pylint: disable=broad-except current_app.logger.error(e) current_app.logger.warning('Notification to Queue failed for the Account Mailer %s - %s', diff --git a/pay-admin/admin/config.py b/pay-admin/admin/config.py index 707552ecf..679e8019c 100755 --- a/pay-admin/admin/config.py +++ b/pay-admin/admin/config.py @@ -58,7 +58,6 @@ def _get_config(config_key: str, **kwargs): value = os.getenv(config_key, kwargs.get('default')) else: value = os.getenv(config_key) - # assert value TODO Un-comment once we find a solution to run pre-hook without initializing app return value diff --git a/pay-admin/requirements.txt b/pay-admin/requirements.txt index b3c14e933..a0d4f1ac5 100644 --- a/pay-admin/requirements.txt +++ b/pay-admin/requirements.txt @@ -28,7 +28,7 @@ requests-futures==1.0.1 requests==2.31.0 typing_extensions==4.10.0 urllib3==2.2.1 --e git+https://github.com/bcgov/sbc-pay.git@queue_python_upgrade#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/bcgov/sbc-pay.git@feature-queue-python-upgrade#egg=pay-api&subdirectory=pay-api -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python git+https://github.com/daxiom/simple-cloudevent.py.git git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-admin/requirements/repo-libraries.txt b/pay-admin/requirements/repo-libraries.txt index 8aaeaf504..3cffb3f2b 100644 --- a/pay-admin/requirements/repo-libraries.txt +++ b/pay-admin/requirements/repo-libraries.txt @@ -1,4 +1,4 @@ --e git+https://github.com/bcgov/sbc-pay.git@queue_python_upgrade#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/bcgov/sbc-pay.git@feature-queue-python-upgrade#egg=pay-api&subdirectory=pay-api -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python git+https://github.com/daxiom/simple-cloudevent.py.git git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-admin/setup.cfg b/pay-admin/setup.cfg index 50d2c7190..fd41bb9f4 100755 --- a/pay-admin/setup.cfg +++ b/pay-admin/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/pay-api/migrations/env.py b/pay-api/migrations/env.py index 911e5fab6..e6603a9b4 100644 --- a/pay-api/migrations/env.py +++ b/pay-api/migrations/env.py @@ -80,7 +80,6 @@ def process_revision_directives(context, revision, directives): context.configure( connection=connection, target_metadata=target_metadata, - compare_type=True, process_revision_directives=process_revision_directives, **current_app.extensions['migrate'].configure_args ) diff --git a/pay-api/requirements.txt b/pay-api/requirements.txt index 62323aa6c..41d1229bb 100644 --- a/pay-api/requirements.txt +++ b/pay-api/requirements.txt @@ -1,6 +1,6 @@ Flask-Caching==2.1.0 Flask-Cors==4.0.0 -Flask-Migrate==2.7.0 +Flask-Migrate==4.0.7 Flask-Moment==1.0.5 Flask-SQLAlchemy==3.1.1 Flask-Script==2.0.6 @@ -9,7 +9,7 @@ Jinja2==3.1.3 Mako==1.3.2 MarkupSafe==2.1.5 SQLAlchemy-Utils==0.41.1 -SQLAlchemy==2.0.27 +SQLAlchemy==2.0.28 Werkzeug==3.0.1 alembic==1.13.1 attrs==23.2.0 @@ -26,15 +26,16 @@ cryptography==42.0.5 dpath==2.1.6 ecdsa==0.18.0 expiringdict==1.2.2 +flask-jwt-oidc==0.3.0 flask-marshmallow==1.2.0 google-api-core==2.17.1 google-auth==2.28.1 google-cloud-pubsub==2.20.0 -googleapis-common-protos==1.62.0 +googleapis-common-protos==1.63.0 greenlet==3.0.3 grpc-google-iam-v1==0.13.0 -grpcio-status==1.62.0 -grpcio==1.62.0 +grpcio-status==1.62.1 +grpcio==1.62.1 gunicorn==21.2.0 holidays==0.37 idna==3.6 @@ -42,11 +43,11 @@ itsdangerous==2.1.2 jaeger-client==4.8.0 jsonschema==4.17.3 launchdarkly-eventsource==1.1.1 -launchdarkly-server-sdk==9.2.1 +launchdarkly-server-sdk==9.2.2 marshmallow-sqlalchemy==1.0.0 -marshmallow==3.21.0 +marshmallow==3.21.1 opentracing==2.4.0 -packaging==23.2 +packaging==24.0 proto-plus==1.23.0 protobuf==4.25.3 psycopg2-binary==2.9.9 @@ -54,6 +55,7 @@ pyRFC3339==1.1 pyasn1-modules==0.3.0 pyasn1==0.5.1 pycparser==2.21 +pyhumps==3.8.0 pyrsistent==0.20.0 python-dateutil==2.9.0.post0 python-dotenv==1.0.1 @@ -62,13 +64,13 @@ pytz==2024.1 requests==2.31.0 rsa==4.9 semver==3.0.2 -sentry-sdk==1.40.6 +sentry-sdk==1.41.0 six==1.16.0 threadloop==1.0.2 thrift==0.16.0 tornado==6.4 typing_extensions==4.10.0 -urllib3==1.26.18 +urllib3==2.2.1 -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python git+https://github.com/daxiom/simple-cloudevent.py.git git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-api/requirements/prod.txt b/pay-api/requirements/prod.txt index 3e5534840..9c4c234fc 100644 --- a/pay-api/requirements/prod.txt +++ b/pay-api/requirements/prod.txt @@ -2,7 +2,7 @@ gunicorn Flask Flask-Caching Flask-Cors -Flask-Migrate<3 +Flask-Migrate Flask-Script Flask-Moment Flask-SQLAlchemy @@ -28,3 +28,4 @@ launchdarkly-server-sdk holidays==0.37 google-auth==2.28.1 google-cloud-pubsub==2.20.0 +pyhumps diff --git a/pay-api/setup.cfg b/pay-api/setup.cfg index 35e4ccdc0..4a29f38b9 100755 --- a/pay-api/setup.cfg +++ b/pay-api/setup.cfg @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 68579db3e..0ecc06a19 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -124,7 +124,10 @@ class _Config(): # pylint: disable=too-few-public-methods AUDIENCE = os.getenv('AUDIENCE', None) GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) - TOPIC_NAME = os.getenv('TOPIC_NAME', None) + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) + EVENT_LISTENER_TOPIC = os.getenv('EVENT_LISTENER_TOPIC', None) + NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', None) + BUSINESS_PAY_TOPIC = os.getenv('BUSINESS_PAY_TOPIC', None) # Auth API Endpoint AUTH_API_ENDPOINT = f'{_get_config("AUTH_API_URL")}/' diff --git a/pay-api/src/pay_api/services/base_payment_system.py b/pay-api/src/pay_api/services/base_payment_system.py index d7355816b..61bcee943 100644 --- a/pay-api/src/pay_api/services/base_payment_system.py +++ b/pay-api/src/pay_api/services/base_payment_system.py @@ -36,10 +36,12 @@ from pay_api.services.invoice_reference import InvoiceReference from pay_api.services.payment import Payment from pay_api.services.payment_account import PaymentAccount +from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.utils.enums import ( - CorpType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, TransactionStatus) + CorpType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, QueueSources, + TransactionStatus) from pay_api.utils.user_context import UserContext -from pay_api.utils.util import get_local_formatted_date_time +from pay_api.utils.util import get_local_formatted_date_time, get_topic_for_corp_type from .payment_line_item import PaymentLineItem from .receipt import Receipt @@ -150,7 +152,14 @@ def _release_payment(invoice: Invoice): payload = PaymentTransaction.create_event_payload(invoice, TransactionStatus.COMPLETED.value) try: current_app.logger.info(f'Releasing record for invoice {invoice.id}') - gcp_queue_publisher.publish_to_queue(payload) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_API.value, + message_type=MessageType.PAYMENT.value, + payload=payload, + topic=get_topic_for_corp_type(invoice.corp_type_code) + ) + ) except Exception as e: # NOQA pylint: disable=broad-except current_app.logger.error(e) current_app.logger.error('Notification to Queue failed for the Payment Event %s', payload) @@ -203,7 +212,6 @@ def _publish_refund_to_mailer(invoice: InvoiceModel): invoice_id=invoice.id, status_code=InvoiceReferenceStatus.COMPLETED.value) payment_transaction: PaymentTransactionModel = PaymentTransactionModel.find_recent_completed_by_invoice_id( invoice_id=invoice.id) - message_type: str = f'bc.registry.payment.{invoice.payment_method_code.lower()}.refundRequest' transaction_date_time = receipt.receipt_date if invoice.payment_method_code == PaymentMethod.DRAWDOWN.value \ else payment_transaction.transaction_end_time filing_description = '' @@ -211,33 +219,34 @@ def _publish_refund_to_mailer(invoice: InvoiceModel): if filing_description: filing_description += ',' filing_description += line_item.description - q_payload = { - 'specversion': '1.x-wip', - 'type': message_type, - 'source': f'https://api.pay.bcregistry.gov.bc.ca/v1/invoices/{invoice.id}', - 'id': invoice.id, - 'datacontenttype': 'application/json', - 'data': { - 'identifier': invoice.business_identifier, - 'orderNumber': receipt.receipt_number, - 'transactionDateTime': get_local_formatted_date_time(transaction_date_time), - 'transactionAmount': receipt.receipt_amount, - 'transactionId': invoice_ref.invoice_number, - 'refundDate': get_local_formatted_date_time(datetime.now(), '%Y%m%d'), - 'filingDescription': filing_description - } + + payload = { + 'identifier': invoice.business_identifier, + 'orderNumber': receipt.receipt_number, + 'transactionDateTime': get_local_formatted_date_time(transaction_date_time), + 'transactionAmount': receipt.receipt_amount, + 'transactionId': invoice_ref.invoice_number, + 'refundDate': get_local_formatted_date_time(datetime.now(), '%Y%m%d'), + 'filingDescription': filing_description } if invoice.payment_method_code == PaymentMethod.DRAWDOWN.value: payment_account: PaymentAccountModel = PaymentAccountModel.find_by_id(invoice.payment_account_id) filing_description += ',' filing_description += invoice_ref.invoice_number - q_payload['data'].update({ + payload.update({ 'bcolAccount': invoice.bcol_account, 'bcolUser': payment_account.bcol_user_id, 'filingDescription': filing_description }) - current_app.logger.debug(f'Publishing payment refund request to mailer for {invoice.id} : {q_payload}') - gcp_queue_publisher.publish_to_queue(q_payload) + current_app.logger.debug(f'Publishing payment refund request to mailer for {invoice.id} : {payload}') + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_API.value, + message_type=f'{invoice.payment_method_code.lower()}.refundRequest', + payload=payload, + topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') + ) + ) def complete_payment(self, invoice, invoice_reference): """Create payment and related records as if the payment is complete.""" diff --git a/pay-api/src/pay_api/services/eft_service.py b/pay-api/src/pay_api/services/eft_service.py index e3abab9be..57e8a83bb 100644 --- a/pay-api/src/pay_api/services/eft_service.py +++ b/pay-api/src/pay_api/services/eft_service.py @@ -13,7 +13,7 @@ # limitations under the License. """Service to manage CFS EFT Payments.""" from datetime import datetime -from typing import Any, Dict +from typing import Any, Dict, List from flask import current_app @@ -24,7 +24,6 @@ from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel from pay_api.utils.enums import CfsAccountStatus, InvoiceReferenceStatus, PaymentMethod, PaymentStatus - from .deposit_service import DepositService from .invoice import Invoice from .invoice_reference import InvoiceReference @@ -48,15 +47,10 @@ def create_account(self, identifier: str, contact_info: Dict[str, Any], payment_ cfs_account.status = CfsAccountStatus.PENDING.value return cfs_account - def create_invoice(self, payment_account: PaymentAccount, line_items: [PaymentLineItem], invoice: Invoice, + def create_invoice(self, payment_account: PaymentAccount, line_items: List[PaymentLineItem], invoice: Invoice, **kwargs) -> InvoiceReference: - """Return a static invoice number for direct pay.""" - payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) - invoice_reference = self.create_invoice_reference(invoice=invoice, payment=payment) - - invoice_reference.save() - - return invoice_reference + """Do nothing here, we create invoice references on the create CFS_INVOICES job.""" + return def apply_credit(self, invoice: Invoice, diff --git a/pay-api/src/pay_api/services/fee_schedule.py b/pay-api/src/pay_api/services/fee_schedule.py index 87ef380d3..5ba5a2b1a 100644 --- a/pay-api/src/pay_api/services/fee_schedule.py +++ b/pay-api/src/pay_api/services/fee_schedule.py @@ -353,7 +353,6 @@ def calculate_service_fees(fee_schedule_model: FeeScheduleModel, account_fee: Ac service_fees: float = 0 # TODO for system accounts with role EXCLUDE_SERVICE_FEES, do not charge service fees for now. - # Handle it properly later if not user.is_staff() and \ not (user.is_system() and Role.EXCLUDE_SERVICE_FEES.value in user.roles) \ and fee_schedule_model.fee.amount > 0 and fee_schedule_model.service_fee: diff --git a/pay-api/src/pay_api/services/gcp_queue_publisher.py b/pay-api/src/pay_api/services/gcp_queue_publisher.py index 0a4a8659f..2e390d3ae 100644 --- a/pay-api/src/pay_api/services/gcp_queue_publisher.py +++ b/pay-api/src/pay_api/services/gcp_queue_publisher.py @@ -1,9 +1,11 @@ """This module provides Queue type services.""" import base64 +from dataclasses import dataclass import json import uuid from concurrent.futures import CancelledError from concurrent.futures import TimeoutError # pylint: disable=W0622 +from datetime import datetime, timezone from flask import current_app from google.auth import jwt @@ -11,26 +13,41 @@ from simple_cloudevent import SimpleCloudEvent, to_queue_message -def publish_to_queue(payload: dict): +@dataclass +class QueueMessage: + """Queue message data class.""" + + source: str + message_type: str + payload: dict + topic: str + + +def publish_to_queue(queue_message: QueueMessage): """Publish to GCP PubSub Queue.""" - ce = SimpleCloudEvent() - ce.id = payload.get('paymentToken', {}).get('id', str(uuid.uuid4())) - ce.source = 'sbc-pay' - ce.subject = 'SUBJECT' # invoice.business_identifier - ce.time = 'TIME' # invoice.payment_date - ce.type = 'payment' - ce.data = payload + if queue_message.topic is None: + current_app.logger.info('Skipping queue message topic not set.') + return + + queue_message_bytes = to_queue_message(SimpleCloudEvent( + id=str(uuid.uuid4()), + source=f'sbc-pay-{queue_message.source}', + # Intentionally blank, this field has been moved to topic. + subject=None, + time=datetime.now(tz=timezone.utc).isoformat(), + type=queue_message.message_type, + data=queue_message.payload + )) - _send_to_queue(to_queue_message(ce)) + _send_to_queue(queue_message.topic, queue_message_bytes) -def _send_to_queue(payload: bytes): +def _send_to_queue(topic_name: str, payload: bytes): """Send payload to the queue.""" if not ((gcp_auth_key := current_app.config.get('GCP_AUTH_KEY')) and (audience := current_app.config.get('AUDIENCE')) and - (topic_name := current_app.config.get('TOPIC_NAME')) and (publisher_audience := current_app.config.get('PUBLISHER_AUDIENCE'))): - raise Exception('missing setup arguments') # pylint: disable=W0719 + raise Exception('Missing setup arguments') # pylint: disable=W0719 try: service_account_info = json.loads(base64.b64decode(gcp_auth_key).decode('utf-8')) diff --git a/pay-api/src/pay_api/services/payment_account.py b/pay-api/src/pay_api/services/payment_account.py index c3e34a509..84fd20a8d 100644 --- a/pay-api/src/pay_api/services/payment_account.py +++ b/pay-api/src/pay_api/services/payment_account.py @@ -18,7 +18,6 @@ from decimal import Decimal from typing import Any, Dict, List, Optional, Tuple from cattr import Converter - from flask import current_app from sentry_sdk import capture_message from sqlalchemy import and_, desc, func, or_ @@ -36,16 +35,17 @@ from pay_api.models import StatementSettings as StatementSettingsModel from pay_api.models import db from pay_api.models.payment_account import PaymentAccountSearchModel +from pay_api.services import gcp_queue_publisher from pay_api.services.cfs_service import CFSService from pay_api.services.distribution_code import DistributionCode -from pay_api.services import gcp_queue_publisher +from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.services.oauth_service import OAuthService from pay_api.services.receipt import Receipt as ReceiptService from pay_api.services.statement import Statement from pay_api.services.statement_settings import StatementSettings from pay_api.utils.enums import ( AuthHeaderType, CfsAccountStatus, ContentType, InvoiceStatus, MessageType, PaymentMethod, PaymentSystem, - StatementFrequency) + QueueSources, StatementFrequency) from pay_api.utils.errors import Error from pay_api.utils.user_context import UserContext, user_context from pay_api.utils.util import ( @@ -805,12 +805,19 @@ def publish_account_mailer_event_on_creation(self): """Publish to account mailer message to send out confirmation email on creation.""" if self.payment_method == PaymentMethod.PAD.value: payload = self.create_account_event_payload(MessageType.PAD_ACCOUNT_CREATE.value, include_pay_info=True) - self._publish_queue_message(payload) + self._publish_queue_message(payload, MessageType.PAD_ACCOUNT_CREATE.value) - def _publish_queue_message(self, payload): + def _publish_queue_message(self, payload: dict, message_type: str): """Publish to account mailer to send out confirmation email or notification email.""" try: - gcp_queue_publisher.publish_to_queue(payload) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_API.value, + message_type=message_type, + payload=payload, + topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') + ) + ) except Exception as e: # NOQA pylint: disable=broad-except current_app.logger.error(e) current_app.logger.error( @@ -824,29 +831,21 @@ def create_account_event_payload(self, event_type: str, receipt_info: dict = Non include_pay_info: bool = False): """Return event payload for account.""" payload: Dict[str, any] = { - 'specversion': '1.x-wip', - 'type': event_type, - 'source': f'https://api.pay.bcregistry.gov.bc.ca/v1/accounts/{self.auth_account_id}', - 'id': f'{self.auth_account_id}', - 'time': f'{datetime.now()}', - 'datacontenttype': 'application/json', - 'data': { - 'accountId': self.auth_account_id, - 'accountName': self.name - } + 'accountId': self.auth_account_id, + 'accountName': self.name } if event_type == MessageType.NSF_UNLOCK_ACCOUNT.value: - payload['data'].update({ + payload.update({ 'invoiceNumber': receipt_info['invoiceNumber'], 'receiptNumber': receipt_info['receiptNumber'], 'paymentMethodDescription': receipt_info['paymentMethodDescription'], 'invoice': receipt_info['invoice'] }) if event_type == MessageType.PAD_ACCOUNT_CREATE.value: - payload['data']['padTosAcceptedBy'] = self.pad_tos_accepted_by + payload['padTosAcceptedBy'] = self.pad_tos_accepted_by if include_pay_info: - payload['data']['paymentInfo'] = { + payload['paymentInfo'] = { 'bankInstitutionNumber': self.bank_number, 'bankTransitNumber': self.bank_branch_number, 'bankAccountNumber': mask(self.bank_account_number, current_app.config['MASK_LEN']), @@ -873,7 +872,14 @@ def unlock_frozen_accounts(payment: Payment): ) try: - gcp_queue_publisher.publish_to_queue(payload=payload) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_API.value, + message_type=MessageType.NSF_UNLOCK_ACCOUNT.value, + payload=payload, + topic=current_app.config.get('EVENT_LISTENER_TOPIC') + ) + ) except Exception as e: # NOQA pylint: disable=broad-except current_app.logger.error(e) current_app.logger.error( @@ -923,5 +929,5 @@ def enable_eft(cls, auth_account_id: str) -> PaymentAccount: pa_service = cls.find_by_id(pay_account.id) if not already_has_eft_enabled: payload = pa_service.create_account_event_payload(MessageType.EFT_AVAILABLE_NOTIFICATION.value) - pa_service._publish_queue_message(payload) + pa_service._publish_queue_message(payload, MessageType.EFT_AVAILABLE_NOTIFICATION.value) return pa_service diff --git a/pay-api/src/pay_api/services/payment_transaction.py b/pay-api/src/pay_api/services/payment_transaction.py index 641bdd189..7c800c6f1 100644 --- a/pay-api/src/pay_api/services/payment_transaction.py +++ b/pay-api/src/pay_api/services/payment_transaction.py @@ -16,9 +16,11 @@ from __future__ import annotations import uuid +from dataclasses import asdict, dataclass from datetime import datetime -from typing import Dict, List +from typing import Dict, List, Optional +import humps from flask import current_app from sentry_sdk import capture_message @@ -26,19 +28,31 @@ from pay_api.factory.payment_system_factory import PaymentSystemFactory from pay_api.models import PaymentTransaction as PaymentTransactionModel from pay_api.models import PaymentTransactionSchema -from pay_api.services.base_payment_system import PaymentSystemService from pay_api.services import gcp_queue_publisher +from pay_api.services.base_payment_system import PaymentSystemService +from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.services.invoice import Invoice from pay_api.services.invoice_reference import InvoiceReference from pay_api.services.payment_account import PaymentAccount from pay_api.services.receipt import Receipt -from pay_api.utils.enums import InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, TransactionStatus +from pay_api.utils.enums import ( + InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, QueueSources, TransactionStatus) from pay_api.utils.errors import Error -from pay_api.utils.util import is_valid_redirect_url +from pay_api.utils.util import get_topic_for_corp_type, is_valid_redirect_url from .payment import Payment +@dataclass +class PaymentToken: + """Payment Token payload common interface for LEAR and Names.""" + + id: Optional[str] = None + status_code: Optional[str] = None + filing_identifier: Optional[str] = None + corp_type_code: Optional[str] = None + + class PaymentTransaction: # pylint: disable=too-many-instance-attributes, too-many-public-methods """Service to manage Payment transaction operations.""" @@ -493,11 +507,15 @@ def publish_status(transaction_dao: PaymentTransactionModel, invoice: Invoice): else: status_code = 'TRANSACTION_FAILED' - payload = PaymentTransaction.create_event_payload(invoice, status_code) - try: - gcp_queue_publisher.publish_to_queue(payload=payload) - + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_API.value, + message_type=MessageType.PAYMENT.value, + payload=PaymentTransaction.create_event_payload(invoice, status_code), + topic=get_topic_for_corp_type(invoice.corp_type_code) + ) + ) except Exception as e: # NOQA pylint: disable=broad-except current_app.logger.error(e) current_app.logger.warning( @@ -509,12 +527,4 @@ def publish_status(transaction_dao: PaymentTransactionModel, invoice: Invoice): @staticmethod def create_event_payload(invoice, status_code): """Create event payload for payment events.""" - payload = { - 'paymentToken': { - 'id': invoice.id, - 'statusCode': status_code, - 'filingIdentifier': invoice.filing_id, - 'corpTypeCode': invoice.corp_type_code - } - } - return payload + return humps.camelize(asdict(PaymentToken(invoice.id, status_code, invoice.filing_id, invoice.corp_type_code))) diff --git a/pay-api/src/pay_api/utils/constants.py b/pay-api/src/pay_api/utils/constants.py index 0cab472ce..a01f3d7c2 100644 --- a/pay-api/src/pay_api/utils/constants.py +++ b/pay-api/src/pay_api/utils/constants.py @@ -22,7 +22,6 @@ RECEIPT_METHOD_PAD_DAILY = 'BCR-PAD Daily' RECEIPT_METHOD_PAD_STOP = 'BCR-PAD Stop' RECEIPT_METHOD_EFT_MONTHLY = 'BCR-EFT MONTHLY' -RECEIPT_METHOD_EFT_STOP = 'BCR-PAD Stop' CFS_BATCH_SOURCE = 'BC REG MANUAL_OTHER' CFS_CM_BATCH_SOURCE = 'MANUAL-OTHER' diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index fba411797..c7bc97b71 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -339,13 +339,24 @@ class EFTShortnameState(Enum): class MessageType(Enum): - """Account Mailer Event Types.""" - - # Ideally Should match account mailer project - FUTURE: move into sbc-common-components. - EFT_AVAILABLE_NOTIFICATION = 'bc.registry.payment.eftAvailableNotification' - PAD_ACCOUNT_CREATE = 'bc.registry.payment.padAccountCreate' - NSF_LOCK_ACCOUNT = 'bc.registry.payment.lockAccount' - NSF_UNLOCK_ACCOUNT = 'bc.registry.payment.unlockAccount' + """Queue Event Types.""" + + EFT_AVAILABLE_NOTIFICATION = 'eftAvailableNotification' + PAD_PAYMENT_SUCCESS = 'PAD.PaymentSuccess' + PAD_ACCOUNT_CREATE = 'padAccountCreate' + NSF_LOCK_ACCOUNT = 'lockAccount' + NSF_UNLOCK_ACCOUNT = 'unlockAccount' + STATEMENT_NOTIFICATION = 'statementNotification' + STATEMENT_DUE_NOTIFICATION = 'statementDueNotification' + STATEMENT_REMINDER_NOTIFICATION = 'statementReminderNotification' + PAYMENT = 'payment' + EJV_FAILED = 'ejvFailed' + CAS_UPLOADED = 'casSettlementUploaded' + INCORPORATION = 'incorporationApplication' + REGISTRATION = 'registration' + CGI_ACK_RECEIVED = 'ACKReceived' + CGI_FEEDBACK_RECEIVED = 'FEEDBACKReceived' + EFT_FILE_UPLOADED = 'eftFileUploaded' class PaymentDetailsGlStatus(Enum): @@ -355,3 +366,12 @@ class PaymentDetailsGlStatus(Enum): INPRG = 'INPRG' RJCT = 'RJCT' # Should have refundglerrormessage CMPLT = 'CMPLT' + + +class QueueSources(Enum): + """Queue sources for PAY.""" + + PAY_API = 'pay-api' + PAY_JOBS = 'pay-jobs' + PAY_QUEUE = 'pay-queue' + FTP_POLLER = 'ftp-poller' diff --git a/pay-api/src/pay_api/utils/util.py b/pay-api/src/pay_api/utils/util.py index 62aa51569..49343b370 100755 --- a/pay-api/src/pay_api/utils/util.py +++ b/pay-api/src/pay_api/utils/util.py @@ -29,7 +29,7 @@ from flask import current_app from .constants import DT_SHORT_FORMAT -from .enums import StatementFrequency +from .enums import CorpType, StatementFrequency def cors_preflight(methods: str = 'GET'): @@ -253,3 +253,15 @@ def cents_to_decimal(amount: int): return None return amount / 100 + + +def get_topic_for_corp_type(corp_type: str): + """Return a topic to direct the queue message to.""" + match corp_type: + case CorpType.NRO.value: + return current_app.config.get('NAMEX_PAY_TOPIC') + # Unused for now, intentionally don't send a queue message for these. + case CorpType.PPR.value | CorpType.VS.value | CorpType.CSO.value: + return None + case _: + return current_app.config.get('BUSINESS_PAY_TOPIC') diff --git a/pay-api/tests/conftest.py b/pay-api/tests/conftest.py index 4de3465b0..3eb46795b 100755 --- a/pay-api/tests/conftest.py +++ b/pay-api/tests/conftest.py @@ -14,9 +14,7 @@ """Common setup and fixtures for the py-test suite used by this service.""" -import asyncio import os -import random import pytest from flask_migrate import Migrate, upgrade @@ -39,6 +37,7 @@ def app(): @pytest.fixture(autouse=True) def mock_queue_publish(monkeypatch): """Mock queue publish.""" + # TODO: so it can be used like this from gcp_queue_publisher import publish_to_queue monkeypatch.setattr('pay_api.services.gcp_queue_publisher.publish_to_queue', lambda *args, **kwargs: None) @@ -46,7 +45,6 @@ def mock_queue_publish(monkeypatch): def app_request(): """Return a session-wide application configured in TEST mode.""" _app = create_app('testing') - return _app @@ -118,38 +116,6 @@ def restart_savepoint(sess2, trans): # pylint: disable=unused-variable transaction.rollback() -@pytest.fixture(scope='function') -def client_id(): - """Return a unique client_id that can be used in tests.""" - _id = random.SystemRandom().getrandbits(0x58) - - return f'client-{_id}' - - -@pytest.fixture(scope='function') -def future(event_loop): - """Return a future that is used for managing function tests.""" - _future = asyncio.Future(loop=event_loop) - return _future - - -@pytest.fixture -def create_mock_coro(mocker, monkeypatch): - """Return a mocked coroutine, and optionally patch-it in.""" - def _create_mock_patch_coro(to_patch=None): - """Return a mocked coroutine, and optionally patch-it in.""" - mock = mocker.Mock() - - async def _coro(*args, **kwargs): - return mock(*args, **kwargs) - - if to_patch: # <-- may not need/want to patch anything - monkeypatch.setattr(to_patch, _coro) - return mock, _coro - - return _create_mock_patch_coro - - @pytest.fixture() def auth_mock(monkeypatch): """Mock check_auth.""" diff --git a/pay-api/tests/unit/api/test_eft_short_names.py b/pay-api/tests/unit/api/test_eft_short_names.py index 802bc14ba..2864ce225 100755 --- a/pay-api/tests/unit/api/test_eft_short_names.py +++ b/pay-api/tests/unit/api/test_eft_short_names.py @@ -19,6 +19,7 @@ import json from datetime import datetime +import pytest from flask import current_app @@ -28,8 +29,7 @@ from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Payment as PaymentModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import ( - EFTFileLineType, EFTProcessStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, Role) +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, InvoiceStatus, PaymentMethod, PaymentStatus, Role from tests.utilities.base_test import ( factory_eft_file, factory_eft_shortname, factory_invoice, factory_payment_account, get_claims, token_header) @@ -431,6 +431,10 @@ def test_search_eft_short_names(session, client, jwt, app): assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) +@pytest.mark.skip(reason='This needs to be re-thought, the create cfs invoice job should be handling receipt creation' + 'and creating invoice references when payments are mapped, ' + 'it should wait until 6 pm before marking invoices as PAID' + 'Otherwise calls to CFS could potentially fail and the two systems would go out of sync.') def test_apply_eft_short_name_credits(session, client, jwt, app): """Assert that credits are applied to invoices when short name is mapped to an account.""" token = jwt.create_jwt(get_claims(roles=[Role.STAFF.value, Role.MANAGE_EFT.value]), token_header) @@ -503,12 +507,7 @@ def test_apply_eft_short_name_credits(session, client, jwt, app): assert payment.invoice_amount == invoice_1_paid assert payment.paid_amount == invoice_1_paid - invoice_reference_1 = invoice_1.references[0] - assert invoice_reference_1 is not None - assert invoice_reference_1.invoice_id == invoice_1.id - assert invoice_reference_1.invoice_number == payment.invoice_number - assert invoice_reference_1.invoice_number == payment.invoice_number - assert invoice_reference_1.status_code == InvoiceReferenceStatus.COMPLETED.value + assert not invoice_1.references # Assert details of partially paid invoice invoice_2_paid = 150 @@ -534,9 +533,4 @@ def test_apply_eft_short_name_credits(session, client, jwt, app): assert payment.invoice_amount == 200 assert payment.paid_amount == invoice_2_paid - invoice_reference_2 = invoice_2.references[0] - assert invoice_reference_2 is not None - assert invoice_reference_2.invoice_id == invoice_2.id - assert invoice_reference_2.invoice_number == payment.invoice_number - assert invoice_reference_2.invoice_number == payment.invoice_number - assert invoice_reference_2.status_code == InvoiceReferenceStatus.ACTIVE.value + assert not invoice_2.references diff --git a/queue_services/events-listener/.envrc b/pay-queue/.envrc similarity index 100% rename from queue_services/events-listener/.envrc rename to pay-queue/.envrc diff --git a/queue_services/payment-reconciliations/Dockerfile b/pay-queue/Dockerfile similarity index 95% rename from queue_services/payment-reconciliations/Dockerfile rename to pay-queue/Dockerfile index 0b09413fa..525967f9c 100644 --- a/queue_services/payment-reconciliations/Dockerfile +++ b/pay-queue/Dockerfile @@ -19,7 +19,7 @@ WORKDIR /opt/app-root COPY ./requirements.txt . #RUN pip install --upgrade pip -RUN pip install pip==22.2.2 +RUN pip install pip==24.0.0 RUN pip install --no-cache-dir -r requirements.txt COPY . . diff --git a/queue_services/events-listener/LICENSE b/pay-queue/LICENSE similarity index 100% rename from queue_services/events-listener/LICENSE rename to pay-queue/LICENSE diff --git a/queue_services/events-listener/MANIFEST.in b/pay-queue/MANIFEST.in similarity index 100% rename from queue_services/events-listener/MANIFEST.in rename to pay-queue/MANIFEST.in diff --git a/queue_services/payment-reconciliations/Makefile b/pay-queue/Makefile similarity index 98% rename from queue_services/payment-reconciliations/Makefile rename to pay-queue/Makefile index 5d0829f10..307e09ca3 100644 --- a/queue_services/payment-reconciliations/Makefile +++ b/pay-queue/Makefile @@ -6,8 +6,8 @@ MKFILE_PATH:=$(abspath $(lastword $(MAKEFILE_LIST))) CURRENT_ABS_DIR:=$(patsubst %/,%,$(dir $(MKFILE_PATH))) -PROJECT_NAME:=reconciliations -DOCKER_NAME:=payment-reconciliations +PROJECT_NAME:=pay_queue +DOCKER_NAME:=pay-queue ################################################################################# # COMMANDS -- Setup # diff --git a/pay-queue/README.md b/pay-queue/README.md new file mode 100755 index 000000000..b98948f78 --- /dev/null +++ b/pay-queue/README.md @@ -0,0 +1,28 @@ + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) + + +# Application Name +BC Registries Payment Reconciliation Queue + +## Technology Stack Used +* Python, Flask +* Postgres - SQLAlchemy, psycopg2-binary & alembic + + +## License + + Copyright 2024 Province of British Columbia + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/jobs/ftp-poller/utils/constants.py b/pay-queue/app.py old mode 100644 new mode 100755 similarity index 65% rename from jobs/ftp-poller/utils/constants.py rename to pay-queue/app.py index 040a16678..dd7bd16a7 --- a/jobs/ftp-poller/utils/constants.py +++ b/pay-queue/app.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + # Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); @@ -11,9 +14,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Enum definitions.""" +"""Provides the WSGI entry point for running the application.""" +import os + +from pay_queue import create_app + +app = create_app() -CAS_MESSAGE_TYPE = 'bc.registry.payment.casSettlementUploaded' -CGI_ACK_MESSAGE_TYPE = 'bc.registry.payment.cgi.ACKReceived' -CGI_FEEDBACK_MESSAGE_TYPE = 'bc.registry.payment.cgi.FEEDBACKReceived' -EFT_MESSAGE_TYPE = 'bc.registry.payment.eft.fileUploaded' +if __name__ == '__main__': + server_port = os.environ.get('PORT', '8080') + app.run(debug=False, port=server_port, host='0.0.0.0') diff --git a/queue_services/payment-reconciliations/devops/vaults.json b/pay-queue/devops/vaults.json similarity index 74% rename from queue_services/payment-reconciliations/devops/vaults.json rename to pay-queue/devops/vaults.json index 0a37699ed..6b84697ac 100644 --- a/queue_services/payment-reconciliations/devops/vaults.json +++ b/pay-queue/devops/vaults.json @@ -12,16 +12,6 @@ "payment-reconciliations" ] }, - { - "vault": "nats", - "application": [ - "base", - "account-events-listener", - "account-mailer", - "payment", - "payment-reconciliations" - ] - }, { "vault": "payment-external-services", "application": [ diff --git a/pay-queue/flags.json b/pay-queue/flags.json new file mode 100644 index 000000000..870b306f4 --- /dev/null +++ b/pay-queue/flags.json @@ -0,0 +1,8 @@ +{ + "flagValues": { + "string-flag": "a string value", + "bool-flag": true, + "integer-flag": 10, + "enable-eft-payment-method": true + } +} diff --git a/queue_services/payment-reconciliations/logging.conf b/pay-queue/logging.conf similarity index 100% rename from queue_services/payment-reconciliations/logging.conf rename to pay-queue/logging.conf diff --git a/queue_services/payment-reconciliations/openshift/templates/payment-reconciliations-build.json b/pay-queue/openshift/templates/payment-reconciliations-build.json similarity index 100% rename from queue_services/payment-reconciliations/openshift/templates/payment-reconciliations-build.json rename to pay-queue/openshift/templates/payment-reconciliations-build.json diff --git a/queue_services/payment-reconciliations/openshift/templates/payment-reconciliations-deploy.json b/pay-queue/openshift/templates/payment-reconciliations-deploy.json similarity index 100% rename from queue_services/payment-reconciliations/openshift/templates/payment-reconciliations-deploy.json rename to pay-queue/openshift/templates/payment-reconciliations-deploy.json diff --git a/pay-queue/requirements.txt b/pay-queue/requirements.txt new file mode 100644 index 000000000..2d3d82364 --- /dev/null +++ b/pay-queue/requirements.txt @@ -0,0 +1,85 @@ +-e git+https://github.com/bcgov/sbc-common-components.git@5f99e135214ae949c9af951d4aa0b88b1067d853#egg=sbc_common_components&subdirectory=python +-e git+https://github.com/seeker25/sbc-pay.git@ca0a69dce17ec602f2c3ceee164f15d0c6e7e804#egg=pay_api&subdirectory=pay-api +-e git+https://github.com/thorwolpert/flask-jwt-oidc.git@40cc811ccf70e838c5f7522fe8d83b7e58853539#egg=flask_jwt_oidc +CacheControl==0.14.0 +Flask-Caching==2.1.0 +Flask-Cors==4.0.0 +Flask-Migrate==4.0.7 +Flask-Moment==1.0.5 +Flask-OpenTracing==1.1.0 +Flask-SQLAlchemy==3.1.1 +Flask-Script==2.0.6 +Flask==3.0.2 +Jinja2==3.1.3 +Mako==1.3.2 +MarkupSafe==2.1.5 +SQLAlchemy-Utils==0.41.1 +SQLAlchemy==2.0.28 +Werkzeug==3.0.1 +alembic==1.13.1 +argon2-cffi-bindings==21.2.0 +argon2-cffi==23.1.0 +attrs==23.2.0 +blinker==1.7.0 +cachelib==0.9.0 +cachetools==5.3.3 +cattrs==23.2.3 +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +croniter==2.0.2 +cryptography==42.0.5 +dpath==2.1.6 +ecdsa==0.18.0 +expiringdict==1.2.2 +flask-marshmallow==1.2.0 +google-api-core==2.17.1 +google-auth==2.28.1 +google-cloud-pubsub==2.20.0 +googleapis-common-protos==1.63.0 +greenlet==3.0.3 +grpc-google-iam-v1==0.13.0 +grpcio-status==1.62.1 +grpcio==1.62.1 +gunicorn==21.2.0 +holidays==0.37 +idna==3.6 +itsdangerous==2.1.2 +jaeger-client==4.8.0 +jsonschema==4.17.3 +launchdarkly-eventsource==1.1.1 +launchdarkly-server-sdk==9.2.2 +marshmallow-sqlalchemy==1.0.0 +marshmallow==3.21.1 +minio==7.2.5 +msgpack==1.0.8 +opentracing==2.4.0 +packaging==24.0 +proto-plus==1.23.0 +protobuf==4.25.3 +psycopg2-binary==2.9.9 +pyRFC3339==1.1 +pyasn1-modules==0.3.0 +pyasn1==0.5.1 +pycountry==23.12.11 +pycparser==2.21 +pycryptodome==3.20.0 +pyhumps==3.8.0 +pyrsistent==0.20.0 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 +python-jose==3.3.0 +pytz==2024.1 +requests==2.31.0 +rsa==4.9 +semver==3.0.2 +sentry-sdk==1.42.0 +simple-cloudevent @ git+https://github.com/daxiom/simple-cloudevent.py.git@447cabb988202206ac69e71177d7cd11b6c0b002 +six==1.16.0 +strict-rfc3339==0.7 +threadloop==1.0.2 +thrift==0.16.0 +tornado==6.4 +typing_extensions==4.10.0 +urllib3==2.2.1 diff --git a/pay-queue/requirements/bcregistry-libraries.txt b/pay-queue/requirements/bcregistry-libraries.txt new file mode 100644 index 000000000..d1067265b --- /dev/null +++ b/pay-queue/requirements/bcregistry-libraries.txt @@ -0,0 +1,5 @@ +-e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python +-e git+https://github.com/seeker25/sbc-pay.git@18263#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/thorwolpert/flask-jwt-oidc.git#egg=flask-jwt-oidc +# Note move out queue stuff into here. +git+https://github.com/daxiom/simple-cloudevent.py.git diff --git a/queue_services/events-listener/requirements/dev.txt b/pay-queue/requirements/dev.txt similarity index 96% rename from queue_services/events-listener/requirements/dev.txt rename to pay-queue/requirements/dev.txt index 04624060f..6c304627f 100755 --- a/queue_services/events-listener/requirements/dev.txt +++ b/pay-queue/requirements/dev.txt @@ -10,7 +10,7 @@ pytest-cov FreezeGun # Lint and code style -flake8==5.0.4 +flake8 flake8-blind-except flake8-debugger flake8-docstrings diff --git a/queue_services/events-listener/requirements/prod.txt b/pay-queue/requirements/prod.txt similarity index 51% rename from queue_services/events-listener/requirements/prod.txt rename to pay-queue/requirements/prod.txt index d8e80b8bb..2266a7ec5 100644 --- a/queue_services/events-listener/requirements/prod.txt +++ b/pay-queue/requirements/prod.txt @@ -2,13 +2,14 @@ Flask jsonschema==4.17.3 python-dotenv sentry-sdk[flask] -asyncio-nats-client -asyncio-nats-streaming pycountry -Werkzeug<2 +Werkzeug +minio jaeger-client attrs -itsdangerous==2.0.1 -Jinja2==3.0.3 -protobuf~=3.19.5 +sqlalchemy +itsdangerous +Jinja2 +protobuf launchdarkly-server-sdk +CacheControl diff --git a/queue_services/events-listener/scripts/verify_license_headers.sh b/pay-queue/scripts/verify_license_headers.sh similarity index 100% rename from queue_services/events-listener/scripts/verify_license_headers.sh rename to pay-queue/scripts/verify_license_headers.sh diff --git a/queue_services/payment-reconciliations/setup.cfg b/pay-queue/setup.cfg similarity index 93% rename from queue_services/payment-reconciliations/setup.cfg rename to pay-queue/setup.cfg index eb4f0cd17..a09beb534 100644 --- a/queue_services/payment-reconciliations/setup.cfg +++ b/pay-queue/setup.cfg @@ -1,6 +1,6 @@ [metadata] -name = account_reconciliations -url = https://github.com/bcgov/sbc-pay/queue_services/payment-reconciliations +name = pay-queue +url = https://github.com/bcgov/sbc-pay/pay-queue author = SBC Relationships team author_email = classifiers = @@ -17,7 +17,7 @@ keywords = [options] zip_safe = True -python_requires = >=3.6 +python_requires = >=3.12 include_package_data = True packages = find: diff --git a/queue_services/payment-reconciliations/setup.py b/pay-queue/setup.py similarity index 97% rename from queue_services/payment-reconciliations/setup.py rename to pay-queue/setup.py index 64d1ad17d..31939e558 100644 --- a/queue_services/payment-reconciliations/setup.py +++ b/pay-queue/setup.py @@ -23,7 +23,7 @@ _version_re = re.compile(r'__version__\s+=\s+(.*)') # pylint: disable=invalid-name -with open('src/reconciliations/version.py', 'rb') as f: +with open('src/pay_queue/version.py', 'rb') as f: version = str(ast.literal_eval(_version_re.search( # pylint: disable=invalid-name f.read().decode('utf-8')).group(1))) diff --git a/pay-queue/src/pay_queue/__init__.py b/pay-queue/src/pay_queue/__init__.py new file mode 100644 index 000000000..331be818d --- /dev/null +++ b/pay-queue/src/pay_queue/__init__.py @@ -0,0 +1,57 @@ +# Copyright © 2024 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""The Reconciliations queue service. + +The service worker for applying payments, receipts and account balance to payment system. +""" +from __future__ import annotations + +import os + +import sentry_sdk +from flask import Flask +from pay_api.models import db +from pay_api.services.flags import flags +from pay_api.utils.run_version import get_run_version +from sentry_sdk.integrations.flask import FlaskIntegration + +from pay_queue.config import CONFIGURATION +from pay_queue.version import __version__ + +from .resources import register_endpoints +from .services import queue + + +def create_app(run_mode=os.getenv('FLASK_ENV', 'production')) -> Flask: + """Return a configured Flask App using the Factory method.""" + app = Flask(__name__) + app.env = run_mode + app.config.from_object(CONFIGURATION[run_mode]) + + # Configure Sentry + if dsn := app.config.get('SENTRY_DSN', None): + sentry_sdk.init( + dsn=dsn, + integrations=[FlaskIntegration()], + release=f'pay-queue@{get_run_version()}', + send_default_pii=False, + ) + + flags.init_app(app) + db.init_app(app) + queue.init_app(app) + + register_endpoints(app) + + return app diff --git a/queue_services/payment-reconciliations/src/reconciliations/config.py b/pay-queue/src/pay_queue/config.py similarity index 66% rename from queue_services/payment-reconciliations/src/reconciliations/config.py rename to pay-queue/src/pay_queue/config.py index 3ce8d1bde..9e9f21106 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/config.py +++ b/pay-queue/src/pay_queue/config.py @@ -20,7 +20,6 @@ or by accessing this configuration directly. """ import os -import random from dotenv import find_dotenv, load_dotenv @@ -29,10 +28,10 @@ load_dotenv(find_dotenv()) CONFIGURATION = { - 'development': 'reconciliations.config.DevConfig', - 'testing': 'reconciliations.config.TestConfig', - 'production': 'reconciliations.config.ProdConfig', - 'default': 'reconciliations.config.ProdConfig' + 'development': 'pay_queue.config.DevConfig', + 'testing': 'pay_queue.config.TestConfig', + 'production': 'pay_queue.config.ProdConfig', + 'default': 'pay_queue.config.ProdConfig' } @@ -74,23 +73,6 @@ class _Config(): # pylint: disable=too-few-public-methods DB_HOST = os.getenv('DATABASE_HOST', '') DB_PORT = os.getenv('DATABASE_PORT', '5432') SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' - NATS_CONNECTION_OPTIONS = { - 'servers': os.getenv('NATS_SERVERS', 'nats://127.0.0.1:4222').split(','), - 'name': os.getenv('NATS_PAYMENT_RECONCILIATIONS_CLIENT_NAME', 'payment.reconciliations.worker') - - } - STAN_CONNECTION_OPTIONS = { - 'cluster_id': os.getenv('NATS_CLUSTER_ID', 'test-cluster'), - 'client_id': str(random.SystemRandom().getrandbits(0x58)), - 'ping_interval': 1, - 'ping_max_out': 5, - } - - SUBSCRIPTION_OPTIONS = { - 'subject': os.getenv('NATS_PAYMENT_RECONCILIATIONS_SUBJECT', 'payment.reconciliations'), - 'queue': os.getenv('NATS_PAYMENT_RECONCILIATIONS_QUEUE', 'payment-reconciliations-worker'), - 'durable_name': os.getenv('NATS_PAYMENT_RECONCILIATIONS_QUEUE', 'payment-reconciliations-worker') + '_durable', - } # Minio configuration values MINIO_ENDPOINT = os.getenv('MINIO_ENDPOINT') @@ -98,16 +80,6 @@ class _Config(): # pylint: disable=too-few-public-methods MINIO_ACCESS_SECRET = os.getenv('MINIO_ACCESS_SECRET') MINIO_SECURE = os.getenv('MINIO_SECURE', 'True').lower() == 'true' - # NATS Config - NATS_SERVERS = os.getenv('NATS_SERVERS', 'nats://127.0.0.1:4222').split(',') - NATS_CLUSTER_ID = os.getenv('NATS_CLUSTER_ID', 'test-cluster') - NATS_PAYMENT_CLIENT_NAME = os.getenv('NATS_PAYMENT_CLIENT_NAME', 'entity.filing.worker') - NATS_PAYMENT_SUBJECT = os.getenv('NATS_PAYMENT_SUBJECT', 'entity.{product}.payment') - NATS_MAILER_CLIENT_NAME = os.getenv('NATS_MAILER_CLIENT_NAME', 'account.mailer.worker') - NATS_MAILER_SUBJECT = os.getenv('NATS_MAILER_SUBJECT', 'account.mailer') - NATS_ACCOUNT_CLIENT_NAME = os.getenv('NATS_ACCOUNT_CLIENT_NAME', 'account.events.worker') - NATS_ACCOUNT_SUBJECT = os.getenv('NATS_ACCOUNT_SUBJECT', 'account.events') - # CFS API Settings CFS_BASE_URL = os.getenv('CFS_BASE_URL') CFS_CLIENT_ID = os.getenv('CFS_CLIENT_ID') @@ -125,6 +97,12 @@ class _Config(): # pylint: disable=too-few-public-methods # Disable PAD Success Email - Incase we need to reprocess records weeks/months later DISABLE_PAD_SUCCESS_EMAIL = os.getenv('DISABLE_PAD_SUCCESS_EMAIL', 'false').lower() == 'true' + # GCP PubSub + AUDIENCE = os.getenv('AUDIENCE', None) + GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) + PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) + class DevConfig(_Config): # pylint: disable=too-few-public-methods """Creates the Development Config object.""" @@ -152,7 +130,6 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' ) - TEST_NATS_DOCKER = os.getenv('TEST_NATS_DOCKER', None) USE_DOCKER_MOCK = os.getenv('USE_DOCKER_MOCK', None) # Minio variables @@ -166,16 +143,6 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods CFS_CLIENT_ID = 'TEST' CFS_CLIENT_SECRET = 'TEST' - # NATS Config - NATS_SERVERS = os.getenv('NATS_SERVERS', 'nats://127.0.0.1:4222').split(',') - NATS_CLUSTER_ID = os.getenv('NATS_CLUSTER_ID', 'test-cluster') - NATS_PAYMENT_CLIENT_NAME = os.getenv('NATS_PAYMENT_CLIENT_NAME', 'entity.filing.worker') - NATS_PAYMENT_SUBJECT = os.getenv('NATS_PAYMENT_SUBJECT', 'entity.{product}.payment') - NATS_MAILER_CLIENT_NAME = os.getenv('NATS_MAILER_CLIENT_NAME', 'account.mailer.worker') - NATS_MAILER_SUBJECT = os.getenv('NATS_MAILER_SUBJECT', 'account.mailer') - NATS_ACCOUNT_CLIENT_NAME = os.getenv('NATS_ACCOUNT_CLIENT_NAME', 'account.events.worker') - NATS_ACCOUNT_SUBJECT = os.getenv('NATS_ACCOUNT_SUBJECT', 'account.events') - # Secret key for encrypting bank account ACCOUNT_SECRET_KEY = os.getenv('ACCOUNT_SECRET_KEY', 'test') diff --git a/queue_services/payment-reconciliations/src/reconciliations/enums.py b/pay-queue/src/pay_queue/enums.py similarity index 86% rename from queue_services/payment-reconciliations/src/reconciliations/enums.py rename to pay-queue/src/pay_queue/enums.py index 92750f68e..cf16aabf2 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/enums.py +++ b/pay-queue/src/pay_queue/enums.py @@ -75,12 +75,3 @@ class TargetTransaction(Enum): DEBIT_MEMO = 'DM' CREDIT_MEMO = 'CM' RECEIPT = 'RECEIPT' - - -class MessageType(Enum): - """Event message types.""" - - CAS_UPLOADED = 'bc.registry.payment.casSettlementUploaded' - CGI_ACK_RECEIVED = 'bc.registry.payment.cgi.ACKReceived' - CGI_FEEDBACK_RECEIVED = 'bc.registry.payment.cgi.FEEDBACKReceived' - EFT_FILE_UPLOADED = 'bc.registry.payment.eft.fileUploaded' diff --git a/pay-queue/src/pay_queue/external/gcp_auth.py b/pay-queue/src/pay_queue/external/gcp_auth.py new file mode 100644 index 000000000..39121ede2 --- /dev/null +++ b/pay-queue/src/pay_queue/external/gcp_auth.py @@ -0,0 +1,40 @@ +# pylint: skip-file +# flake8: noqa +# This will get moved to an external library, which is linted by black (different than our rules) +"""Move this to external library.""" +import functools +from http import HTTPStatus + +import google.oauth2.id_token as id_token +from cachecontrol import CacheControl +from flask import abort, current_app, request +from google.auth.transport.requests import Request +from requests.sessions import Session + + +def verify_jwt(session): + """Verify token is valid.""" + msg = '' + try: + # Get the Cloud Pub/Sub-generated JWT in the "Authorization" header. + id_token.verify_oauth2_token( + request.headers.get('Authorization').split()[1], + Request(session=session), + audience=current_app.config.get('PAY_SUB_AUDIENCE') + ) + except Exception as e: # TODO fix + msg = f'Invalid token: {e}\n' + finally: + return msg + + +def ensure_authorized_queue_user(f): + """Ensures the user is authorized to use the queue.""" + @functools.wraps(f) + def decorated_function(*args, **kwargs): + # Use CacheControl to avoid re-fetching certificates for every request. + if message := verify_jwt(CacheControl(Session())): + print(message) + abort(HTTPStatus.UNAUTHORIZED) + return f(*args, **kwargs) + return decorated_function diff --git a/pay-queue/src/pay_queue/external/pubsub.py b/pay-queue/src/pay_queue/external/pubsub.py new file mode 100644 index 000000000..c116e1afd --- /dev/null +++ b/pay-queue/src/pay_queue/external/pubsub.py @@ -0,0 +1,196 @@ +# pylint: skip-file +# flake8: noqa +# This will get moved to an external library, which is linted by black (different than our rules) +# Copyright © 2023 Province of British Columbia +# +# Licensed under the BSD 3 Clause License, (the 'License'); +# you may not use this file except in compliance with the License. +# The template for the license can be found here +# https://opensource.org/license/bsd-3-clause/ +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +"""This module provides Queue type services.""" +from __future__ import annotations + +import base64 +import json +from concurrent.futures import TimeoutError # pylint: disable=W0622 +from concurrent.futures import CancelledError +from contextlib import suppress +from typing import Optional + +from flask import Flask +from google.auth import jwt +from google.cloud import pubsub_v1 +from simple_cloudevent import ( + CloudEventVersionException, + InvalidCloudEventError, + SimpleCloudEvent, + from_queue_message, + to_queue_message, +) +from werkzeug.local import LocalProxy + + +class GcpQueue: + """Provides Queue type services""" + + def __init__(self, app: Flask = None): + """Initializes the GCP Queue class""" + self.audience = None + self.credentials_pub = None + self.gcp_auth_key = None + self.publisher_audience = None + self.service_account_info = None + self._publisher = None + + if app: + self.init_app(app) + + def init_app(self, app: Flask): + """Initializes the application""" + + self.gcp_auth_key = app.config.get('GCP_AUTH_KEY') + if self.gcp_auth_key: + try: + audience = app.config.get( + 'AUDIENCE', + 'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber', + ) + publisher_audience = app.config.get( + 'PUBLISHER_AUDIENCE', + 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher', + ) + + self.service_account_info = json.loads( + base64.b64decode(self.gcp_auth_key).decode('utf-8')) + credentials = jwt.Credentials.from_service_account_info( + self.service_account_info, audience=audience) + self.credentials_pub = credentials.with_claims( + audience=publisher_audience) + except Exception as error: # noqa: B902 + raise Exception('Unable to create a connection', + error) from error # pylint: disable=W0719 + + @property + def publisher(self): + """Returns the publisher""" + + if not self._publisher and self.credentials_pub: + self._publisher = pubsub_v1.PublisherClient( + credentials=self.credentials_pub) + return self._publisher + + @staticmethod + def is_valid_envelope(msg: dict): + """Checks if the envelope is valid""" + + if ( + msg.get('subscription') + and (message := msg.get('message')) + and isinstance(message, dict) + and message.get('data') + ): + return True + return False + + @staticmethod + def get_envelope(request: LocalProxy) -> Optional[dict]: + """Returns the envelope""" + + with suppress(Exception): + if (envelope := request.get_json()) and GcpQueue.is_valid_envelope(envelope): + return envelope + return None + + @staticmethod + def get_simple_cloud_event(request: LocalProxy, return_raw: bool = False) -> type[SimpleCloudEvent | dict | None]: + """Return a SimpleCloudEvent if one is in session from the PubSub call. + + Parameters + ------------ + request: LocalProxy + An active Flask request object + return_raw: bool, Optional = False + Flag to return the raw data on error, if it exists + Return + ----------- + ce_returned: boolean + if a ce is returned == True + SimpleCloudEvent | + dict | + None + the second value returned is either a: + SimpleCloudEvent -or- + None - if there is no SimpleCloudEvent + + dict - if return_raw was set to true and it's not a SimpleCloudEvent -or- + """ + if not (envelope := GcpQueue.get_envelope(request)): + return None + + if ( + (message := envelope.get('message')) + and (raw_data := message.get('data')) + and (str_data := base64.b64decode(raw_data)) + ): + try: + return from_queue_message(str_data) + except ( + CloudEventVersionException, + InvalidCloudEventError, + ValueError, + Exception, + ): + if return_raw and str_data: + return str_data + return None + + def publish(self, topic: str, payload: bytes): + """Send payload to the queue.""" + if not (publisher := self.publisher): + raise Exception('missing setup arguments') # pylint: disable=W0719 + + try: + future = publisher.publish(topic, payload) + + return future.result() + except (CancelledError, TimeoutError) as error: + raise Exception('Unable to post to queue', + error) from error # pylint: disable=W0719 + + @staticmethod + def to_queue_message(ce: SimpleCloudEvent): + """Return a byte string of the CloudEvent in JSON format""" + + return to_queue_message(ce) + + @staticmethod + def from_queue_message(data: dict): + """Convert a queue message back to a simple CloudEvent""" + return from_queue_message(data) diff --git a/pay-queue/src/pay_queue/external/readme.txt b/pay-queue/src/pay_queue/external/readme.txt new file mode 100644 index 000000000..558309a53 --- /dev/null +++ b/pay-queue/src/pay_queue/external/readme.txt @@ -0,0 +1 @@ +These will be refactored in the future, just here now to get stuff going. diff --git a/queue_services/payment-reconciliations/src/reconciliations/minio.py b/pay-queue/src/pay_queue/minio.py similarity index 100% rename from queue_services/payment-reconciliations/src/reconciliations/minio.py rename to pay-queue/src/pay_queue/minio.py diff --git a/queue_services/payment-reconciliations/scripts/verify_license_headers.sh b/pay-queue/src/pay_queue/resources/__init__.py old mode 100755 new mode 100644 similarity index 50% rename from queue_services/payment-reconciliations/scripts/verify_license_headers.sh rename to pay-queue/src/pay_queue/resources/__init__.py index 028b95c63..cca349370 --- a/queue_services/payment-reconciliations/scripts/verify_license_headers.sh +++ b/pay-queue/src/pay_queue/resources/__init__.py @@ -1,6 +1,4 @@ -#!/usr/bin/env bash - -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,18 +11,20 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Resource package for the pay-queue service.""" +from flask import Flask +from pay_api.resources.ops import bp as ops_bp + +from .worker import bp as worker_endpoint -COPYRIGHT="Copyright © 2019 Province of British Columbia" -RET=0 +def register_endpoints(app: Flask): + """Register endpoints with the flask application.""" + # Allow base route to match with, and without a trailing slash + app.url_map.strict_slashes = False -for file in $(find $@ -not \( -path */venv -prune \) -not \( -path */migrations -prune \) -not \( -path */tests -prune \) -not \( -path */.egg* -prune \) -name \*.py) -do - grep "${COPYRIGHT}" ${file} >/dev/null - if [[ $? != 0 ]] - then - echo "${file} missing copyright header" - RET=1 - fi -done -exit ${RET} + app.register_blueprint( + url_prefix='/', + blueprint=worker_endpoint, + ) + app.register_blueprint(ops_bp) diff --git a/pay-queue/src/pay_queue/resources/worker.py b/pay-queue/src/pay_queue/resources/worker.py new file mode 100644 index 000000000..d429ec693 --- /dev/null +++ b/pay-queue/src/pay_queue/resources/worker.py @@ -0,0 +1,52 @@ +# Copyright © 2024 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Worker resource to handle incoming queue pushes from gcp.""" +from http import HTTPStatus + +from flask import Blueprint, request +from pay_api.utils.enums import MessageType + +from pay_queue.external.gcp_auth import ensure_authorized_queue_user +from pay_queue.services import queue, update_temporary_identifier +from pay_queue.services.cgi_reconciliations import reconcile_distributions +from pay_queue.services.eft.eft_reconciliation import reconcile_eft_payments +from pay_queue.services.payment_reconciliations import reconcile_payments + + +bp = Blueprint('worker', __name__) + + +@bp.route('/', methods=('POST',)) +@ensure_authorized_queue_user +def worker(): + """Worker to handle incoming queue pushes.""" + if not (ce := queue.get_simple_cloud_event(request)): + # Return a 200, so event is removed from the Queue + return {}, HTTPStatus.OK + + match ce.type: + case MessageType.CAS_UPLOADED.value: + reconcile_payments(ce.data) + case MessageType.CGI_ACK_RECEIVED.value: + reconcile_distributions(ce.data) + case MessageType.CGI_FEEDBACK_RECEIVED.value: + reconcile_distributions(ce.data, is_feedback=True) + case MessageType.EFT_FILE_UPLOADED.value: + reconcile_eft_payments(ce.data) + case MessageType.INCORPORATION.value | MessageType.REGISTRATION.value: + update_temporary_identifier(ce.data) + case _: + raise Exception('Invalid queue message type') # pylint: disable=broad-exception-raised + + return {}, HTTPStatus.OK diff --git a/pay-queue/src/pay_queue/services/__init__.py b/pay-queue/src/pay_queue/services/__init__.py new file mode 100644 index 000000000..63a1cf400 --- /dev/null +++ b/pay-queue/src/pay_queue/services/__init__.py @@ -0,0 +1,41 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the BSD 3 Clause License, (the "License"); +# you may not use this file except in compliance with the License. +# The template for the license can be found here +# https://opensource.org/license/bsd-3-clause/ +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +"""This module provides Queue type services.""" + +from pay_queue.external.pubsub import GcpQueue + +from .identifier_updater import update_temporary_identifier + + +queue = GcpQueue() diff --git a/queue_services/payment-reconciliations/src/reconciliations/cgi_reconciliations.py b/pay-queue/src/pay_queue/services/cgi_reconciliations.py similarity index 80% rename from queue_services/payment-reconciliations/src/reconciliations/cgi_reconciliations.py rename to pay-queue/src/pay_queue/services/cgi_reconciliations.py index 9e7e17f0a..d3040f9b9 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/cgi_reconciliations.py +++ b/pay-queue/src/pay_queue/services/cgi_reconciliations.py @@ -11,25 +11,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""CGI reconciliation file. - -The entry-point is the **cb_subscription_handler** - -The design and flow leverage a few constraints that are placed upon it -by NATS Streaming and using AWAIT on the default loop. -- NATS streaming queues require one message to be processed at a time. -- AWAIT on the default loop effectively runs synchronously - -If these constraints change, the use of Flask-SQLAlchemy would need to change. -Flask-SQLAlchemy currently allows the base model to be changed, or reworking -the model to a standalone SQLAlchemy usage with an async engine would need -to be pursued. -""" +"""CGI reconciliation file.""" import os from datetime import datetime from typing import Dict, List, Optional -from entity_queue_common.service_utils import logger +from flask import current_app from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel @@ -42,27 +29,28 @@ from pay_api.models import Refund as RefundModel from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db -from pay_api.services.queue_publisher import publish +from pay_api.services import gcp_queue_publisher +from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.utils.enums import ( - DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, PaymentSystem, - RoutingSlipStatus) + DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, + PaymentSystem, QueueSources, RoutingSlipStatus) from sentry_sdk import capture_message -from reconciliations import config -from reconciliations.minio import get_object +from pay_queue import config +from pay_queue.minio import get_object APP_CONFIG = config.get_named_config(os.getenv('DEPLOYMENT_ENV', 'production')) -async def reconcile_distributions(msg: Dict[str, any], is_feedback: bool = False): +def reconcile_distributions(msg: Dict[str, any], is_feedback: bool = False): """Read the file and update distribution details. 1: Lookup the invoice details based on the file content. 2: Update the statuses """ if is_feedback: - await _update_feedback(msg) + _update_feedback(msg) else: _update_acknowledgement(msg) @@ -72,11 +60,11 @@ def _update_acknowledgement(msg: Dict[str, any]): # so query uploaded jv file records and mark it as acknowledged. # Check to see that our ack file doesn't exist, if it exists, skip it. - ack_file_name = msg.get('data').get('fileName') + ack_file_name = msg.get('fileName') ack_exists: EjvFileModel = db.session.query(EjvFileModel).filter( EjvFileModel.ack_file_ref == ack_file_name).first() if ack_exists: - logger.warning('Ack file: %s - already exists, possible duplicate, skipping ack.', ack_file_name) + current_app.logger.warning('Ack file: %s - already exists, possible duplicate, skipping ack.', ack_file_name) return ejv_file: EjvFileModel = db.session.query(EjvFileModel).filter( @@ -98,24 +86,24 @@ def _update_acknowledgement(msg: Dict[str, any]): db.session.commit() -async def _update_feedback(msg: Dict[str, any]): # pylint:disable=too-many-locals, too-many-statements +def _update_feedback(msg: Dict[str, any]): # pylint:disable=too-many-locals, too-many-statements # Read the file and find records from the database, and update status. - file_name: str = msg.get('data').get('fileName') - minio_location: str = msg.get('data').get('location') + file_name: str = msg.get('fileName') + minio_location: str = msg.get('location') file = get_object(minio_location, file_name) content = file.data.decode('utf-8-sig') group_batches: List[str] = _group_batches(content) - has_errors, already_processed = await _process_ejv_feedback(group_batches['EJV'], file_name) + has_errors, already_processed = _process_ejv_feedback(group_batches['EJV'], file_name) if not already_processed: - has_errors = await _process_ap_feedback(group_batches['AP']) or has_errors + has_errors = _process_ap_feedback(group_batches['AP']) or has_errors if has_errors and not APP_CONFIG.DISABLE_EJV_ERROR_EMAIL: - await _publish_mailer_events(file_name, minio_location) - logger.info('> update_feedback') + _publish_mailer_events(file_name, minio_location) + current_app.logger.info('> update_feedback') -async def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:disable=too-many-locals +def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:disable=too-many-locals """Process EJV Feedback contents.""" has_errors = False already_processed = False @@ -132,7 +120,7 @@ async def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:dis batch_number = int(line[15:24]) ejv_file = EjvFileModel.find_by_id(batch_number) if ejv_file.feedback_file_ref: - logger.info( + current_app.logger.info( 'EJV file id %s with feedback file %s has already been processed, skipping.', batch_number, file_name) already_processed = True @@ -159,22 +147,22 @@ async def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:dis elif ejv_file.file_type == EjvFileType.PAYMENT.value: amount = float(line[42:57]) receipt_number = line[0:42].strip() - await _create_payment_record(amount, ejv_header, receipt_number) + _create_payment_record(amount, ejv_header, receipt_number) elif is_jv_detail: - has_errors = await _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number) + has_errors = _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number) db.session.commit() return has_errors, already_processed -async def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number): # pylint:disable=too-many-locals +def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number): # pylint:disable=too-many-locals journal_name: str = line[7:17] # {ministry}{ejv_header_model.id:0>8} ejv_header_model_id = int(journal_name[2:]) # Work around for CAS, they said fix the feedback files. line = _fix_invoice_line(line) invoice_id = int(line[205:315]) - logger.info('Invoice id - %s', invoice_id) + current_app.logger.info('Invoice id - %s', invoice_id) invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id) invoice_link: EjvInvoiceLinkModel = db.session.query(EjvInvoiceLinkModel).filter( EjvInvoiceLinkModel.ejv_header_id == ejv_header_model_id).filter( @@ -183,12 +171,12 @@ async def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_numbe invoice_return_message = line[319:469] # If the JV process failed, then mark the GL code against the invoice to be stopped # for further JV process for the credit GL. - logger.info('Is Credit or Debit %s - %s', line[104:105], ejv_file.file_type) + current_app.logger.info('Is Credit or Debit %s - %s', line[104:105], ejv_file.file_type) if line[104:105] == 'C' and ejv_file.file_type == EjvFileType.DISBURSEMENT.value: disbursement_status = _get_disbursement_status(invoice_return_code) invoice_link.disbursement_status_code = disbursement_status invoice_link.message = invoice_return_message - logger.info('disbursement_status %s', disbursement_status) + current_app.logger.info('disbursement_status %s', disbursement_status) if disbursement_status == DisbursementStatus.ERRORED.value: has_errors = True invoice.disbursement_status_code = DisbursementStatus.ERRORED.value @@ -202,17 +190,17 @@ async def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_numbe credit_distribution.stop_ejv = True else: effective_date = datetime.strptime(line[22:30], '%Y%m%d') - await _update_invoice_disbursement_status(invoice, effective_date) + _update_invoice_disbursement_status(invoice, effective_date) elif line[104:105] == 'D' and ejv_file.file_type == EjvFileType.PAYMENT.value: # This is for gov account payment JV. invoice_link.disbursement_status_code = _get_disbursement_status(invoice_return_code) invoice_link.message = invoice_return_message - logger.info('Invoice ID %s', invoice_id) + current_app.logger.info('Invoice ID %s', invoice_id) inv_ref: InvoiceReferenceModel = InvoiceReferenceModel.find_by_invoice_id_and_status( invoice_id, InvoiceReferenceStatus.ACTIVE.value) - logger.info('invoice_link.disbursement_status_code %s', invoice_link.disbursement_status_code) + current_app.logger.info('invoice_link.disbursement_status_code %s', invoice_link.disbursement_status_code) if invoice_link.disbursement_status_code == DisbursementStatus.ERRORED.value: has_errors = True # Cancel the invoice reference. @@ -264,7 +252,7 @@ def _fix_invoice_line(line): return line -async def _update_invoice_disbursement_status(invoice, effective_date: datetime): +def _update_invoice_disbursement_status(invoice, effective_date: datetime): """Update status to reversed if its a refund, else to completed.""" invoice.disbursement_date = effective_date if invoice.invoice_status_code in (InvoiceStatus.REFUNDED.value, InvoiceStatus.REFUND_REQUESTED.value, @@ -274,7 +262,7 @@ async def _update_invoice_disbursement_status(invoice, effective_date: datetime) invoice.disbursement_status_code = DisbursementStatus.COMPLETED.value -async def _create_payment_record(amount, ejv_header, receipt_number): +def _create_payment_record(amount, ejv_header, receipt_number): """Create payment record.""" PaymentModel( payment_system_code=PaymentSystem.CGI.value, @@ -315,33 +303,27 @@ def _get_disbursement_status(return_code: str) -> str: return DisbursementStatus.ERRORED.value -async def _publish_mailer_events(file_name: str, minio_location: str): +def _publish_mailer_events(file_name: str, minio_location: str): """Publish payment message to the mailer queue.""" - # Publish message to the Queue, saying account has been created. Using the event spec. - queue_data = { + payload = { 'fileName': file_name, 'minioLocation': minio_location } - payload = { - 'specversion': '1.x-wip', - 'type': 'bc.registry.payment.ejvFailed', - 'source': 'https://api.pay.bcregistry.gov.bc.ca/v1/accounts/', - 'id': file_name, - 'time': f'{datetime.now()}', - 'datacontenttype': 'application/json', - 'data': queue_data - } - try: - await publish(payload=payload, - client_name=APP_CONFIG.NATS_MAILER_CLIENT_NAME, - subject=APP_CONFIG.NATS_MAILER_SUBJECT) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_QUEUE.value, + message_type=MessageType.EJV_FAILED.value, + payload=payload, + topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') + ) + ) except Exception as e: # NOQA pylint: disable=broad-except - logger.error(e) + current_app.logger.error(e) capture_message('EJV Failed message error', level='error') -async def _process_ap_feedback(group_batches) -> bool: # pylint:disable=too-many-locals +def _process_ap_feedback(group_batches) -> bool: # pylint:disable=too-many-locals """Process AP Feedback contents.""" has_errors = False for group_batch in group_batches: @@ -362,22 +344,22 @@ async def _process_ap_feedback(group_batches) -> bool: # pylint:disable=too-man if ejv_file.disbursement_status_code == DisbursementStatus.ERRORED.value: has_errors = True elif is_ap_header: - has_errors = await _process_ap_header(line, ejv_file) or has_errors + has_errors = _process_ap_header(line, ejv_file) or has_errors db.session.commit() return has_errors -async def _process_ap_header(line, ejv_file: EjvFileModel) -> bool: +def _process_ap_header(line, ejv_file: EjvFileModel) -> bool: has_errors = False if ejv_file.file_type == EjvFileType.REFUND.value: - has_errors = await _process_ap_header_routing_slips(line) + has_errors = _process_ap_header_routing_slips(line) else: - has_errors = await _process_ap_header_non_gov_disbursement(line, ejv_file) + has_errors = _process_ap_header_non_gov_disbursement(line, ejv_file) return has_errors -async def _process_ap_header_routing_slips(line) -> bool: +def _process_ap_header_routing_slips(line) -> bool: has_errors = False routing_slip_number = line[19:69].strip() routing_slip: RoutingSlipModel = RoutingSlipModel.find_by_number(routing_slip_number) @@ -396,7 +378,7 @@ async def _process_ap_header_routing_slips(line) -> bool: return has_errors -async def _process_ap_header_non_gov_disbursement(line, ejv_file: EjvFileModel) -> bool: +def _process_ap_header_non_gov_disbursement(line, ejv_file: EjvFileModel) -> bool: has_errors = False invoice_id = line[19:69].strip() invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id) @@ -417,7 +399,7 @@ async def _process_ap_header_non_gov_disbursement(line, ejv_file: EjvFileModel) level='error') else: # TODO - Fix this on BC Assessment launch, so the effective date reads from the feedback. - await _update_invoice_disbursement_status(invoice, effective_date=datetime.now()) + _update_invoice_disbursement_status(invoice, effective_date=datetime.now()) if invoice.invoice_status_code != InvoiceStatus.PAID.value: refund = RefundModel.find_by_invoice_id(invoice.id) refund.gl_posted = datetime.now() diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/__init__.py b/pay-queue/src/pay_queue/services/eft/__init__.py similarity index 100% rename from queue_services/payment-reconciliations/src/reconciliations/eft/__init__.py rename to pay-queue/src/pay_queue/services/eft/__init__.py diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_base.py b/pay-queue/src/pay_queue/services/eft/eft_base.py similarity index 95% rename from queue_services/payment-reconciliations/src/reconciliations/eft/eft_base.py rename to pay-queue/src/pay_queue/services/eft/eft_base.py index 2a9982b01..13f396db7 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_base.py +++ b/pay-queue/src/pay_queue/services/eft/eft_base.py @@ -58,10 +58,11 @@ """This manages the EFT base class.""" import decimal from datetime import datetime +from typing import List -from reconciliations.eft.eft_enums import EFTConstants -from reconciliations.eft.eft_errors import EFTError -from reconciliations.eft.eft_parse_error import EFTParseError +from pay_queue.services.eft.eft_enums import EFTConstants +from pay_queue.services.eft.eft_errors import EFTError +from pay_queue.services.eft.eft_parse_error import EFTParseError class EFTBase: @@ -71,7 +72,7 @@ class EFTBase: record_type: str # Always 1 for header, 2 for transaction, 7 for trailer content: str index: int - errors: [EFTParseError] + errors: List[EFTParseError] def __init__(self, content: str, index: int): """Return an EFT Base record.""" @@ -148,6 +149,6 @@ def has_errors(self) -> bool: """Return true if the error array has elements.""" return len(self.errors) > 0 - def get_error_messages(self) -> [str]: + def get_error_messages(self) -> List[str]: """Return a string array of the error messages.""" return [error.message for error in self.errors] diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_enums.py b/pay-queue/src/pay_queue/services/eft/eft_enums.py similarity index 100% rename from queue_services/payment-reconciliations/src/reconciliations/eft/eft_enums.py rename to pay-queue/src/pay_queue/services/eft/eft_enums.py diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_errors.py b/pay-queue/src/pay_queue/services/eft/eft_errors.py similarity index 100% rename from queue_services/payment-reconciliations/src/reconciliations/eft/eft_errors.py rename to pay-queue/src/pay_queue/services/eft/eft_errors.py diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_header.py b/pay-queue/src/pay_queue/services/eft/eft_header.py similarity index 89% rename from queue_services/payment-reconciliations/src/reconciliations/eft/eft_header.py rename to pay-queue/src/pay_queue/services/eft/eft_header.py index a096ae06e..9244eafa3 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_header.py +++ b/pay-queue/src/pay_queue/services/eft/eft_header.py @@ -14,10 +14,10 @@ """This manages the EFT Header record.""" from datetime import datetime -from reconciliations.eft.eft_base import EFTBase -from reconciliations.eft.eft_enums import EFTConstants -from reconciliations.eft.eft_errors import EFTError -from reconciliations.eft.eft_parse_error import EFTParseError +from pay_queue.services.eft.eft_base import EFTBase +from pay_queue.services.eft.eft_enums import EFTConstants +from pay_queue.services.eft.eft_errors import EFTError +from pay_queue.services.eft.eft_parse_error import EFTParseError class EFTHeader(EFTBase): diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_parse_error.py b/pay-queue/src/pay_queue/services/eft/eft_parse_error.py similarity index 94% rename from queue_services/payment-reconciliations/src/reconciliations/eft/eft_parse_error.py rename to pay-queue/src/pay_queue/services/eft/eft_parse_error.py index b7348d89c..6ef7ad81c 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_parse_error.py +++ b/pay-queue/src/pay_queue/services/eft/eft_parse_error.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Defines the structure of EFT Errors.""" -from reconciliations.eft.eft_errors import EFTError +from pay_queue.services.eft.eft_errors import EFTError class EFTParseError: # pylint: disable=too-few-public-methods diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_reconciliation.py b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py similarity index 89% rename from queue_services/payment-reconciliations/src/reconciliations/eft/eft_reconciliation.py rename to pay-queue/src/pay_queue/services/eft/eft_reconciliation.py index d22c316bc..6cdc36122 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_reconciliation.py +++ b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py @@ -11,26 +11,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""EFT reconciliation file. - -The entry-point is the **cb_subscription_handler** - -The design and flow leverage a few constraints that are placed upon it -by NATS Streaming and using AWAIT on the default loop. -- NATS streaming queues require one message to be processed at a time. -- AWAIT on the default loop effectively runs synchronously - -If these constraints change, the use of Flask-SQLAlchemy would need to change. -Flask-SQLAlchemy currently allows the base model to be changed, or reworking -the model to a standalone SQLAlchemy usage with an async engine would need -to be pursued. -""" +"""EFT reconciliation file.""" from datetime import datetime from operator import and_ from typing import Dict, List from _decimal import Decimal -from entity_queue_common.service_utils import logger +from flask import current_app from pay_api import db from pay_api.factory.payment_system_factory import PaymentSystemFactory from pay_api.models import EFTCredit as EFTCreditModel @@ -44,11 +31,11 @@ from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, InvoiceStatus, PaymentMethod from sentry_sdk import capture_message -from reconciliations.eft import EFTHeader, EFTRecord, EFTTrailer -from reconciliations.minio import get_object +from pay_queue.minio import get_object +from pay_queue.services.eft import EFTHeader, EFTRecord, EFTTrailer -async def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-many-locals +def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-many-locals """Read the TDI17 file, create processing records and update payment details. 1: Check to see if file has been previously processed. @@ -65,8 +52,8 @@ async def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-ma 9: Finalize and complete """ # Fetch EFT File - file_name: str = msg.get('data').get('fileName') - minio_location: str = msg.get('data').get('location') + file_name: str = msg.get('fileName') + minio_location: str = msg.get('location') file = get_object(minio_location, file_name) file_content = file.data.decode('utf-8-sig') @@ -78,7 +65,7 @@ async def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-ma EFTFileModel.file_ref == file_name).one_or_none() if eft_file_model and eft_file_model.status_code == EFTProcessStatus.COMPLETED.value: - logger.info('File: %s already completed processing on %s.', file_name, eft_file_model.completed_on) + current_app.logger.info('File: %s already completed processing on %s.', file_name, eft_file_model.completed_on) return # There is no existing EFT File record - instantiate one @@ -93,7 +80,7 @@ async def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-ma # EFT File parsed data holders eft_header: EFTHeader = None eft_trailer: EFTTrailer = None - eft_transactions: [EFTRecord] = [] + eft_transactions: List[EFTRecord] = [] # Read and parse EFT file header, trailer, transactions for index, line in enumerate(lines): @@ -109,7 +96,7 @@ async def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-ma # If header and/or trailer has errors do not proceed if not (eft_header_valid and eft_trailer_valid): - logger.error('Failed to process file %s with an invalid header or trailer.', file_name) + current_app.logger.error('Failed to process file %s with an invalid header or trailer.', file_name) eft_file_model.status_code = EFTProcessStatus.FAILED.value eft_file_model.save() return @@ -128,7 +115,7 @@ async def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-ma # EFT Transactions have parsing errors - stop and FAIL transactions # We want a full file to be parseable as we want to get a full accurate balance before applying them to invoices if has_eft_transaction_errors: - logger.error('Failed to process file %s has transaction parsing errors.', file_name) + current_app.logger.error('Failed to process file %s has transaction parsing errors.', file_name) _update_transactions_to_fail(eft_file_model) return @@ -146,7 +133,7 @@ async def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-ma if has_eft_transaction_errors or has_eft_credits_error: db.session.rollback() _update_transactions_to_fail(eft_file_model) - logger.error('Failed to process file %s due to transaction errors.', file_name) + current_app.logger.error('Failed to process file %s due to transaction errors.', file_name) return _finalize_process_state(eft_file_model) @@ -165,7 +152,7 @@ def _finalize_process_state(eft_file_model: EFTFileModel): def _process_eft_header(eft_header: EFTHeader, eft_file_model: EFTFileModel) -> bool: """Process the EFT Header.""" if eft_header is None: - logger.error('Failed to process file %s with an invalid header.', eft_file_model.file_ref) + current_app.logger.error('Failed to process file %s with an invalid header.', eft_file_model.file_ref) return False # Populate header and trailer data on EFT File record - values will return None if parsing failed @@ -182,7 +169,7 @@ def _process_eft_header(eft_header: EFTHeader, eft_file_model: EFTFileModel) -> def _process_eft_trailer(eft_trailer: EFTTrailer, eft_file_model: EFTFileModel) -> bool: """Process the EFT Trailer.""" if eft_trailer is None: - logger.error('Failed to process file %s with an invalid trailer.', eft_file_model.file_ref) + current_app.logger.error('Failed to process file %s with an invalid trailer.', eft_file_model.file_ref) return False # Populate header and trailer data on EFT File record - values will return None if parsing failed @@ -237,7 +224,7 @@ def _process_eft_credits(shortname_balance, eft_file_id): db.session.add(eft_credit_model) except Exception as e: # NOQA pylint: disable=broad-exception-caught has_credit_errors = True - logger.error(e) + current_app.logger.error(e) capture_message('EFT Failed to set EFT balance.', level='error') return has_credit_errors @@ -252,7 +239,8 @@ def _process_eft_payments(shortname_balance: Dict, eft_file: EFTFileModel) -> bo # No balance to apply - move to next shortname if shortname_balance[shortname]['balance'] <= 0: - logger.warning('UNEXPECTED BALANCE: %s had zero or less balance on file: %s', shortname, eft_file.file_ref) + current_app.logger.warning('UNEXPECTED BALANCE: %s had zero or less balance on file: %s', + shortname, eft_file.file_ref) continue # check if short name is mapped to an auth account @@ -262,14 +250,12 @@ def _process_eft_payments(shortname_balance: Dict, eft_file: EFTFileModel) -> bo auth_account_id = eft_shortname_model.auth_account_id # Find invoices to be paid invoices: List[InvoiceModel] = EFTShortnames.get_invoices_owing(auth_account_id) - if invoices is not None: - for invoice in invoices: - _pay_invoice(invoice=invoice, - shortname_balance=shortname_balance[shortname]) + for invoice in invoices: + _pay_invoice(invoice=invoice, shortname_balance=shortname_balance[shortname]) except Exception as e: # NOQA pylint: disable=broad-exception-caught has_eft_transaction_errors = True - logger.error(e) + current_app.logger.error(e) capture_message('EFT Failed to apply balance to invoice.', level='error') return has_eft_transaction_errors @@ -433,6 +419,7 @@ def _shortname_balance_as_dict(eft_transactions: List[EFTRecord]) -> Dict: return shortname_balance +# TODO: THIS NEEDS TO CHANGE TO WORK ON THE CFS JOB instead. def _pay_invoice(invoice: InvoiceModel, shortname_balance: Dict): """Pay for an invoice and update invoice state.""" payment_date = shortname_balance.get('transaction_date') or datetime.now() @@ -447,12 +434,11 @@ def _pay_invoice(invoice: InvoiceModel, shortname_balance: Dict): # Create the payment record eft_payment_service: EFTService = PaymentSystemFactory.create_from_payment_method(PaymentMethod.EFT.value) - payment, invoice_reference, receipt = eft_payment_service.apply_credit(invoice=invoice, - payment_date=payment_date, - auto_save=True) + payment, receipt = eft_payment_service.apply_credit(invoice=invoice, + payment_date=payment_date, + auto_save=True) db.session.add(payment) - db.session.add(invoice_reference) db.session.add(receipt) # Paid - update the shortname balance diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_record.py b/pay-queue/src/pay_queue/services/eft/eft_record.py similarity index 94% rename from queue_services/payment-reconciliations/src/reconciliations/eft/eft_record.py rename to pay-queue/src/pay_queue/services/eft/eft_record.py index 19acedc30..4c002754e 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_record.py +++ b/pay-queue/src/pay_queue/services/eft/eft_record.py @@ -15,10 +15,10 @@ import decimal from datetime import datetime -from reconciliations.eft.eft_base import EFTBase -from reconciliations.eft.eft_enums import EFTConstants -from reconciliations.eft.eft_errors import EFTError -from reconciliations.eft.eft_parse_error import EFTParseError +from pay_queue.services.eft.eft_base import EFTBase +from pay_queue.services.eft.eft_enums import EFTConstants +from pay_queue.services.eft.eft_errors import EFTError +from pay_queue.services.eft.eft_parse_error import EFTParseError class EFTRecord(EFTBase): diff --git a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_trailer.py b/pay-queue/src/pay_queue/services/eft/eft_trailer.py similarity index 88% rename from queue_services/payment-reconciliations/src/reconciliations/eft/eft_trailer.py rename to pay-queue/src/pay_queue/services/eft/eft_trailer.py index 8ee4fd999..3ead1dd01 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/eft/eft_trailer.py +++ b/pay-queue/src/pay_queue/services/eft/eft_trailer.py @@ -14,10 +14,10 @@ """This manages the EFT Trailer record.""" import decimal -from reconciliations.eft.eft_base import EFTBase -from reconciliations.eft.eft_enums import EFTConstants -from reconciliations.eft.eft_errors import EFTError -from reconciliations.eft.eft_parse_error import EFTParseError +from pay_queue.services.eft.eft_base import EFTBase +from pay_queue.services.eft.eft_enums import EFTConstants +from pay_queue.services.eft.eft_errors import EFTError +from pay_queue.services.eft.eft_parse_error import EFTParseError class EFTTrailer(EFTBase): diff --git a/pay-queue/src/pay_queue/services/identifier_updater.py b/pay-queue/src/pay_queue/services/identifier_updater.py new file mode 100644 index 000000000..dba0322e4 --- /dev/null +++ b/pay-queue/src/pay_queue/services/identifier_updater.py @@ -0,0 +1,34 @@ +# Copyright © 2024 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Updates the temporary identifier to a permanent identifier in the invoice table.""" +from flask import current_app +from pay_api.models import db +from pay_api.models.invoice import Invoice + + +def update_temporary_identifier(event_message): + """Update a temporary identifier to a permanent identifier.""" + if 'tempidentifier' not in event_message or event_message.get('tempidentifier', None) is None: + return + + old_identifier = event_message.get('tempidentifier') + new_identifier = event_message.get('identifier') + current_app.logger.debug('Received message to update %s to %s', old_identifier, new_identifier) + + invoices = Invoice.find_by_business_identifier(old_identifier) + for inv in invoices: + inv.business_identifier = new_identifier + inv.flush() + + db.session.commit() diff --git a/queue_services/payment-reconciliations/src/reconciliations/payment_reconciliations.py b/pay-queue/src/pay_queue/services/payment_reconciliations.py similarity index 80% rename from queue_services/payment-reconciliations/src/reconciliations/payment_reconciliations.py rename to pay-queue/src/pay_queue/services/payment_reconciliations.py index 055c190dc..de5cefeb7 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/payment_reconciliations.py +++ b/pay-queue/src/pay_queue/services/payment_reconciliations.py @@ -11,27 +11,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Payment reconciliation file. - -The entry-point is the **cb_subscription_handler** - -The design and flow leverage a few constraints that are placed upon it -by NATS Streaming and using AWAIT on the default loop. -- NATS streaming queues require one message to be processed at a time. -- AWAIT on the default loop effectively runs synchronously - -If these constraints change, the use of Flask-SQLAlchemy would need to change. -Flask-SQLAlchemy currently allows the base model to be changed, or reworking -the model to a standalone SQLAlchemy usage with an async engine would need -to be pursued. -""" +"""Payment reconciliation file.""" import csv import os from datetime import datetime from decimal import Decimal from typing import Dict, List, Tuple -from entity_queue_common.service_utils import logger +from flask import current_app from pay_api.models import CasSettlement as CasSettlementModel from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import Credit as CreditModel @@ -44,25 +31,27 @@ from pay_api.models import PaymentLineItem as PaymentLineItemModel from pay_api.models import Receipt as ReceiptModel from pay_api.models import db +from pay_api.services import gcp_queue_publisher from pay_api.services.cfs_service import CFSService +from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.services.non_sufficient_funds import NonSufficientFundsService from pay_api.services.payment_transaction import PaymentTransaction as PaymentTransactionService -from pay_api.services.queue_publisher import publish from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, PaymentStatus) -from pay_api.utils.util import get_pay_subject_name + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, MessageType, PaymentMethod, PaymentStatus, + QueueSources) +from pay_api.utils.util import get_topic_for_corp_type from sentry_sdk import capture_message -from reconciliations import config -from reconciliations.minio import get_object +from pay_queue import config +from pay_queue.minio import get_object -from .enums import Column, RecordType, SourceTransaction, Status, TargetTransaction +from ..enums import Column, RecordType, SourceTransaction, Status, TargetTransaction APP_CONFIG = config.get_named_config(os.getenv('DEPLOYMENT_ENV', 'production')) -async def _create_payment_records(csv_content: str): +def _create_payment_records(csv_content: str): """Create payment records by grouping the lines with target transaction number.""" # Iterate the rows and create a dict with key as the source transaction number. source_txns: Dict[str, List[Dict[str, str]]] = {} @@ -116,8 +105,7 @@ async def _create_payment_records(csv_content: str): _save_payment(payment_date, inv_number, invoice_amount, paid_amount, row, PaymentStatus.COMPLETED.value, PaymentMethod.ONLINE_BANKING.value, source_txn_number) - # publish email event. - await _publish_online_banking_mailer_events(payment_lines, paid_amount) + _publish_online_banking_mailer_events(payment_lines, paid_amount) elif settlement_type == RecordType.EFTP.value: # Find the payment using receipt_number and mark it as COMPLETED @@ -186,7 +174,7 @@ def _get_payment_by_inv_number_and_status(inv_number: str, status: str) -> Payme return payment -async def reconcile_payments(msg: Dict[str, any]): +def reconcile_payments(msg: Dict[str, any]): """Read the file and update payment details. 1: Check to see if file has been processed already. @@ -198,18 +186,19 @@ async def reconcile_payments(msg: Dict[str, any]): 3.3 : If transaction status is PARTIAL, update payment and invoice status, publish to account mailer. 4: If the transaction is On Account for Credit, apply the credit to the account. """ - file_name: str = msg.get('data').get('fileName') - minio_location: str = msg.get('data').get('location') + file_name: str = msg.get('fileName') + minio_location: str = msg.get('location') cas_settlement: CasSettlementModel = db.session.query(CasSettlementModel) \ .filter(CasSettlementModel.file_name == file_name).one_or_none() if cas_settlement and not cas_settlement.processed_on: - logger.info('File: %s has attempted to be processed before.', file_name) + current_app.logger.info('File: %s has attempted to be processed before.', file_name) elif cas_settlement and cas_settlement.processed_on: - logger.info('File: %s already processed on: %s. Skipping file.', file_name, cas_settlement.processed_on) + current_app.logger.info('File: %s already processed on: %s. Skipping file.', + file_name, cas_settlement.processed_on) return else: - logger.info('Creating cas_settlement record for file: %s', file_name) + current_app.logger.info('Creating cas_settlement record for file: %s', file_name) cas_settlement = _create_cas_settlement(file_name) file = get_object(minio_location, file_name) @@ -218,7 +207,7 @@ async def reconcile_payments(msg: Dict[str, any]): for row in csv.DictReader(content.splitlines()): # Convert lower case keys to avoid any key mismatch row = dict((k.lower(), v) for k, v in row.items()) - logger.debug('Processing %s', row) + current_app.logger.debug('Processing %s', row) # IF not PAD and application amount is zero, continue record_type = _get_row_value(row, Column.RECORD_TYPE) @@ -234,17 +223,17 @@ async def reconcile_payments(msg: Dict[str, any]): # PS : Duplicating some code to make the code more readable. if record_type in pad_record_types: # Handle invoices - await _process_consolidated_invoices(row) + _process_consolidated_invoices(row) elif record_type in (RecordType.BOLP.value, RecordType.EFTP.value): # EFT, WIRE and Online Banking are one-to-one invoice. So handle them in same way. - await _process_unconsolidated_invoices(row) + _process_unconsolidated_invoices(row) elif record_type in (RecordType.ONAC.value, RecordType.CMAP.value, RecordType.DRWP.value): - await _process_credit_on_invoices(row) + _process_credit_on_invoices(row) elif record_type == RecordType.ADJS.value: - logger.info('Adjustment received for %s.', msg) + current_app.logger.info('Adjustment received for %s.', msg) else: # For any other transactions like DM log error and continue. - logger.error('Record Type is received as %s, and cannot process %s.', record_type, msg) + current_app.logger.error('Record Type is received as %s, and cannot process %s.', record_type, msg) capture_message(f'Record Type is received as {record_type}, and cannot process {msg}.', level='error') # Continue processing @@ -252,7 +241,7 @@ async def reconcile_payments(msg: Dict[str, any]): db.session.commit() # Create payment records for lines other than PAD - await _create_payment_records(content) + _create_payment_records(content) # Create Credit Records. _create_credit_records(content) @@ -263,41 +252,43 @@ async def reconcile_payments(msg: Dict[str, any]): cas_settlement.save() -async def _process_consolidated_invoices(row): +def _process_consolidated_invoices(row): target_txn_status = _get_row_value(row, Column.TARGET_TXN_STATUS) if (target_txn := _get_row_value(row, Column.TARGET_TXN)) == TargetTransaction.INV.value: inv_number = _get_row_value(row, Column.TARGET_TXN_NO) record_type = _get_row_value(row, Column.RECORD_TYPE) - logger.debug('Processing invoice : %s', inv_number) + current_app.logger.debug('Processing invoice : %s', inv_number) inv_references = _find_invoice_reference_by_number_and_status(inv_number, InvoiceReferenceStatus.ACTIVE.value) payment_account: PaymentAccountModel = _get_payment_account(row) if target_txn_status.lower() == Status.PAID.value.lower(): - logger.debug('Fully PAID payment.') + current_app.logger.debug('Fully PAID payment.') # if no inv reference is found, and if there are no COMPLETED inv ref, raise alert completed_inv_references = _find_invoice_reference_by_number_and_status( inv_number, InvoiceReferenceStatus.COMPLETED.value ) if not inv_references and not completed_inv_references: - logger.error('No invoice found for %s in the system, and cannot process %s.', inv_number, row) + current_app.logger.error('No invoice found for %s in the system, and cannot process %s.', + inv_number, row) capture_message(f'No invoice found for {inv_number} in the system, and cannot process {row}.', level='error') return - await _process_paid_invoices(inv_references, row) + _process_paid_invoices(inv_references, row) if not APP_CONFIG.DISABLE_PAD_SUCCESS_EMAIL: - await _publish_mailer_events('PAD.PaymentSuccess', payment_account, row) + _publish_mailer_events(MessageType.PAD_PAYMENT_SUCCESS.value, payment_account, row) elif target_txn_status.lower() == Status.NOT_PAID.value.lower() \ or record_type in (RecordType.PADR.value, RecordType.PAYR.value): - logger.info('NOT PAID. NSF identified.') + current_app.logger.info('NOT PAID. NSF identified.') # NSF Condition. Publish to account events for NSF. if _process_failed_payments(row): # Send mailer and account events to update status and send email notification - await _publish_account_events('lockAccount', payment_account, row) + _publish_account_events(MessageType.NSF_LOCK_ACCOUNT.value, payment_account, row) else: - logger.error('Target Transaction Type is received as %s for PAD, and cannot process %s.', target_txn, row) + current_app.logger.error('Target Transaction Type is received as %s for PAD, and cannot process %s.', + target_txn, row) capture_message( f'Target Transaction Type is received as {target_txn} for PAD, and cannot process.', level='error') @@ -310,7 +301,7 @@ def _find_invoice_reference_by_number_and_status(inv_number: str, status: str): return inv_references -async def _process_unconsolidated_invoices(row): +def _process_unconsolidated_invoices(row): target_txn_status = _get_row_value(row, Column.TARGET_TXN_STATUS) record_type = _get_row_value(row, Column.RECORD_TYPE) if (target_txn := _get_row_value(row, Column.TARGET_TXN)) == TargetTransaction.INV.value: @@ -328,37 +319,37 @@ async def _process_unconsolidated_invoices(row): filter(InvoiceReferenceModel.status_code == InvoiceReferenceStatus.COMPLETED.value). \ filter(InvoiceReferenceModel.invoice_number == inv_number). \ all() - logger.info('Found %s completed invoice references for invoice number %s', len(completed_inv_references), - inv_number) + current_app.logger.info('Found %s completed invoice references for invoice number %s', + len(completed_inv_references), inv_number) if len(completed_inv_references) != 1: - logger.error('More than one or none invoice reference received for invoice number %s for %s', - inv_number, record_type) + current_app.logger.error('More than one or none invoice reference received ' + 'for invoice number %s for %s', inv_number, record_type) capture_message( f'More than one or none invoice reference received for invoice number {inv_number} for ' f'{record_type}', level='error') else: # Handle fully PAID and Partially Paid scenarios. if target_txn_status.lower() == Status.PAID.value.lower(): - logger.debug('Fully PAID payment.') - await _process_paid_invoices(inv_references, row) + current_app.logger.debug('Fully PAID payment.') + _process_paid_invoices(inv_references, row) elif target_txn_status.lower() == Status.PARTIAL.value.lower(): - logger.info('Partially PAID.') + current_app.logger.info('Partially PAID.') # As per validation above, get first and only inv ref _process_partial_paid_invoices(inv_references[0], row) else: - logger.error('Target Transaction Type is received as %s for %s, and cannot process.', - target_txn, record_type) + current_app.logger.error('Target Transaction Type is received as %s for %s, and cannot process.', + target_txn, record_type) capture_message( f'Target Transaction Type is received as {target_txn} for {record_type}, and cannot process.', level='error') -async def _process_credit_on_invoices(row): +def _process_credit_on_invoices(row): # Credit memo can happen for any type of accounts. target_txn_status = _get_row_value(row, Column.TARGET_TXN_STATUS) if _get_row_value(row, Column.TARGET_TXN) == TargetTransaction.INV.value: inv_number = _get_row_value(row, Column.TARGET_TXN_NO) - logger.debug('Processing invoice : %s', inv_number) + current_app.logger.debug('Processing invoice : %s', inv_number) inv_references: List[InvoiceReferenceModel] = db.session.query(InvoiceReferenceModel). \ filter(InvoiceReferenceModel.status_code == InvoiceReferenceStatus.ACTIVE.value). \ @@ -366,18 +357,20 @@ async def _process_credit_on_invoices(row): all() if target_txn_status.lower() == Status.PAID.value.lower(): - logger.debug('Fully PAID payment.') - await _process_paid_invoices(inv_references, row) + current_app.logger.debug('Fully PAID payment.') + _process_paid_invoices(inv_references, row) elif target_txn_status.lower() == Status.PARTIAL.value.lower(): - logger.info('Partially PAID using credit memo. Ignoring as the credit memo payment is already captured.') + current_app.logger.info('Partially PAID using credit memo. ' + 'Ignoring as the credit memo payment is already captured.') else: - logger.error('Target Transaction status is received as %s for CMAP, and cannot process.', target_txn_status) + current_app.logger.error('Target Transaction status is received as %s for CMAP, and cannot process.', + target_txn_status) capture_message( f'Target Transaction status is received as {target_txn_status} for CMAP, and cannot process.', level='error') -async def _process_paid_invoices(inv_references, row): +def _process_paid_invoices(inv_references, row): """Process PAID invoices. Update invoices as PAID @@ -388,7 +381,7 @@ async def _process_paid_invoices(inv_references, row): for inv_ref in inv_references: invoice: InvoiceModel = InvoiceModel.find_by_id(inv_ref.invoice_id) if invoice.payment_method_code == PaymentMethod.CC.value: - logger.info('Cannot mark CC invoices as PAID.') + current_app.logger.info('Cannot mark CC invoices as PAID.') return receipt_date: datetime = datetime.strptime(_get_row_value(row, Column.APP_DATE), '%d-%b-%y') @@ -398,7 +391,8 @@ async def _process_paid_invoices(inv_references, row): # Find invoice, update status inv: InvoiceModel = InvoiceModel.find_by_id(inv_ref.invoice_id) _validate_account(inv, row) - logger.debug('PAID Invoice. Invoice Reference ID : %s, invoice ID : %s', inv_ref.id, inv_ref.invoice_id) + current_app.logger.debug('PAID Invoice. Invoice Reference ID : %s, invoice ID : %s', + inv_ref.id, inv_ref.invoice_id) inv.invoice_status_code = InvoiceStatus.PAID.value inv.payment_date = receipt_date @@ -412,8 +406,8 @@ async def _process_paid_invoices(inv_references, row): db.session.add(receipt) # Publish to the queue if it's an Online Banking payment if inv.payment_method_code == PaymentMethod.ONLINE_BANKING.value: - logger.debug('Publishing payment event for OB. Invoice : %s', inv.id) - await _publish_payment_event(inv) + current_app.logger.debug('Publishing payment event for OB. Invoice : %s', inv.id) + _publish_payment_event(inv) def _process_partial_paid_invoices(inv_ref: InvoiceReferenceModel, row): @@ -428,7 +422,8 @@ def _process_partial_paid_invoices(inv_ref: InvoiceReferenceModel, row): inv: InvoiceModel = InvoiceModel.find_by_id(inv_ref.invoice_id) _validate_account(inv, row) - logger.debug('Partial Invoice. Invoice Reference ID : %s, invoice ID : %s', inv_ref.id, inv_ref.invoice_id) + current_app.logger.debug('Partial Invoice. Invoice Reference ID : %s, invoice ID : %s', + inv_ref.id, inv_ref.invoice_id) inv.invoice_status_code = InvoiceStatus.PARTIAL.value inv.paid = inv.total - Decimal(_get_row_value(row, Column.TARGET_TXN_OUTSTANDING)) # Create Receipt records @@ -457,22 +452,22 @@ def _process_failed_payments(row): inv_number, PaymentStatus.FAILED.value ) if payment: - logger.info('Ignoring duplicate NSF message for invoice : %s ', inv_number) + current_app.logger.info('Ignoring duplicate NSF message for invoice : %s ', inv_number) return False # If there is an NSF row, it means it's a duplicate NSF event. Ignore it. if NonSufficientFundsService.exists_for_invoice_number(inv_number): - logger.info('Ignoring duplicate NSF event for account: %s ', payment_account.auth_account_id) + current_app.logger.info('Ignoring duplicate NSF event for account: %s ', payment_account.auth_account_id) return False # Set CFS Account Status. cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) is_already_frozen = cfs_account.status == CfsAccountStatus.FREEZE.value - logger.info('setting payment account id : %s status as FREEZE', payment_account.id) + current_app.logger.info('setting payment account id : %s status as FREEZE', payment_account.id) cfs_account.status = CfsAccountStatus.FREEZE.value # Call CFS to stop any further PAD transactions on this account. CFSService.suspend_cfs_account(cfs_account) if is_already_frozen: - logger.info('Ignoring NSF message for invoice : %s as the account is already FREEZE', inv_number) + current_app.logger.info('Ignoring NSF message for invoice : %s as the account is already FREEZE', inv_number) return False # Find the invoice_reference for this invoice and mark it as ACTIVE. inv_references: List[InvoiceReferenceModel] = db.session.query(InvoiceReferenceModel). \ @@ -529,7 +524,7 @@ def _sync_credit_records(): # 3. If it's credit memo, call credit memo endpoint and calculate balance. # 4. Roll up the credits to credit field in payment_account. active_credits: List[CreditModel] = db.session.query(CreditModel).filter(CreditModel.remaining_amount > 0).all() - logger.info('Found %s credit records', len(active_credits)) + current_app.logger.info('Found %s credit records', len(active_credits)) account_ids: List[int] = [] for credit in active_credits: cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(credit.account_id) @@ -582,45 +577,56 @@ def _validate_account(inv: InvoiceModel, row: Dict[str, str]): # This should never happen, just in case cfs_account: CfsAccountModel = CfsAccountModel.find_by_id(inv.cfs_account_id) if (account_number := _get_row_value(row, Column.CUSTOMER_ACC)) != cfs_account.cfs_account: - logger.error('Customer Account received as %s, but expected %s.', account_number, cfs_account.cfs_account) + current_app.logger.error('Customer Account received as %s, but expected %s.', + account_number, cfs_account.cfs_account) capture_message(f'Customer Account received as {account_number}, but expected {cfs_account.cfs_account}.', level='error') raise Exception('Invalid Account Number') # pylint: disable=broad-exception-raised -async def _publish_payment_event(inv: InvoiceModel): +def _publish_payment_event(inv: InvoiceModel): """Publish payment message to the queue.""" - payment_event_payload = PaymentTransactionService.create_event_payload(invoice=inv, - status_code=PaymentStatus.COMPLETED.value) + payload = PaymentTransactionService.create_event_payload(invoice=inv, + status_code=PaymentStatus.COMPLETED.value) try: - - await publish(payload=payment_event_payload, client_name=APP_CONFIG.NATS_PAYMENT_CLIENT_NAME, - subject=get_pay_subject_name(inv.corp_type_code, subject_format=APP_CONFIG.NATS_PAYMENT_SUBJECT)) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_QUEUE.value, + message_type=MessageType.PAYMENT.value, + payload=payload, + topic=get_topic_for_corp_type(inv.corp_type_code) + ) + ) except Exception as e: # NOQA pylint: disable=broad-except - logger.error(e) - logger.warning('Notification to Queue failed for the Payment Event - %s', payment_event_payload) - capture_message(f'Notification to Queue failed for the Payment Event {payment_event_payload}.', + current_app.logger.error(e) + current_app.logger.warning('Notification to Queue failed for the Payment Event - %s', payload) + capture_message(f'Notification to Queue failed for the Payment Event {payload}.', level='error') -async def _publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, row: Dict[str, str]): +def _publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, row: Dict[str, str]): """Publish payment message to the mailer queue.""" # Publish message to the Queue, saying account has been created. Using the event spec. - payload = _create_event_payload(message_type, pay_account, row) + payload = _create_event_payload(pay_account, row) try: - await publish(payload=payload, - client_name=APP_CONFIG.NATS_MAILER_CLIENT_NAME, - subject=APP_CONFIG.NATS_MAILER_SUBJECT) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_QUEUE.value, + message_type=message_type, + payload=payload, + topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') + ) + ) except Exception as e: # NOQA pylint: disable=broad-except - logger.error(e) - logger.warning('Notification to Queue failed for the Account Mailer %s - %s', pay_account.auth_account_id, - payload) + current_app.logger.error(e) + current_app.logger.warning('Notification to Queue failed for the Account Mailer %s - %s', + pay_account.auth_account_id, payload) capture_message('Notification to Queue failed for the Account Mailer {auth_account_id}, {msg}.'.format( auth_account_id=pay_account.auth_account_id, msg=payload), level='error') -async def _publish_online_banking_mailer_events(rows: List[Dict[str, str]], paid_amount: float): +def _publish_online_banking_mailer_events(rows: List[Dict[str, str]], paid_amount: float): """Publish payment message to the mailer queue.""" # Publish message to the Queue, saying account has been created. Using the event spec. pay_account = _get_payment_account(rows[0]) # All rows are for same account. @@ -640,70 +646,60 @@ async def _publish_online_banking_mailer_events(rows: List[Dict[str, str]], paid else: message_type = 'bc.registry.payment.Payment' - queue_data = { + payload = { 'accountId': pay_account.auth_account_id, 'paymentMethod': PaymentMethod.ONLINE_BANKING.value, 'amount': '{:.2f}'.format(paid_amount), # pylint: disable = consider-using-f-string 'creditAmount': '{:.2f}'.format(credit_amount) # pylint: disable = consider-using-f-string } - payload = { - 'specversion': '1.x-wip', - 'type': message_type, - 'source': f'https://api.pay.bcregistry.gov.bc.ca/v1/accounts/{pay_account.auth_account_id}', - 'id': f'{pay_account.auth_account_id}', - 'time': f'{datetime.now()}', - 'datacontenttype': 'application/json', - 'data': queue_data - } - try: - await publish(payload=payload, - client_name=APP_CONFIG.NATS_MAILER_CLIENT_NAME, - subject=APP_CONFIG.NATS_MAILER_SUBJECT) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_QUEUE.value, + message_type=message_type, + payload=payload, + topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') + ) + ) except Exception as e: # NOQA pylint: disable=broad-except - logger.error(e) - logger.warning('Notification to Queue failed for the Account Mailer %s - %s', pay_account.auth_account_id, - payload) - capture_message('Notification to Queue failed for the Account Mailer {auth_account_id}, {msg}.'.format( - auth_account_id=pay_account.auth_account_id, msg=payload), level='error') + current_app.logger.error(e) + current_app.logger.warning('Notification to Queue failed for the Account Mailer %s - %s', + pay_account.auth_account_id, payload) + capture_message('Notification to Queue failed for the Account Mailer ' + '{auth_account_id}, {msg}.'.format(auth_account_id=pay_account.auth_account_id, msg=payload), + level='error') -async def _publish_account_events(message_type: str, pay_account: PaymentAccountModel, row: Dict[str, str]): +def _publish_account_events(message_type: str, pay_account: PaymentAccountModel, row: Dict[str, str]): """Publish payment message to the mailer queue.""" # Publish message to the Queue, saying account has been created. Using the event spec. - payload = _create_event_payload(message_type, pay_account, row) - + payload = _create_event_payload(pay_account, row) try: - await publish(payload=payload, - client_name=APP_CONFIG.NATS_ACCOUNT_CLIENT_NAME, - subject=APP_CONFIG.NATS_ACCOUNT_SUBJECT) + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.PAY_QUEUE.value, + message_type=message_type, + payload=payload, + topic=current_app.config.get('AUTH_QUEUE_TOPIC') + ) + ) except Exception as e: # NOQA pylint: disable=broad-except - logger.error(e) - logger.warning('Notification to Queue failed for the Account %s - %s', pay_account.auth_account_id, - pay_account.name) + current_app.logger.error(e) + current_app.logger.warning('Notification to Queue failed for the Account %s - %s', pay_account.auth_account_id, + pay_account.name) capture_message('Notification to Queue failed for the Account {auth_account_id}, {msg}.'.format( auth_account_id=pay_account.auth_account_id, msg=payload), level='error') -def _create_event_payload(message_type, pay_account, row): - queue_data = { +def _create_event_payload(pay_account, row): + return { 'accountId': pay_account.auth_account_id, 'paymentMethod': _convert_payment_method(_get_row_value(row, Column.SOURCE_TXN)), 'outstandingAmount': _get_row_value(row, Column.TARGET_TXN_OUTSTANDING), 'originalAmount': _get_row_value(row, Column.TARGET_TXN_ORIGINAL), 'amount': _get_row_value(row, Column.APP_AMOUNT) } - payload = { - 'specversion': '1.x-wip', - 'type': f'bc.registry.payment.{message_type}', - 'source': f'https://api.pay.bcregistry.gov.bc.ca/v1/accounts/{pay_account.auth_account_id}', - 'id': f'{pay_account.auth_account_id}', - 'time': f'{datetime.now()}', - 'datacontenttype': 'application/json', - 'data': queue_data - } - return payload def _convert_payment_method(cfs_method: str) -> str: @@ -763,7 +759,7 @@ def _create_nsf_invoice(cfs_account: CfsAccountModel, inv_number: str, future_effective_fees=0, line_item_status_code=LineItemStatus.ACTIVE.value, service_fees=0, - fee_distribution_id=distribution.distribution_code_id if distribution else 1) # TODO + fee_distribution_id=distribution.distribution_code_id if distribution else 1) line_item.save() inv_ref: InvoiceReferenceModel = InvoiceReferenceModel( @@ -782,7 +778,6 @@ def _get_settlement_type(payment_lines) -> str: """Exclude ONAC, ADJS, PAYR, ONAP and return the record type.""" settlement_type: str = None for row in payment_lines: - # TODO Add BC Online Drawdown record type. if _get_row_value(row, Column.RECORD_TYPE) in \ (RecordType.BOLP.value, RecordType.EFTP.value, RecordType.PAD.value, RecordType.PADR.value, RecordType.PAYR.value): diff --git a/queue_services/payment-reconciliations/src/reconciliations/version.py b/pay-queue/src/pay_queue/version.py similarity index 93% rename from queue_services/payment-reconciliations/src/reconciliations/version.py rename to pay-queue/src/pay_queue/version.py index 071e98e87..19ea931f0 100644 --- a/queue_services/payment-reconciliations/src/reconciliations/version.py +++ b/pay-queue/src/pay_queue/version.py @@ -22,4 +22,4 @@ Development release segment: .devN """ -__version__ = '1.1.3' # pylint: disable=invalid-name +__version__ = '2.0.0' # pylint: disable=invalid-name diff --git a/queue_services/events-listener/tests/__init__.py b/pay-queue/tests/__init__.py similarity index 100% rename from queue_services/events-listener/tests/__init__.py rename to pay-queue/tests/__init__.py diff --git a/pay-queue/tests/conftest.py b/pay-queue/tests/conftest.py new file mode 100644 index 000000000..56771964a --- /dev/null +++ b/pay-queue/tests/conftest.py @@ -0,0 +1,115 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Common setup and fixtures for the pytest suite used by this service.""" +import sys + +import pytest +from flask_migrate import Migrate, upgrade +from pay_api import db as _db +from sqlalchemy import event, text +from sqlalchemy_utils import create_database, database_exists, drop_database + +from pay_queue import create_app + + +@pytest.fixture(scope='session', autouse=True) +def app(): + """Return a session-wide application configured in TEST mode.""" + _app = create_app('testing') + return _app + + +@pytest.fixture(scope='session', autouse=True) +def db(app): # pylint: disable=redefined-outer-name, invalid-name + """Return a session-wide initialised database.""" + with app.app_context(): + if database_exists(_db.engine.url): + drop_database(_db.engine.url) + create_database(_db.engine.url) + _db.session().execute(text('SET TIME ZONE "UTC";')) + migrations_path = [folder for folder in sys.path if 'pay-api/pay-api' in folder] + if len(migrations_path) > 0: + migrations_path = migrations_path[0].replace('/pay-api/src', '/pay-api/migrations') + Migrate(app, _db, directory=migrations_path) + upgrade() + return _db + + +@pytest.fixture +def config(app): + """Return the application config.""" + return app.config + + +@pytest.fixture(scope='session') +def client(app): # pylint: disable=redefined-outer-name + """Return a session-wide Flask test client.""" + return app.test_client() + + +@pytest.fixture(scope='function', autouse=True) +def session(db, app): # pylint: disable=redefined-outer-name, invalid-name + """Return a function-scoped session.""" + with app.app_context(): + with db.engine.connect() as conn: + transaction = conn.begin() + sess = db._make_scoped_session(dict(bind=conn)) # pylint: disable=protected-access + # Establish SAVEPOINT (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) + nested = sess.begin_nested() + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback + + @event.listens_for(sess, 'after_transaction_end') + def restart_savepoint(sess2, trans): # pylint: disable=unused-variable + nonlocal nested + if trans.nested: + # Handle where test DOESN'T session.commit() + sess2.expire_all() + nested = sess.begin_nested() + # When using a SAVEPOINT via the Session.begin_nested() or Connection.begin_nested() methods, + # the transaction object returned must be used to commit or rollback the SAVEPOINT. + # Calling the Session.commit() or Connection.commit() methods will always commit the + # outermost transaction; this is a SQLAlchemy 2.0 specific behavior that is + # reversed from the 1.x series + db.session = sess + db.session.commit = nested.commit + db.session.rollback = nested.rollback + + try: + yield db.session + finally: + db.session.remove() + transaction.rollback() + + +@pytest.fixture(scope='session', autouse=True) +def auto(docker_services, app): + """Spin up docker containers.""" + if app.config['USE_DOCKER_MOCK']: + docker_services.start('minio') + docker_services.start('proxy') + docker_services.start('paybc') + + +@pytest.fixture() +def mock_publish(monkeypatch): + """Mock check_auth.""" + monkeypatch.setattr('pay_api.services.gcp_queue_publisher.publish_to_queue', lambda *args, **kwargs: None) + + +@pytest.fixture(autouse=True) +def mock_queue_auth(mocker): + """Mock queue authorization.""" + mocker.patch('pay_queue.external.gcp_auth.verify_jwt', return_value='') diff --git a/queue_services/payment-reconciliations/tests/docker-compose.yml b/pay-queue/tests/docker-compose.yml similarity index 71% rename from queue_services/payment-reconciliations/tests/docker-compose.yml rename to pay-queue/tests/docker-compose.yml index 80a5de1b1..5d5bca67a 100644 --- a/queue_services/payment-reconciliations/tests/docker-compose.yml +++ b/pay-queue/tests/docker-compose.yml @@ -1,18 +1,5 @@ version: '2.1' services: - nats: - image: nats-streaming - restart: always - mem_limit: 512m - expose: - - 4222 - - 8222 - labels: - - entity.services=nats - ports: - - 4222:4222 - - 8222:8222 - tty: true minio: image: 'bitnami/minio:2022.4.26' ports: diff --git a/queue_services/payment-reconciliations/tests/integration/__init__.py b/pay-queue/tests/integration/__init__.py similarity index 97% rename from queue_services/payment-reconciliations/tests/integration/__init__.py rename to pay-queue/tests/integration/__init__.py index 16d8c866a..44812fe3c 100644 --- a/queue_services/payment-reconciliations/tests/integration/__init__.py +++ b/pay-queue/tests/integration/__init__.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Test suite for the integrations to NATS Queue.""" +"""Test suite for payment reconciliation integration.""" from datetime import datetime @@ -53,7 +53,6 @@ def factory_payment( payment_system_code=payment_system_code, payment_method_code=payment_method_code, payment_status_code=payment_status_code, - created_on=created_on, invoice_number=invoice_number ).save() diff --git a/queue_services/payment-reconciliations/tests/integration/factory.py b/pay-queue/tests/integration/factory.py similarity index 100% rename from queue_services/payment-reconciliations/tests/integration/factory.py rename to pay-queue/tests/integration/factory.py diff --git a/queue_services/payment-reconciliations/tests/integration/test_cgi_reconciliations.py b/pay-queue/tests/integration/test_cgi_reconciliations.py similarity index 89% rename from queue_services/payment-reconciliations/tests/integration/test_cgi_reconciliations.py rename to pay-queue/tests/integration/test_cgi_reconciliations.py index b26621fe0..1e7ccda87 100644 --- a/queue_services/payment-reconciliations/tests/integration/test_cgi_reconciliations.py +++ b/pay-queue/tests/integration/test_cgi_reconciliations.py @@ -19,9 +19,6 @@ from datetime import datetime -import pytest -from entity_queue_common.service_utils import subscribe_to_queue -from flask import current_app from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel @@ -37,30 +34,19 @@ from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db from pay_api.utils.enums import ( - CfsAccountStatus, DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, - PaymentStatus, RoutingSlipStatus) + CfsAccountStatus, DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, MessageType, + PaymentMethod, PaymentStatus, RoutingSlipStatus) + +from tests.integration.utils import helper_add_file_event_to_queue from .factory import ( factory_create_ejv_account, factory_create_pad_account, factory_distribution, factory_invoice, factory_invoice_reference, factory_payment_line_item, factory_refund, factory_routing_slip_account) -from .utils import helper_add_ejv_event_to_queue, upload_to_minio +from .utils import upload_to_minio -@pytest.mark.asyncio -async def test_successful_partner_ejv_reconciliations(session, app, stan_server, event_loop, client_id, events_stan, - future, mock_publish): +def test_successful_partner_ejv_reconciliations(client): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoice and related records # 3. Create CFS Invoice records @@ -121,7 +107,7 @@ async def test_successful_partner_ejv_reconciliations(session, app, stan_server, # Now upload the ACK file to minio and publish message. upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -162,9 +148,8 @@ async def test_successful_partner_ejv_reconciliations(session, app, stan_server, # Now upload the ACK file to minio and publish message. with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - # upload_to_minio(file_name=feedback_file_name, value_as_bytes=feedback_content.encode()) - await helper_add_ejv_event_to_queue(events_stan, file_name=feedback_file_name, message_type='FEEDBACKReceived') + helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -173,21 +158,8 @@ async def test_successful_partner_ejv_reconciliations(session, app, stan_server, assert invoice.disbursement_status_code == DisbursementStatus.COMPLETED.value -@pytest.mark.asyncio -async def test_failed_partner_ejv_reconciliations(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_failed_partner_ejv_reconciliations(client, mock_publish): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoice and related records # 3. Create CFS Invoice records @@ -249,7 +221,7 @@ async def test_failed_partner_ejv_reconciliations(session, app, stan_server, eve # Now upload the ACK file to minio and publish message. upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -290,9 +262,8 @@ async def test_failed_partner_ejv_reconciliations(session, app, stan_server, eve # Now upload the ACK file to minio and publish message. with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - # upload_to_minio(file_name=feedback_file_name, value_as_bytes=feedback_content.encode()) - await helper_add_ejv_event_to_queue(events_stan, file_name=feedback_file_name, message_type='FEEDBACKReceived') + helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -303,21 +274,8 @@ async def test_failed_partner_ejv_reconciliations(session, app, stan_server, eve assert disbursement_distribution_code.stop_ejv -@pytest.mark.asyncio -async def test_successful_partner_reversal_ejv_reconciliations(session, app, stan_server, event_loop, client_id, - events_stan, future, mock_publish): +def test_successful_partner_reversal_ejv_reconciliations(client): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoice and related records # 3. Create CFS Invoice records @@ -381,7 +339,7 @@ async def test_successful_partner_reversal_ejv_reconciliations(session, app, sta # Now upload the ACK file to minio and publish message. upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -422,9 +380,8 @@ async def test_successful_partner_reversal_ejv_reconciliations(session, app, sta # Now upload the ACK file to minio and publish message. with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - # upload_to_minio(file_name=feedback_file_name, value_as_bytes=feedback_content.encode()) - await helper_add_ejv_event_to_queue(events_stan, file_name=feedback_file_name, message_type='FEEDBACKReceived') + helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -434,21 +391,8 @@ async def test_successful_partner_reversal_ejv_reconciliations(session, app, sta assert invoice.disbursement_date == datetime(2023, 5, 29) -@pytest.mark.asyncio -async def test_succesful_payment_ejv_reconciliations(session, app, stan_server, event_loop, client_id, events_stan, - future, mock_publish): +def test_succesful_payment_ejv_reconciliations(client): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create EJV payment accounts # 2. Create invoice and related records # 3. Create a feedback file and assert status @@ -563,7 +507,7 @@ async def test_succesful_payment_ejv_reconciliations(session, app, stan_server, # Now upload the ACK file to minio and publish message. upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -579,7 +523,7 @@ async def test_succesful_payment_ejv_reconciliations(session, app, stan_server, with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - await helper_add_ejv_event_to_queue(events_stan, file_name=feedback_file_name, message_type='FEEDBACKReceived') + helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -607,21 +551,8 @@ async def test_succesful_payment_ejv_reconciliations(session, app, stan_server, assert payment[0][0].paid_amount == inv_total_amount -@pytest.mark.asyncio -async def test_succesful_payment_reversal_ejv_reconciliations(session, app, stan_server, event_loop, client_id, - events_stan, future, mock_publish): +def test_succesful_payment_reversal_ejv_reconciliations(client): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create EJV payment accounts # 2. Create invoice and related records # 3. Create a feedback file and assert status @@ -733,7 +664,7 @@ async def test_succesful_payment_reversal_ejv_reconciliations(session, app, stan # Now upload the ACK file to minio and publish message. upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -749,7 +680,7 @@ async def test_succesful_payment_reversal_ejv_reconciliations(session, app, stan with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - await helper_add_ejv_event_to_queue(events_stan, file_name=feedback_file_name, message_type='FEEDBACKReceived') + helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -775,21 +706,8 @@ async def test_succesful_payment_reversal_ejv_reconciliations(session, app, stan assert payment[0][0].paid_amount == inv_total_amount -@pytest.mark.asyncio -async def test_successful_refund_reconciliations( - session, app, stan_server, event_loop, client_id, events_stan, future, mock_publish -): +def test_successful_refund_reconciliations(client, mock_publish): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create a routing slip. # 2. Mark the routing slip for refund. # 3. Create a AP reconciliation file. @@ -836,7 +754,7 @@ async def test_successful_refund_reconciliations( # Now upload the ACK file to minio and publish message. upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -911,7 +829,7 @@ async def test_successful_refund_reconciliations( with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - await helper_add_ejv_event_to_queue(events_stan, file_name=feedback_file_name, message_type='FEEDBACKReceived') + helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -921,21 +839,8 @@ async def test_successful_refund_reconciliations( assert routing_slip.status == RoutingSlipStatus.REFUND_COMPLETED.value -@pytest.mark.asyncio -async def test_failed_refund_reconciliations( - session, app, stan_server, event_loop, client_id, events_stan, future, mock_publish -): +def test_failed_refund_reconciliations(client, mock_publish): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create a routing slip. # 2. Mark the routing slip for refund. # 3. Create a AP reconciliation file. @@ -982,7 +887,7 @@ async def test_failed_refund_reconciliations( # Now upload the ACK file to minio and publish message. upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -1058,7 +963,7 @@ async def test_failed_refund_reconciliations( with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - await helper_add_ejv_event_to_queue(events_stan, file_name=feedback_file_name, message_type='FEEDBACKReceived') + helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -1070,21 +975,8 @@ async def test_failed_refund_reconciliations( assert routing_slip_2.status == RoutingSlipStatus.REFUND_REJECTED.value -@pytest.mark.asyncio -async def test_prevent_duplicate_ack( - session, app, stan_server, event_loop, client_id, events_stan, future, mock_publish -): +def test_prevent_duplicate_ack(client, mock_publish): """Assert processing completes when existing ack.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - file_ref = f'INBOX.{datetime.now()}' # Upload an acknowledgement file ack_file_name = f'ACK.{file_ref}' @@ -1097,30 +989,19 @@ async def test_prevent_duplicate_ack( jv_file.write('') jv_file.close() - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value # Nothing should change, because it's already processed this ACK. ejv.disbursement_status_code = DisbursementStatus.UPLOADED.value - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.UPLOADED.value -@pytest.mark.asyncio -async def test_successful_ap_disbursement( - session, app, stan_server, event_loop, client_id, events_stan, future, mock_publish -): +def test_successful_ap_disbursement(client, mock_publish): """Test Reconciliations worker for ap disbursement.""" - from reconciliations.worker import cb_subscription_handler - - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create invoice. # 2. Create a AP reconciliation file. # 3. Assert the status. @@ -1178,7 +1059,7 @@ async def test_successful_ap_disbursement( upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1251,7 +1132,8 @@ async def test_successful_ap_disbursement( with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - await helper_add_ejv_event_to_queue(events_stan, file_name=feedback_file_name, message_type='FEEDBACKReceived') + helper_add_file_event_to_queue(client, file_name=feedback_file_name, + message_type=MessageType.CGI_FEEDBACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value @@ -1267,19 +1149,8 @@ async def test_successful_ap_disbursement( assert refund.gl_posted is not None -@pytest.mark.asyncio -async def test_failure_ap_disbursement( - session, app, stan_server, event_loop, client_id, events_stan, future, mock_publish -): +def test_failure_ap_disbursement(client, mock_publish): """Test Reconciliations worker for ap disbursement.""" - from reconciliations.worker import cb_subscription_handler - - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create invoice. # 2. Create a AP reconciliation file. # 3. Assert the status. @@ -1335,7 +1206,7 @@ async def test_failure_ap_disbursement( upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) - await helper_add_ejv_event_to_queue(events_stan, file_name=ack_file_name) + helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1411,7 +1282,7 @@ async def test_failure_ap_disbursement( with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - await helper_add_ejv_event_to_queue(events_stan, file_name=feedback_file_name, message_type='FEEDBACKReceived') + helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value diff --git a/queue_services/payment-reconciliations/tests/integration/test_eft_reconciliation.py b/pay-queue/tests/integration/test_eft_reconciliation.py similarity index 70% rename from queue_services/payment-reconciliations/tests/integration/test_eft_reconciliation.py rename to pay-queue/tests/integration/test_eft_reconciliation.py index 5ae62ac3e..3c44cf62d 100644 --- a/queue_services/payment-reconciliations/tests/integration/test_eft_reconciliation.py +++ b/pay-queue/tests/integration/test_eft_reconciliation.py @@ -19,9 +19,6 @@ from datetime import datetime from typing import List -import pytest -from entity_queue_common.service_utils import subscribe_to_queue -from flask import current_app from pay_api import db from pay_api.models import EFTCredit as EFTCreditModel from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel @@ -29,32 +26,17 @@ from pay_api.models import EFTShortnames as EFTShortnameModel from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Invoice as InvoiceModel -from pay_api.models import InvoiceReference as InvoiceReferenceModel -from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import ( - EFTFileLineType, EFTProcessStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus) +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, MessageType, PaymentMethod -from reconciliations.eft.eft_enums import EFTConstants +from pay_queue.services.eft.eft_enums import EFTConstants from tests.integration.factory import factory_create_eft_account, factory_invoice -from tests.integration.utils import create_and_upload_eft_file, helper_add_eft_event_to_queue +from tests.integration.utils import create_and_upload_eft_file, helper_add_file_event_to_queue from tests.utilities.factory_utils import factory_eft_header, factory_eft_record, factory_eft_trailer -@pytest.mark.asyncio -async def test_eft_tdi17_fail_header(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_eft_tdi17_fail_header(client, mock_publish): """Test EFT Reconciliations properly fails for a bad EFT header.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # Generate file with invalid header file_name: str = 'test_eft_tdi17.txt' header = factory_eft_header(record_type=EFTConstants.HEADER_RECORD_TYPE.value, file_creation_date='20230814', @@ -62,7 +44,7 @@ async def test_eft_tdi17_fail_header(session, app, stan_server, event_loop, clie create_and_upload_eft_file(file_name, [header]) - await helper_add_eft_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name, MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -105,19 +87,8 @@ async def test_eft_tdi17_fail_header(session, app, stan_server, event_loop, clie assert not bool(eft_transactions) -@pytest.mark.asyncio -async def test_eft_tdi17_fail_trailer(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_eft_tdi17_fail_trailer(client, mock_publish): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' header = factory_eft_header(record_type=EFTConstants.HEADER_RECORD_TYPE.value, file_creation_date='20230814', @@ -127,7 +98,7 @@ async def test_eft_tdi17_fail_trailer(session, app, stan_server, event_loop, cli create_and_upload_eft_file(file_name, [header, trailer]) - await helper_add_eft_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -170,19 +141,8 @@ async def test_eft_tdi17_fail_trailer(session, app, stan_server, event_loop, cli assert not bool(eft_transactions) -@pytest.mark.asyncio -async def test_eft_tdi17_fail_transactions(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_eft_tdi17_fail_transactions(client, mock_publish): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' header = factory_eft_header(record_type=EFTConstants.HEADER_RECORD_TYPE.value, file_creation_date='20230814', @@ -200,7 +160,7 @@ async def test_eft_tdi17_fail_transactions(session, app, stan_server, event_loop create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - await helper_add_eft_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -238,24 +198,13 @@ async def test_eft_tdi17_fail_transactions(session, app, stan_server, event_loop assert eft_transactions[0].error_messages[0] == 'Invalid transaction deposit amount CAD.' -@pytest.mark.asyncio -async def test_eft_tdi17_basic_process(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_eft_tdi17_basic_process(client, mock_publish): """Test EFT Reconciliations worker is able to create basic EFT processing records.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_basic_tdi17_file(file_name) - await helper_add_eft_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -327,30 +276,14 @@ async def test_eft_tdi17_basic_process(session, app, stan_server, event_loop, cl assert not eft_credit_invoice_links -@pytest.mark.asyncio -async def test_eft_tdi17_process(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_eft_tdi17_process(client, mock_publish): """Test EFT Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - payment_account, eft_shortname, invoice = create_test_data() - - assert payment_account is not None - assert eft_shortname is not None - assert invoice is not None - - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_tdi17_file(file_name) - await helper_add_eft_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -400,88 +333,79 @@ async def test_eft_tdi17_process(session, app, stan_server, event_loop, client_i assert eft_shortnames[1].auth_account_id is None assert eft_shortnames[1].short_name == 'ABC123' - today = datetime.now().date() - - # Assert Invoice is paid - invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) - expected_amount = 100 - assert invoice is not None - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert invoice.invoice_status_code == InvoiceStatus.PAID.value - assert invoice.payment_date is not None - assert invoice.payment_date.date() == today - assert invoice.paid == expected_amount - assert invoice.total == expected_amount - - receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(invoice.id, invoice.id) - assert receipt is not None - assert receipt.receipt_number == str(invoice.id) - assert receipt.receipt_amount == expected_amount - - expected_invoice_number = f'{current_app.config["EFT_INVOICE_PREFIX"]}{invoice.id}' - payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) - assert payment is not None - assert payment.payment_date.date() == today - assert payment.invoice_number == expected_invoice_number - assert payment.payment_account_id == payment_account.id - assert payment.payment_status_code == PaymentStatus.COMPLETED.value - assert payment.payment_method_code == PaymentMethod.EFT.value - assert payment.invoice_amount == expected_amount - assert payment.paid_amount == expected_amount - - invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel\ - .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) - - assert invoice_reference is not None - assert invoice_reference.invoice_id == invoice.id - assert invoice_reference.invoice_number == payment.invoice_number - assert invoice_reference.invoice_number == expected_invoice_number - assert invoice_reference.status_code == InvoiceReferenceStatus.ACTIVE.value - - eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel).order_by(EFTCreditModel.created_on.asc()).all() - assert eft_credits is not None - assert len(eft_credits) == 3 - assert eft_credits[0].payment_account_id == payment_account.id - assert eft_credits[0].short_name_id == eft_shortnames[0].id - assert eft_credits[0].eft_file_id == eft_file_model.id - assert eft_credits[0].amount == 100.00 - assert eft_credits[0].remaining_amount == 0 - assert eft_credits[0].eft_transaction_id == eft_transactions[0].id - assert eft_credits[1].payment_account_id == payment_account.id - assert eft_credits[1].short_name_id == eft_shortnames[0].id - assert eft_credits[1].eft_file_id == eft_file_model.id - assert eft_credits[1].amount == 50.5 - assert eft_credits[1].remaining_amount == 50.5 - assert eft_credits[1].eft_transaction_id == eft_transactions[1].id - assert eft_credits[2].payment_account_id is None - assert eft_credits[2].short_name_id == eft_shortnames[1].id - assert eft_credits[2].eft_file_id == eft_file_model.id - assert eft_credits[2].amount == 351.5 - assert eft_credits[2].remaining_amount == 351.5 - assert eft_credits[2].eft_transaction_id == eft_transactions[2].id - - eft_credit_invoice_links: List[EFTCreditInvoiceLinkModel] = db.session.query(EFTCreditInvoiceLinkModel).all() - assert eft_credit_invoice_links is not None - assert len(eft_credit_invoice_links) == 1 - assert eft_credit_invoice_links[0].eft_credit_id == eft_credits[0].id - assert eft_credit_invoice_links[0].invoice_id == invoice.id - - -@pytest.mark.asyncio -async def test_eft_tdi17_rerun(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): + # NOTE THIS NEEDS TO BE RE-WRITTEN INSIDE OF THE JOB. + # today = datetime.now().date() + + # # Assert Invoice is paid + # invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + # expected_amount = 100 + # assert invoice is not None + # assert invoice.payment_method_code == PaymentMethod.EFT.value + # assert invoice.invoice_status_code == InvoiceStatus.PAID.value + # assert invoice.payment_date is not None + # assert invoice.payment_date.date() == today + # assert invoice.paid == expected_amount + # assert invoice.total == expected_amount + + # receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(invoice.id, invoice.id) + # assert receipt is not None + # assert receipt.receipt_number == str(invoice.id) + # assert receipt.receipt_amount == expected_amount + + # expected_invoice_number = f'{current_app.config["EFT_INVOICE_PREFIX"]}{invoice.id}' + # payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) + # assert payment is not None + # assert payment.payment_date.date() == today + # assert payment.invoice_number == expected_invoice_number + # assert payment.payment_account_id == payment_account.id + # assert payment.payment_status_code == PaymentStatus.COMPLETED.value + # assert payment.payment_method_code == PaymentMethod.EFT.value + # assert payment.invoice_amount == expected_amount + # assert payment.paid_amount == expected_amount + + # invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel\ + # .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + # assert invoice_reference is not None + # assert invoice_reference.invoice_id == invoice.id + # assert invoice_reference.invoice_number == payment.invoice_number + # assert invoice_reference.invoice_number == expected_invoice_number + # assert invoice_reference.status_code == InvoiceReferenceStatus.ACTIVE.value + + # eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel) \ + # .order_by(EFTCreditModel.created_on.asc()).all() + # assert eft_credits is not None + # assert len(eft_credits) == 3 + # assert eft_credits[0].payment_account_id == payment_account.id + # assert eft_credits[0].short_name_id == eft_shortnames[0].id + # assert eft_credits[0].eft_file_id == eft_file_model.id + # assert eft_credits[0].amount == 100.00 + # assert eft_credits[0].remaining_amount == 0 + # assert eft_credits[0].eft_transaction_id == eft_transactions[0].id + # assert eft_credits[1].payment_account_id == payment_account.id + # assert eft_credits[1].short_name_id == eft_shortnames[0].id + # assert eft_credits[1].eft_file_id == eft_file_model.id + # assert eft_credits[1].amount == 50.5 + # assert eft_credits[1].remaining_amount == 50.5 + # assert eft_credits[1].eft_transaction_id == eft_transactions[1].id + # assert eft_credits[2].payment_account_id is None + # assert eft_credits[2].short_name_id == eft_shortnames[1].id + # assert eft_credits[2].eft_file_id == eft_file_model.id + # assert eft_credits[2].amount == 351.5 + # assert eft_credits[2].remaining_amount == 351.5 + # assert eft_credits[2].eft_transaction_id == eft_transactions[2].id + + # eft_credit_invoice_links: List[EFTCreditInvoiceLinkModel] = db.session.query(EFTCreditInvoiceLinkModel).all() + # assert eft_credit_invoice_links is not None + # assert len(eft_credit_invoice_links) == 1 + # assert eft_credit_invoice_links[0].eft_credit_id == eft_credits[0].id + # assert eft_credit_invoice_links[0].invoice_id == invoice.id + + +def test_eft_tdi17_rerun(client, mock_publish): """Test EFT Reconciliations can be re-executed with a corrected file.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - payment_account, eft_shortname, invoice = create_test_data() - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' header = factory_eft_header(record_type=EFTConstants.HEADER_RECORD_TYPE.value, file_creation_date='20230814', @@ -499,7 +423,7 @@ async def test_eft_tdi17_rerun(session, app, stan_server, event_loop, client_id, create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - await helper_add_eft_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -538,7 +462,7 @@ async def test_eft_tdi17_rerun(session, app, stan_server, event_loop, client_id, jv_number='002425669', transaction_date='') create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - await helper_add_eft_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Check file is completed after correction eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -569,51 +493,53 @@ async def test_eft_tdi17_rerun(session, app, stan_server, event_loop, client_id, assert eft_transactions[0].status_code == EFTProcessStatus.COMPLETED.value assert eft_transactions[0].deposit_amount_cents == 13500 - today = datetime.now().date() - # Assert Invoice is paid - invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) - expected_amount = 100 - assert invoice is not None - assert invoice.payment_method_code == PaymentMethod.EFT.value - assert invoice.invoice_status_code == InvoiceStatus.PAID.value - assert invoice.payment_date is not None - assert invoice.payment_date.date() == today - assert invoice.paid == expected_amount - assert invoice.total == expected_amount - - receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(invoice.id, invoice.id) - assert receipt is not None - assert receipt.receipt_number == str(invoice.id) - assert receipt.receipt_amount == expected_amount - - expected_invoice_number = f'{current_app.config["EFT_INVOICE_PREFIX"]}{invoice.id}' - payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) - assert payment is not None - assert payment.payment_date.date() == today - assert payment.invoice_number == expected_invoice_number - assert payment.payment_account_id == payment_account.id - assert payment.payment_status_code == PaymentStatus.COMPLETED.value - assert payment.payment_method_code == PaymentMethod.EFT.value - assert payment.invoice_amount == expected_amount - - invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel \ - .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) - - assert invoice_reference is not None - assert invoice_reference.invoice_id == invoice.id - assert invoice_reference.invoice_number == payment.invoice_number - assert invoice_reference.invoice_number == expected_invoice_number - assert invoice_reference.status_code == InvoiceReferenceStatus.ACTIVE.value - - eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel).order_by(EFTCreditModel.created_on.asc()).all() - assert eft_credits is not None - assert len(eft_credits) == 1 - assert eft_credits[0].payment_account_id == payment_account.id - assert eft_credits[0].short_name_id == eft_shortname.id - assert eft_credits[0].eft_file_id == eft_file_model.id - assert eft_credits[0].amount == 135 - assert eft_credits[0].remaining_amount == 35 - assert eft_credits[0].eft_transaction_id == eft_transactions[0].id + # NOTE THIS NEEDS TO BE REWRITTEN IN A JOB + # today = datetime.now().date() + # # Assert Invoice is paid + # invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + # expected_amount = 100 + # assert invoice is not None + # assert invoice.payment_method_code == PaymentMethod.EFT.value + # assert invoice.invoice_status_code == InvoiceStatus.PAID.value + # assert invoice.payment_date is not None + # assert invoice.payment_date.date() == today + # assert invoice.paid == expected_amount + # assert invoice.total == expected_amount + + # receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(invoice.id, invoice.id) + # assert receipt is not None + # assert receipt.receipt_number == str(invoice.id) + # assert receipt.receipt_amount == expected_amount + + # expected_invoice_number = f'{current_app.config["EFT_INVOICE_PREFIX"]}{invoice.id}' + # payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id) + # assert payment is not None + # assert payment.payment_date.date() == today + # assert payment.invoice_number == expected_invoice_number + # assert payment.payment_account_id == payment_account.id + # assert payment.payment_status_code == PaymentStatus.COMPLETED.value + # assert payment.payment_method_code == PaymentMethod.EFT.value + # assert payment.invoice_amount == expected_amount + + # invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel \ + # .find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + # assert invoice_reference is not None + # assert invoice_reference.invoice_id == invoice.id + # assert invoice_reference.invoice_number == payment.invoice_number + # assert invoice_reference.invoice_number == expected_invoice_number + # assert invoice_reference.status_code == InvoiceReferenceStatus.ACTIVE.value + + # eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel) \ + # .order_by(EFTCreditModel.created_on.asc()).all() + # assert eft_credits is not None + # assert len(eft_credits) == 1 + # assert eft_credits[0].payment_account_id == payment_account.id + # assert eft_credits[0].short_name_id == eft_shortname.id + # assert eft_credits[0].eft_file_id == eft_file_model.id + # assert eft_credits[0].amount == 135 + # assert eft_credits[0].remaining_amount == 35 + # assert eft_credits[0].eft_transaction_id == eft_transactions[0].id def create_test_data(): diff --git a/queue_services/payment-reconciliations/tests/integration/test_payment_reconciliations.py b/pay-queue/tests/integration/test_payment_reconciliations.py similarity index 77% rename from queue_services/payment-reconciliations/tests/integration/test_payment_reconciliations.py rename to pay-queue/tests/integration/test_payment_reconciliations.py index 10dff791d..fe3eb034a 100644 --- a/queue_services/payment-reconciliations/tests/integration/test_payment_reconciliations.py +++ b/pay-queue/tests/integration/test_payment_reconciliations.py @@ -20,39 +20,25 @@ from datetime import datetime import pytest -from entity_queue_common.service_utils import subscribe_to_queue -from flask import current_app from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import Credit as CreditModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus +from pay_api.utils.enums import ( + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus) -from reconciliations.enums import RecordType, SourceTransaction, Status, TargetTransaction +from pay_queue.enums import RecordType, SourceTransaction, Status, TargetTransaction from .factory import ( factory_create_online_banking_account, factory_create_pad_account, factory_invoice, factory_invoice_reference, factory_payment, factory_payment_line_item, factory_receipt) -from .utils import create_and_upload_settlement_file, helper_add_event_to_queue +from .utils import create_and_upload_settlement_file, helper_add_file_event_to_queue -@pytest.mark.asyncio -async def test_online_banking_reconciliations(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_online_banking_reconciliations(client): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoice and related records # 3. Create CFS Invoice records @@ -81,7 +67,7 @@ async def test_online_banking_reconciliations(session, app, stan_server, event_l TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -95,22 +81,8 @@ async def test_online_banking_reconciliations(session, app, stan_server, event_l assert payment.invoice_number == invoice_number -@pytest.mark.asyncio -async def test_online_banking_reconciliations_over_payment(session, app, stan_server, event_loop, client_id, - events_stan, future, - mock_publish): +def test_online_banking_reconciliations_over_payment(client): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoice and related records # 3. Create CFS Invoice records @@ -141,7 +113,7 @@ async def test_online_banking_reconciliations_over_payment(session, app, stan_se over_payment_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, over_payment_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -155,22 +127,8 @@ async def test_online_banking_reconciliations_over_payment(session, app, stan_se assert payment.invoice_number is None # No invoice_number if payment is not for 1 invoice -@pytest.mark.asyncio -async def test_online_banking_reconciliations_with_credit(session, app, stan_server, event_loop, client_id, events_stan, - future, - mock_publish): +def test_online_banking_reconciliations_with_credit(client): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoice and related records # 3. Create CFS Invoice records @@ -201,7 +159,7 @@ async def test_online_banking_reconciliations_with_credit(session, app, stan_ser credit_row = [RecordType.ONAC.value, SourceTransaction.EFT_WIRE.value, '555566677', 100001, date, credit_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -215,22 +173,8 @@ async def test_online_banking_reconciliations_with_credit(session, app, stan_ser assert payment.invoice_number == invoice_number -@pytest.mark.asyncio -async def test_online_banking_reconciliations_overflows_credit(session, app, stan_server, event_loop, client_id, - events_stan, future, - mock_publish): +def test_online_banking_reconciliations_overflows_credit(client, mock_publish): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoice and related records # 3. Create CFS Invoice records @@ -266,7 +210,7 @@ async def test_online_banking_reconciliations_overflows_credit(session, app, sta Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row, onac_row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -280,21 +224,8 @@ async def test_online_banking_reconciliations_overflows_credit(session, app, sta assert payment.invoice_number is None -@pytest.mark.asyncio -async def test_online_banking_under_payment(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_online_banking_under_payment(client): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoice and related records # 3. Create CFS Invoice records @@ -325,7 +256,7 @@ async def test_online_banking_under_payment(session, app, stan_server, event_loo TargetTransaction.INV.value, invoice_number, total, total - paid_amount, Status.PARTIAL.value] create_and_upload_settlement_file(file_name, [row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id) @@ -340,20 +271,8 @@ async def test_online_banking_under_payment(session, app, stan_server, event_loo assert payment.invoice_number == invoice_number -@pytest.mark.asyncio -async def test_pad_reconciliations(session, app, stan_server, event_loop, client_id, events_stan, future, mock_publish): +def test_pad_reconciliations(client, mock_publish): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoices and related records # 3. Create CFS Invoice records @@ -394,7 +313,7 @@ async def test_pad_reconciliations(session, app, stan_server, event_loop, client 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -416,21 +335,8 @@ async def test_pad_reconciliations(session, app, stan_server, event_loop, client assert rcpt1.receipt_date == rcpt2.receipt_date -@pytest.mark.asyncio -async def test_pad_reconciliations_with_credit_memo(session, app, stan_server, event_loop, - client_id, events_stan, future, mock_publish): +def test_pad_reconciliations_with_credit_memo(client): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoices and related records # 3. Create CFS Invoice records @@ -475,7 +381,7 @@ async def test_pad_reconciliations_with_credit_memo(session, app, stan_server, e pad_row = [RecordType.PAD.value, SourceTransaction.PAD.value, receipt_number, 100001, date, total - credit_amount, cfs_account_number, 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [credit_row, pad_row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -497,20 +403,8 @@ async def test_pad_reconciliations_with_credit_memo(session, app, stan_server, e assert rcpt1.receipt_date == rcpt2.receipt_date -@pytest.mark.asyncio -async def test_pad_nsf_reconciliations(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_pad_nsf_reconciliations(client, mock_publish): """Test Reconciliations worker for NSF.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoices and related records # 3. Create CFS Invoice records @@ -552,7 +446,7 @@ async def test_pad_nsf_reconciliations(session, app, stan_server, event_loop, cl 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -571,20 +465,8 @@ async def test_pad_nsf_reconciliations(session, app, stan_server, event_loop, cl assert cfs_account.status == CfsAccountStatus.FREEZE.value -@pytest.mark.asyncio -async def test_pad_reversal_reconciliations(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +def test_pad_reversal_reconciliations(client, mock_publish): """Test Reconciliations worker for NSF.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoices and related records for a completed payment # 3. Create CFS Invoice records @@ -635,7 +517,7 @@ async def test_pad_reversal_reconciliations(session, app, stan_server, event_loo 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -659,20 +541,8 @@ async def test_pad_reversal_reconciliations(session, app, stan_server, event_loo @pytest.mark.asyncio -async def test_eft_wire_reconciliations(session, app, stan_server, event_loop, client_id, events_stan, future, - mock_publish): +async def test_eft_wire_reconciliations(client, mock_publish): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account # 2. Create invoice and related records # 3. Create CFS Invoice records @@ -712,7 +582,7 @@ async def test_eft_wire_reconciliations(session, app, stan_server, event_loop, c row = [RecordType.EFTP.value, SourceTransaction.EFT_WIRE.value, eft_wire_receipt, 100001, date, total, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -725,20 +595,8 @@ async def test_eft_wire_reconciliations(session, app, stan_server, event_loop, c @pytest.mark.asyncio -async def test_credits(session, app, stan_server, event_loop, client_id, events_stan, future, mock_publish, - monkeypatch): +async def test_credits(client, monkeypatch): """Test Reconciliations worker.""" - # Call back for the subscription - from reconciliations.worker import cb_subscription_handler - - # Create a Credit Card Payment - # register the handler to test it - await subscribe_to_queue(events_stan, - current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'), - current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'), - cb_subscription_handler) - # 1. Create payment account. # 2. Create EFT/WIRE payment db record. # 3. Create a credit memo db record. @@ -799,7 +657,7 @@ def mock_cms(cfs_account: CfsAccountModel, cfs_account_number, TargetTransaction.RECEIPT.value, eft_wire_receipt, onac_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [row]) - await helper_add_event_to_queue(events_stan, file_name=file_name) + helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # Look up credit file and make sure the credits are recorded. pay_account = PaymentAccountModel.find_by_id(pay_account_id) diff --git a/pay-queue/tests/integration/test_worker_queue.py b/pay-queue/tests/integration/test_worker_queue.py new file mode 100644 index 000000000..932aa48bd --- /dev/null +++ b/pay-queue/tests/integration/test_worker_queue.py @@ -0,0 +1,48 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Test Suite to ensure the worker routines are working as expected.""" + +from pay_api.models import Invoice +from pay_api.utils.enums import PaymentMethod, PaymentSystem + +from tests.integration import factory_invoice, factory_invoice_reference, factory_payment, factory_payment_account + +from .utils import helper_add_identifier_event_to_queue + + +def test_update_payment(client): + """Assert that the update internal payment records works.""" + # vars + old_identifier = 'T000000000' + new_identifier = 'BC12345678' + + # Create an Internal Payment + payment_account = factory_payment_account(payment_system_code=PaymentSystem.BCOL.value).save() + + invoice: Invoice = factory_invoice(payment_account=payment_account, + business_identifier=old_identifier, + payment_method_code=PaymentMethod.INTERNAL.value).save() + + inv_ref = factory_invoice_reference(invoice_id=invoice.id) + factory_payment(invoice_number=inv_ref.invoice_number) + + invoice_id = invoice.id + + helper_add_identifier_event_to_queue(client, old_identifier=old_identifier, + new_identifier=new_identifier) + + # Get the internal account and invoice and assert that the identifier is new identifier + invoice = Invoice.find_by_id(invoice_id) + + assert invoice.business_identifier == new_identifier diff --git a/queue_services/payment-reconciliations/tests/integration/utils.py b/pay-queue/tests/integration/utils.py similarity index 51% rename from queue_services/payment-reconciliations/tests/integration/utils.py rename to pay-queue/tests/integration/utils.py index 098228b25..65c54f8b8 100644 --- a/queue_services/payment-reconciliations/tests/integration/utils.py +++ b/pay-queue/tests/integration/utils.py @@ -12,77 +12,44 @@ # See the License for the specific language governing permissions and # limitations under the License. """Utilities used by the integration tests.""" +import base64 import csv import io import json import os +import uuid +from datetime import datetime, timezone from typing import List -import stan from flask import current_app from minio import Minio - -from reconciliations.enums import MessageType - - -async def helper_add_event_to_queue(stan_client: stan.aio.client.Client, - file_name: str): - """Add event to the Queue.""" - payload = { - 'specversion': '1.x-wip', - 'type': 'bc.registry.payment.casSettlementUploaded', - 'source': 'https://api.business.bcregistry.gov.bc.ca/v1/accounts/1/', - 'id': 'C234-1234-1234', - 'time': '2020-08-28T17:37:34.651294+00:00', - 'datacontenttype': 'application/json', - 'data': { - 'fileName': file_name, - 'location': current_app.config['MINIO_BUCKET_NAME'] - } +from pay_api.utils.enums import MessageType +from simple_cloudevent import SimpleCloudEvent, to_queue_message + + +def build_request_for_queue_push(message_type, payload): + """Build request for queue message.""" + queue_message_bytes = to_queue_message(SimpleCloudEvent( + id=str(uuid.uuid4()), + source='pay-queue', + subject=None, + time=datetime.now(tz=timezone.utc).isoformat(), + type=message_type, + data=payload + )) + + return { + 'message': { + 'data': base64.b64encode(queue_message_bytes).decode('utf-8') + }, + 'subscription': 'foobar' } - await stan_client.publish(subject=current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - payload=json.dumps(payload).encode('utf-8')) - - -async def helper_add_eft_event_to_queue(stan_client: stan.aio.client.Client, file_name: str, - message_type: str = MessageType.EFT_FILE_UPLOADED.value): - """Add eft event to the Queue.""" - payload = { - 'specversion': '1.x-wip', - 'type': message_type, - 'source': 'https://api.business.bcregistry.gov.bc.ca/v1/accounts/1/', - 'id': 'C234-1234-1234', - 'time': '2020-08-28T17:37:34.651294+00:00', - 'datacontenttype': 'text/plain', - 'data': { - 'fileName': file_name, - 'location': current_app.config['MINIO_BUCKET_NAME'] - } - } - - await stan_client.publish(subject=current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - payload=json.dumps(payload).encode('utf-8')) - - -async def helper_add_ejv_event_to_queue(stan_client: stan.aio.client.Client, file_name: str, - message_type: str = 'ACKReceived'): - """Add event to the Queue.""" - payload = { - 'specversion': '1.x-wip', - 'type': f'bc.registry.payment.cgi.{message_type}', - 'source': 'https://api.business.bcregistry.gov.bc.ca/v1/accounts/1/', - 'id': 'C234-1234-1234', - 'time': '2020-08-28T17:37:34.651294+00:00', - 'datacontenttype': 'application/json', - 'data': { - 'fileName': file_name, - 'location': current_app.config['MINIO_BUCKET_NAME'] - } - } - await stan_client.publish(subject=current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'), - payload=json.dumps(payload).encode('utf-8')) +def post_to_queue(client, request_payload): + """Post request to queue.""" + response = client.post('/', data=json.dumps(request_payload), headers={'Content-Type': 'application/json'}) + assert response.status_code == 200 def create_and_upload_settlement_file(file_name: str, rows: List[List]): @@ -93,7 +60,7 @@ def create_and_upload_settlement_file(file_name: str, rows: List[List]): 'Target transaction Number', 'Target Transaction Original amount', 'Target Transaction Outstanding Amount', 'Target transaction status', 'Reversal Reason code', 'Reversal reason description'] - with open(file_name, mode='w') as cas_file: + with open(file_name, mode='w', encoding='utf-8') as cas_file: cas_writer = csv.writer(cas_file, quoting=csv.QUOTE_ALL) cas_writer.writerow(headers) for row in rows: @@ -105,7 +72,7 @@ def create_and_upload_settlement_file(file_name: str, rows: List[List]): def create_and_upload_eft_file(file_name: str, rows: List[List]): """Create eft file, upload to minio and send event.""" - with open(file_name, mode='w') as eft_file: + with open(file_name, mode='w', encoding='utf-8') as eft_file: for row in rows: print(row, file=eft_file) @@ -124,3 +91,29 @@ def upload_to_minio(value_as_bytes, file_name: str): value_as_stream = io.BytesIO(value_as_bytes) minio_client.put_object(current_app.config['MINIO_BUCKET_NAME'], file_name, value_as_stream, os.stat(file_name).st_size) + + +def helper_add_file_event_to_queue(client, file_name: str, message_type: str): + """Add event to the Queue.""" + queue_payload = { + 'fileName': file_name, + 'location': current_app.config['MINIO_BUCKET_NAME'] + } + request_payload = build_request_for_queue_push(message_type, queue_payload) + post_to_queue(client, request_payload) + + +def helper_add_identifier_event_to_queue(client, old_identifier: str = 'T1234567890', + new_identifier: str = 'BC1234567890'): + """Add event to the Queue.""" + message_type = MessageType.INCORPORATION.value + queue_payload = { + 'filing': { + 'header': {'filingId': '12345678'}, + 'business': {'identifier': 'BC1234567'} + }, + 'identifier': new_identifier, + 'tempidentifier': old_identifier, + } + request_payload = build_request_for_queue_push(message_type, queue_payload) + post_to_queue(client, request_payload) diff --git a/queue_services/payment-reconciliations/tests/nginx.conf b/pay-queue/tests/nginx.conf similarity index 100% rename from queue_services/payment-reconciliations/tests/nginx.conf rename to pay-queue/tests/nginx.conf diff --git a/queue_services/payment-reconciliations/tests/unit/__init__.py b/pay-queue/tests/unit/__init__.py similarity index 100% rename from queue_services/payment-reconciliations/tests/unit/__init__.py rename to pay-queue/tests/unit/__init__.py diff --git a/queue_services/payment-reconciliations/tests/unit/test_data/tdi17_sample.txt b/pay-queue/tests/unit/test_data/tdi17_sample.txt similarity index 100% rename from queue_services/payment-reconciliations/tests/unit/test_data/tdi17_sample.txt rename to pay-queue/tests/unit/test_data/tdi17_sample.txt diff --git a/queue_services/payment-reconciliations/tests/unit/test_eft_file_parser.py b/pay-queue/tests/unit/test_eft_file_parser.py similarity index 99% rename from queue_services/payment-reconciliations/tests/unit/test_eft_file_parser.py rename to pay-queue/tests/unit/test_eft_file_parser.py index f6feb96f5..fd667674f 100644 --- a/queue_services/payment-reconciliations/tests/unit/test_eft_file_parser.py +++ b/pay-queue/tests/unit/test_eft_file_parser.py @@ -18,9 +18,9 @@ """ from datetime import datetime -from reconciliations.eft import EFTHeader, EFTRecord, EFTTrailer -from reconciliations.eft.eft_enums import EFTConstants -from reconciliations.eft.eft_errors import EFTError +from pay_queue.services.eft import EFTHeader, EFTRecord, EFTTrailer +from pay_queue.services.eft.eft_enums import EFTConstants +from pay_queue.services.eft.eft_errors import EFTError from tests.utilities.factory_utils import factory_eft_header, factory_eft_record, factory_eft_trailer diff --git a/queue_services/payment-reconciliations/tests/utilities/__init__.py b/pay-queue/tests/utilities/__init__.py similarity index 100% rename from queue_services/payment-reconciliations/tests/utilities/__init__.py rename to pay-queue/tests/utilities/__init__.py diff --git a/queue_services/payment-reconciliations/tests/utilities/factory_utils.py b/pay-queue/tests/utilities/factory_utils.py similarity index 98% rename from queue_services/payment-reconciliations/tests/utilities/factory_utils.py rename to pay-queue/tests/utilities/factory_utils.py index 714085d05..d8047880a 100644 --- a/queue_services/payment-reconciliations/tests/utilities/factory_utils.py +++ b/pay-queue/tests/utilities/factory_utils.py @@ -15,7 +15,7 @@ Test Factory Utility for creating test data. """ -from reconciliations.eft.eft_enums import EFTConstants +from pay_queue.services.eft.eft_enums import EFTConstants def factory_eft_header(record_type: str, file_creation_date: str, file_creation_time: str, diff --git a/queue_services/events-listener/Dockerfile b/queue_services/events-listener/Dockerfile deleted file mode 100644 index 3747d9aa5..000000000 --- a/queue_services/events-listener/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -FROM python:3.12.2-bullseye - -ARG VCS_REF="missing" -ARG BUILD_DATE="missing" - -ENV VCS_REF=${VCS_REF} -ENV BUILD_DATE=${BUILD_DATE} - -LABEL org.label-schema.vcs-ref=${VCS_REF} \ - org.label-schema.build-date=${BUILD_DATE} - -USER root - -# Create working directory -RUN mkdir /opt/app-root && chmod 755 /opt/app-root -WORKDIR /opt/app-root - -# Install the requirements -COPY ./requirements.txt . - -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -RUN pip install . - -USER 1001 - -# Set Python path -ENV PYTHONPATH=/opt/app-root/src - -#EXPOSE 8080 - -CMD [ "python", "/opt/app-root/app.py" ] diff --git a/queue_services/events-listener/Makefile b/queue_services/events-listener/Makefile deleted file mode 100644 index 822b1ae88..000000000 --- a/queue_services/events-listener/Makefile +++ /dev/null @@ -1,144 +0,0 @@ -.PHONY: license -.PHONY: setup -.PHONY: ci cd -.PHONY: run - -MKFILE_PATH:=$(abspath $(lastword $(MAKEFILE_LIST))) -CURRENT_ABS_DIR:=$(patsubst %/,%,$(dir $(MKFILE_PATH))) - -PROJECT_NAME:=events_listener -DOCKER_NAME:=events-listener - -################################################################################# -# COMMANDS -- Setup # -################################################################################# -setup: install install-dev ## Setup the project - -clean: clean-build clean-pyc clean-test ## Clean the project - rm -rf venv/ - -clean-build: ## Clean build files - rm -fr build/ - rm -fr dist/ - rm -fr .eggs/ - find . -name '*.egg-info' -exec rm -fr {} + - find . -name '*.egg' -exec rm -fr {} + - -clean-pyc: ## Clean cache files - find . -name '*.pyc' -exec rm -f {} + - find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + - find . -name '__pycache__' -exec rm -fr {} + - -clean-test: ## clean test files - find . -name '.pytest_cache' -exec rm -fr {} + - rm -fr .tox/ - rm -f .coverage - rm -fr htmlcov/ - -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip freeze | sort > requirements.txt ;\ - cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . - -################################################################################# -# COMMANDS - CI # -################################################################################# -ci: lint flake8 test ## CI flow - -pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME) - -flake8: ## Linting with flake8 - . venv/bin/activate && flake8 src/$(PROJECT_NAME) tests - -lint: pylint flake8 ## run all lint type scripts - -test: ## Unit testing - . venv/bin/activate && pytest - -mac-cov: test ## Run the coverage report and display in a browser window (mac) - @open -a "Google Chrome" htmlcov/index.html - -################################################################################# -# COMMANDS - CD -# expects the terminal to be openshift login -# expects export OPENSHIFT_DOCKER_REGISTRY="" -# expects export OPENSHIFT_SA_NAME="$(oc whoami)" -# expects export OPENSHIFT_SA_TOKEN="$(oc whoami -t)" -# expects export OPENSHIFT_REPOSITORY="" -# expects export TAG_NAME="dev/test/prod" -# expects export OPS_REPOSITORY="" # -################################################################################# -cd: ## CD flow -ifeq ($(TAG_NAME), test) -cd: update-env - oc -n "$(OPENSHIFT_REPOSITORY)-tools" tag $(DOCKER_NAME):dev $(DOCKER_NAME):$(TAG_NAME) -else ifeq ($(TAG_NAME), prod) -cd: update-env - oc -n "$(OPENSHIFT_REPOSITORY)-tools" tag $(DOCKER_NAME):$(TAG_NAME) $(DOCKER_NAME):$(TAG_NAME)-$(shell date +%F) - oc -n "$(OPENSHIFT_REPOSITORY)-tools" tag $(DOCKER_NAME):test $(DOCKER_NAME):$(TAG_NAME) -else -TAG_NAME=dev -cd: build update-env tag -endif - -build: ## Build the docker container - docker build . -t $(DOCKER_NAME) \ - --build-arg VCS_REF=$(shell git rev-parse --short HEAD) \ - --build-arg BUILD_DATE=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ") \ - -build-nc: ## Build the docker container without caching - docker build --no-cache -t $(DOCKER_NAME) . - -REGISTRY_IMAGE=$(OPENSHIFT_DOCKER_REGISTRY)/$(OPENSHIFT_REPOSITORY)-tools/$(DOCKER_NAME) -push: #build ## Push the docker container to the registry & tag latest - @echo "$(OPENSHIFT_SA_TOKEN)" | docker login $(OPENSHIFT_DOCKER_REGISTRY) -u $(OPENSHIFT_SA_NAME) --password-stdin ;\ - docker tag $(DOCKER_NAME) $(REGISTRY_IMAGE):latest ;\ - docker push $(REGISTRY_IMAGE):latest - -VAULTS=`cat devops/vaults.json` -update-env: ## Update env from 1pass - oc -n "$(OPS_REPOSITORY)-$(TAG_NAME)" exec "dc/vault-service-$(TAG_NAME)" -- ./scripts/1pass.sh \ - -m "secret" \ - -e "$(TAG_NAME)" \ - -a "$(DOCKER_NAME)-$(TAG_NAME)" \ - -n "$(OPENSHIFT_REPOSITORY)-$(TAG_NAME)" \ - -v "$(VAULTS)" \ - -r "true" \ - -f "false" - -tag: push ## tag image - oc -n "$(OPENSHIFT_REPOSITORY)-tools" tag $(DOCKER_NAME):latest $(DOCKER_NAME):$(TAG_NAME) - -################################################################################# -# COMMANDS - Local # -################################################################################# - -run: ## Run the project in local - . venv/bin/activate && python app.py - -################################################################################# -# Self Documenting Commands # -################################################################################# -.PHONY: help - -.DEFAULT_GOAL := help - -help: - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/queue_services/events-listener/README.md b/queue_services/events-listener/README.md deleted file mode 100755 index 2379003c9..000000000 --- a/queue_services/events-listener/README.md +++ /dev/null @@ -1,75 +0,0 @@ - -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) - - -# Application Name - -BC Registries Names Examination, research and approval system API - -## Technology Stack Used -* Python, Flask -* Postgres - SQLAlchemy, psycopg2-binary & alembic - -## Third-Party Products/Libraries used and the the License they are covert by - -## Project Status -As of 2018-02-22 in **ALPHA** - -## Documnentation - -GitHub Pages (https://guides.github.com/features/pages/) are a neat way to document you application/project. - -## Security - -Future - BCGov Keycloak - -Current - JWT hack - -## Files in this repository - -``` -docs/ - Project Documentation -└── images -└── icons - -openshift/ - OpenShift-specific files -├── scripts - helper scripts -└── templates - application templates -``` - -## Deployment (Local Development) - -* Developer Workstation Requirements/Setup -* Application Specific Setup - -## Deployment (OpenShift) - -See (openshift/Readme.md) - -## Getting Help or Reporting an Issue - -To report bugs/issues/feature requests, please file an [issue](../../issues). - -## How to Contribute - -If you would like to contribute, please see our [CONTRIBUTING](./CONTRIBUTING.md) guidelines. - -Please note that this project is released with a [Contributor Code of Conduct](./CODE_OF_CONDUCT.md). -By participating in this project you agree to abide by its terms. - -## License - - Copyright 2018 Province of British Columbia - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/queue_services/events-listener/app.py b/queue_services/events-listener/app.py deleted file mode 100755 index 5ea127a92..000000000 --- a/queue_services/events-listener/app.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""s2i based launch script to run the service.""" -import asyncio -import os - -from events_listener.worker import APP_CONFIG, cb_subscription_handler, qsm - - -if __name__ == '__main__': - - # my_config = config.get_named_config(os.getenv('DEPLOYMENT_ENV', 'production')) - - event_loop = asyncio.get_event_loop() - event_loop.run_until_complete(qsm.run(loop=event_loop, - config=APP_CONFIG, - callback=cb_subscription_handler)) - try: - event_loop.run_forever() - finally: - event_loop.close() diff --git a/queue_services/events-listener/coverage.xml b/queue_services/events-listener/coverage.xml deleted file mode 100644 index 45f65708f..000000000 --- a/queue_services/events-listener/coverage.xml +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - /home/pwei/xapp/sbc-pay/queue_services/events-listener/src - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/queue_services/events-listener/devops/vaults.json b/queue_services/events-listener/devops/vaults.json deleted file mode 100644 index b043cf203..000000000 --- a/queue_services/events-listener/devops/vaults.json +++ /dev/null @@ -1,28 +0,0 @@ -[ - { - "vault": "nats", - "application": [ - "base", - "entity-events-listener" - ] - }, - { - "vault": "relationship", - "application": [ - "postgres-pay", - "jwt" - ] - }, - { - "vault": "sentry", - "application": [ - "relationship-api" - ] - }, - { - "vault": "launchdarkly", - "application": [ - "pay" - ] - } -] diff --git a/queue_services/events-listener/logging.conf b/queue_services/events-listener/logging.conf deleted file mode 100644 index ffc1a01e3..000000000 --- a/queue_services/events-listener/logging.conf +++ /dev/null @@ -1,28 +0,0 @@ -[loggers] -keys=root,api - -[handlers] -keys=console - -[formatters] -keys=simple - -[logger_root] -level=DEBUG -handlers=console - -[logger_api] -level=DEBUG -handlers=console -qualname=api -propagate=0 - -[handler_console] -class=StreamHandler -level=DEBUG -formatter=simple -args=(sys.stdout,) - -[formatter_simple] -format=%(asctime)s - %(name)s - %(levelname)s in %(module)s:%(filename)s:%(lineno)d - %(funcName)s: %(message)s -datefmt= \ No newline at end of file diff --git a/queue_services/events-listener/openshift/templates/events-listener-build.json b/queue_services/events-listener/openshift/templates/events-listener-build.json deleted file mode 100755 index ce0bb88e3..000000000 --- a/queue_services/events-listener/openshift/templates/events-listener-build.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a events listener.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "events-listener" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "queue_services/events-listener" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} \ No newline at end of file diff --git a/queue_services/events-listener/openshift/templates/events-listener-deploy.json b/queue_services/events-listener/openshift/templates/events-listener-deploy.json deleted file mode 100755 index b30d289f5..000000000 --- a/queue_services/events-listener/openshift/templates/events-listener-deploy.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for events listener service.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": "${REPLICAS}", - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "env": [ - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/healthz", - "port": 7070, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "readinessProbe": { - "httpGet": { - "path": "/readyz", - "port": 7070, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "events-listener" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} diff --git a/queue_services/events-listener/q_cli.py b/queue_services/events-listener/q_cli.py deleted file mode 100755 index a14856165..000000000 --- a/queue_services/events-listener/q_cli.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Service for listening and handling Queue Messages. - -This service registers interest in listening to a Queue and processing received messages. -""" -import asyncio -import functools -import getopt -import json -import os -import random -import signal -import sys - -from entity_queue_common.service_utils import error_cb, logger, signal_handler -from nats.aio.client import Client as NATS # noqa N814; by convention the name is NATS -from stan.aio.client import Client as STAN # noqa N814; by convention the name is STAN - - -async def run(loop, old_identifier, new_identifier): # pylint: disable=too-many-locals - """Run the main application loop for the service. - - This runs the main top level service functions for working with the Queue. - """ - # NATS client connections - nc = NATS() - sc = STAN() - - async def close(): - """Close the stream and nats connections.""" - await sc.close() - await nc.close() - - # Connection and Queue configuration. - def nats_connection_options(): - return { - 'servers': os.getenv('NATS_SERVERS', 'nats://127.0.0.1:4222').split(','), - 'io_loop': loop, - 'error_cb': error_cb, - 'name': os.getenv('NATS_ENTITY_EVENTS_CLIENT_NAME', 'entity.events.worker') - } - - def stan_connection_options(): - return { - 'cluster_id': os.getenv('NATS_CLUSTER_ID', 'test-cluster'), - 'client_id': str(random.SystemRandom().getrandbits(0x58)), - 'nats': nc - } - - def subscription_options(): - return { - 'subject': os.getenv('NATS_ENTITY_EVENTS_SUBJECT', 'entity.events'), - 'queue': os.getenv('NATS_ENTITY_EVENTS_QUEUE', 'events-worker'), - 'durable_name': os.getenv('NATS_ENTITY_EVENTS_QUEUE', 'events-worker') + '_durable' - } - - try: - # Connect to the NATS server, and then use that for the streaming connection. - await nc.connect(**nats_connection_options()) - await sc.connect(**stan_connection_options()) - - # register the signal handler - for sig in ('SIGINT', 'SIGTERM'): - loop.add_signal_handler(getattr(signal, sig), - functools.partial(signal_handler, sig_loop=loop, sig_nc=nc, task=close) - ) - - payload = { - 'specversion': '1.x-wip', - 'type': 'bc.registry.business.incorporationApplication', - 'source': 'https://api.business.bcregistry.gov.bc.ca/v1/business/BC1234567/filing/12345678', - 'id': 'C234-1234-1234', - 'time': '2020-08-28T17:37:34.651294+00:00', - 'datacontenttype': 'application/json', - 'identifier': new_identifier, - 'tempidentifier': old_identifier, - 'data': { - 'filing': { - 'header': {'filingId': '12345678'}, - 'business': {'identifier': 'BC1234567'} - } - } - } - - print('payload-->', payload) - - await sc.publish(subject=subscription_options().get('subject'), - payload=json.dumps(payload).encode('utf-8')) - - except Exception as e: # pylint: disable=broad-except - # TODO tighten this error and decide when to bail on the infinite reconnect - logger.error(e) - - -if __name__ == '__main__': - try: - opts, args = getopt.getopt(sys.argv[1:], "ho:n:", ["oldid=", "newid="]) - except getopt.GetoptError: - print('q_cli.py -o -n ') - sys.exit(2) - - for opt, arg in opts: - if opt == '-h': - print('q_cli.py -o -n ') - sys.exit() - elif opt in ("-o", "--oldid"): - old_id = arg - elif opt in ("-n", "--newid"): - new_id = arg - - print('publish:', old_id, new_id) - event_loop = asyncio.get_event_loop() - event_loop.run_until_complete(run(event_loop, old_id, new_id)) diff --git a/queue_services/events-listener/requirements.txt b/queue_services/events-listener/requirements.txt deleted file mode 100644 index c9ce347ce..000000000 --- a/queue_services/events-listener/requirements.txt +++ /dev/null @@ -1,36 +0,0 @@ -Flask==1.1.2 -Jinja2==3.0.3 -MarkupSafe==2.1.3 -Werkzeug==1.0.1 -asyncio-nats-client==0.11.5 -asyncio-nats-streaming==0.4.0 -attrs==23.1.0 -blinker==1.6.2 -certifi==2023.7.22 -click==8.1.3 -expiringdict==1.2.2 -importlib-resources==5.12.0 -itsdangerous==2.0.1 -jaeger-client==4.8.0 -jsonschema==4.17.3 -launchdarkly-server-sdk==8.1.4 -opentracing==2.4.0 -pkgutil_resolve_name==1.3.10 -protobuf==3.19.6 -pyRFC3339==1.1 -pycountry==22.3.5 -pyrsistent==0.19.3 -python-dotenv==1.0.0 -pytz==2023.3 -semver==2.13.0 -sentry-sdk==1.25.1 -six==1.16.0 -threadloop==1.0.2 -thrift==0.16.0 -tornado==6.3.3 -urllib3==1.26.17 -zipp==3.15.0 --e git+https://github.com/bcgov/lear.git#egg=entity_queue_common&subdirectory=queue_services/common --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git#egg=pay-api&subdirectory=pay-api -git+https://github.com/daxiom/simple-cloudevent.py.git diff --git a/queue_services/events-listener/requirements/repo-libraries.txt b/queue_services/events-listener/requirements/repo-libraries.txt deleted file mode 100644 index 77f773f6e..000000000 --- a/queue_services/events-listener/requirements/repo-libraries.txt +++ /dev/null @@ -1,4 +0,0 @@ --e git+https://github.com/bcgov/lear.git#egg=entity_queue_common&subdirectory=queue_services/common --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git#egg=pay-api&subdirectory=pay-api -git+https://github.com/daxiom/simple-cloudevent.py.git diff --git a/queue_services/events-listener/setup.cfg b/queue_services/events-listener/setup.cfg deleted file mode 100644 index 113ffe725..000000000 --- a/queue_services/events-listener/setup.cfg +++ /dev/null @@ -1,119 +0,0 @@ -[metadata] -name = events_listener -url = https://github.com/bcgov/sbc-pay/queue_services/events-listener -author = SBC Relationships team -author_email = -classifiers = - Development Status :: Beta - Intended Audience :: Developers / QA - Topic :: Payments - License :: OSI Approved :: Apache Software License - Natural Language :: English - Programming Language :: Python :: 3.8 -license = Apache Software License Version 2.0 -description = A short description of the project -long_description = file: README.md -keywords = - -[options] -zip_safe = True -python_requires = >=3.6 -include_package_data = True -packages = find: - -[options.package_data] -events_listener = - -[wheel] -universal = 1 - -[bdist_wheel] -universal = 1 - -[aliases] -test = pytest - -[flake8] -exclude = .git,*migrations* -max-line-length = 120 -docstring-min-length=10 -per-file-ignores = - */__init__.py:F401 - -[pycodestyle] -max_line_length = 120 -ignore = E501 -docstring-min-length=10 -notes=FIXME,XXX # TODO is ignored -match_dir = src/events_listener -ignored-modules=flask_sqlalchemy - sqlalchemy -per-file-ignores = - */__init__.py:F401 -good-names= - b, - d, - i, - e, - f, - k, - u, - v, - ar, - cb, #common shorthand for callback - nc, - rv, - sc, - event_loop, - logger, - loop, - -[pylint] -ignore=migrations,test -notes=FIXME,XXX,TODO -ignored-modules=flask_sqlalchemy,sqlalchemy,SQLAlchemy,alembic,scoped_session -ignored-classes=scoped_session -disable=C0301,W0511,R0801,R0902 -good-names= - b, - d, - i, - e, - f, - k, - u, - v, - ar, - cb, #common shorthand for callback - nc, - rv, - sc, - event_loop, - logger, - loop, - -[isort] -line_length = 120 -indent = 4 -multi_line_output = 4 -lines_after_imports = 2 - -[tool:pytest] -addopts = --cov=src --cov-report html:htmlcov --cov-report xml:coverage.xml -testpaths = tests -filterwarnings = - ignore::UserWarning - - -[report:run] -exclude_lines = - pragma: no cover - from - import - def __repr__ - if self.debug: - if settings.DEBUG - raise AssertionError - raise NotImplementedError - if 0: - if __name__ == .__main__.: diff --git a/queue_services/events-listener/setup.py b/queue_services/events-listener/setup.py deleted file mode 100644 index 1477e6874..000000000 --- a/queue_services/events-listener/setup.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright © 2019 Province of British Columbia. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Installer and setup for this module -""" -import ast -import re -from glob import glob -from os.path import basename, splitext - -from setuptools import find_packages, setup - - -_version_re = re.compile(r'__version__\s+=\s+(.*)') # pylint: disable=invalid-name - -with open('src/events_listener/version.py', 'rb') as f: - version = str(ast.literal_eval(_version_re.search( # pylint: disable=invalid-name - f.read().decode('utf-8')).group(1))) - - -def read_requirements(filename): - """ - Get application requirements from - the requirements.txt file. - :return: Python requirements - """ - with open(filename, 'r') as req: - requirements = req.readlines() - install_requires = [r.strip() for r in requirements if (r.find('git+') != 0 and r.find('-e git+') != 0)] - return install_requires - - -def read(filepath): - """ - Read the contents from a file. - :param str filepath: path to the file to be read - :return: file contents - """ - with open(filepath, 'r') as file_handle: - content = file_handle.read() - return content - - -REQUIREMENTS = read_requirements('requirements.txt') - -setup( - name="events_listener", - version=version, - author_email='', - packages=find_packages('src'), - package_dir={'': 'src'}, - py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], - include_package_data=True, - license=read('LICENSE'), - long_description=read('README.md'), - zip_safe=False, - install_requires=REQUIREMENTS, - setup_requires=["pytest-runner", ], - tests_require=["pytest", ], -) diff --git a/queue_services/events-listener/src/events_listener/__init__.py b/queue_services/events-listener/src/events_listener/__init__.py deleted file mode 100644 index 7fcd624dc..000000000 --- a/queue_services/events-listener/src/events_listener/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The Events Listener service. - -This module is the service worker for applying filings to the Business Database structure. -""" diff --git a/queue_services/events-listener/src/events_listener/config.py b/queue_services/events-listener/src/events_listener/config.py deleted file mode 100644 index e5e12c062..000000000 --- a/queue_services/events-listener/src/events_listener/config.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""All of the configuration for the service is captured here. - -All items are loaded, or have Constants defined here that -are loaded into the Flask configuration. -All modules and lookups get their configuration from the -Flask config, rather than reading environment variables directly -or by accessing this configuration directly. -""" -import os -import random - -from dotenv import find_dotenv, load_dotenv - - -# this will load all the envars from a .env file located in the project root (api) -load_dotenv(find_dotenv()) - -CONFIGURATION = { - 'development': 'events_listener.config.DevConfig', - 'testing': 'events_listener.config.TestConfig', - 'production': 'events_listener.config.ProdConfig', - 'default': 'events_listener.config.ProdConfig' -} - - -def get_named_config(config_name: str = 'production'): - """Return the configuration object based on the name. - - :raise: KeyError: if an unknown configuration is requested - """ - if config_name in ['production', 'staging', 'default']: - app_config = ProdConfig() - elif config_name == 'testing': - app_config = TestConfig() - elif config_name == 'development': - app_config = DevConfig() - else: - raise KeyError(f'Unknown configuration: {config_name}') - return app_config - - -class _Config(): # pylint: disable=too-few-public-methods - """Base class configuration that should set reasonable defaults. - - Used as the base for all the other configurations. - """ - - LEGISLATIVE_TIMEZONE = os.getenv('LEGISLATIVE_TIMEZONE', 'America/Vancouver') - PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) - PAY_LD_SDK_KEY = os.getenv('PAY_LD_SDK_KEY', None) - - SENTRY_ENABLE = os.getenv('SENTRY_ENABLE', 'False') - SENTRY_DSN = os.getenv('SENTRY_DSN', None) - - SQLALCHEMY_TRACK_MODIFICATIONS = False - - # POSTGRESQL - DB_USER = os.getenv('DATABASE_USERNAME', '') - DB_PASSWORD = os.getenv('DATABASE_PASSWORD', '') - DB_NAME = os.getenv('DATABASE_NAME', '') - DB_HOST = os.getenv('DATABASE_HOST', '') - DB_PORT = os.getenv('DATABASE_PORT', '5432') - SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' - - NATS_CONNECTION_OPTIONS = { - 'servers': os.getenv('NATS_SERVERS', 'nats://127.0.0.1:4222').split(','), - 'name': os.getenv('NATS_ENTITY_EVENTS_CLIENT_NAME', 'entity.events.worker') - - } - STAN_CONNECTION_OPTIONS = { - 'cluster_id': os.getenv('NATS_CLUSTER_ID', 'test-cluster'), - 'client_id': str(random.SystemRandom().getrandbits(0x58)), - 'ping_interval': 1, - 'ping_max_out': 5, - } - - SUBSCRIPTION_OPTIONS = { - 'subject': os.getenv('NATS_ENTITY_EVENTS_SUBJECT', 'entity.events'), - 'queue': os.getenv('NATS_ENTITY_EVENTS_QUEUE', 'events-worker'), - 'durable_name': os.getenv('NATS_ENTITY_EVENTS_QUEUE', 'events-worker') + '_durable', - } - - -class DevConfig(_Config): # pylint: disable=too-few-public-methods - """Creates the Development Config object.""" - - TESTING = False - DEBUG = True - - -class TestConfig(_Config): # pylint: disable=too-few-public-methods - """In support of testing only. - - Used by the py.test suite - """ - - DEBUG = True - TESTING = True - # POSTGRESQL - DB_USER = os.getenv('DATABASE_TEST_USERNAME', '') - DB_PASSWORD = os.getenv('DATABASE_TEST_PASSWORD', '') - DB_NAME = os.getenv('DATABASE_TEST_NAME', '') - DB_HOST = os.getenv('DATABASE_TEST_HOST', '') - DB_PORT = os.getenv('DATABASE_TEST_PORT', '5432') - SQLALCHEMY_DATABASE_URI = os.getenv( - 'DATABASE_TEST_URL', - default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' - ) - - STAN_CLUSTER_NAME = 'test-cluster' - TEST_NATS_DOCKER = os.getenv('TEST_NATS_DOCKER', None) - - -class ProdConfig(_Config): # pylint: disable=too-few-public-methods - """Production environment configuration.""" - - TESTING = False - DEBUG = False diff --git a/queue_services/events-listener/src/events_listener/utils.py b/queue_services/events-listener/src/events_listener/utils.py deleted file mode 100644 index a5c34c3fb..000000000 --- a/queue_services/events-listener/src/events_listener/utils.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Supply version and commit hash info. - -When deployed in OKD, it adds the last commit hash onto the version info. -""" -import os - -from events_listener.version import __version__ - - -def _get_build_openshift_commit_hash(): - return os.getenv('OPENSHIFT_BUILD_COMMIT', None) - - -def get_run_version(): - """Return a formatted version string for this service.""" - commit_hash = _get_build_openshift_commit_hash() - if commit_hash: - return f'{__version__}-{commit_hash}' - return __version__ diff --git a/queue_services/events-listener/src/events_listener/version.py b/queue_services/events-listener/src/events_listener/version.py deleted file mode 100644 index 313a6e40e..000000000 --- a/queue_services/events-listener/src/events_listener/version.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Version of this service in PEP440. - -[N!]N(.N)*[{a|b|rc}N][.postN][.devN] -Epoch segment: N! -Release segment: N(.N)* -Pre-release segment: {a|b|rc}N -Post-release segment: .postN -Development release segment: .devN -""" - -__version__ = '2.15.2' # pylint: disable=invalid-name diff --git a/queue_services/events-listener/src/events_listener/worker.py b/queue_services/events-listener/src/events_listener/worker.py deleted file mode 100644 index 831ccb18b..000000000 --- a/queue_services/events-listener/src/events_listener/worker.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The unique worker functionality for this service is contained here. - -The entry-point is the **cb_subscription_handler** - -The design and flow leverage a few constraints that are placed upon it -by NATS Streaming and using AWAIT on the default loop. -- NATS streaming queues require one message to be processed at a time. -- AWAIT on the default loop effectively runs synchronously - -If these constraints change, the use of Flask-SQLAlchemy would need to change. -Flask-SQLAlchemy currently allows the base model to be changed, or reworking -the model to a standalone SQLAlchemy usage with an async engine would need -to be pursued. -""" -import json -import os - -import nats -from entity_queue_common.service import QueueServiceManager -from entity_queue_common.service_utils import QueueException, logger -from flask import Flask # pylint: disable=wrong-import-order -from pay_api.models import Invoice, db -from pay_api.services import Flags - -from events_listener import config - - -qsm = QueueServiceManager() # pylint: disable=invalid-name -APP_CONFIG = config.get_named_config(os.getenv('DEPLOYMENT_ENV', 'production')) -FLASK_APP = Flask(__name__) -FLASK_APP.config.from_object(APP_CONFIG) -db.init_app(FLASK_APP) -flag_service = Flags(FLASK_APP) - -INCORPORATION_TYPE = 'bc.registry.business.incorporationApplication' -REGISTRATION = 'bc.registry.business.registration' - - -async def process_event(event_message, flask_app): - """Render the payment status.""" - if not flask_app: - raise QueueException('Flask App not available.') - - with flask_app.app_context(): - if event_message.get('type', None) in [INCORPORATION_TYPE, REGISTRATION] \ - and 'tempidentifier' in event_message \ - and event_message.get('tempidentifier', None) is not None: - - old_identifier = event_message.get('tempidentifier') - new_identifier = event_message.get('identifier') - logger.debug('Received message to update %s to %s', old_identifier, new_identifier) - - # Find all invoice records which have the old corp number - invoices = Invoice.find_by_business_identifier(old_identifier) - for inv in invoices: - inv.business_identifier = new_identifier - inv.flush() - - db.session.commit() - - -async def cb_subscription_handler(msg: nats.aio.client.Msg): - """Use Callback to process Queue Msg objects.""" - try: - logger.info('Received raw message seq:%s, data= %s', msg.sequence, msg.data.decode()) - event_message = json.loads(msg.data.decode('utf-8')) - logger.debug('Event Message Received: %s', event_message) - await process_event(event_message, FLASK_APP) - except Exception: # noqa pylint: disable=broad-except - # Catch Exception so that any error is still caught and the message is removed from the queue - logger.error('Queue Error: %s', json.dumps(event_message), exc_info=True) diff --git a/queue_services/events-listener/tests/conftest.py b/queue_services/events-listener/tests/conftest.py deleted file mode 100644 index 2bc5e4a7e..000000000 --- a/queue_services/events-listener/tests/conftest.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Common setup and fixtures for the pytest suite used by this service.""" -import asyncio -import os -import random -import time -from contextlib import contextmanager - -import pytest -from flask import Flask -from flask_migrate import Migrate, upgrade -from nats.aio.client import Client as Nats -from pay_api import db as _db -from sqlalchemy import event, text -from sqlalchemy.schema import DropConstraint, MetaData -from stan.aio.client import Client as Stan - -from events_listener.config import get_named_config - - -@contextmanager -def not_raises(exception): - """Corallary to the pytest raises builtin. - - Assures that an exception is NOT thrown. - """ - try: - yield - except exception: - raise pytest.fail(f'DID RAISE {exception}') - - -@pytest.fixture(scope='session') -def app(): - """Return a session-wide application configured in TEST mode.""" - # _app = create_app('testing') - _app = Flask(__name__) - _app.config.from_object(get_named_config('testing')) - _db.init_app(_app) - - return _app - - -@pytest.fixture(scope='session') -def db(app): # pylint: disable=redefined-outer-name, invalid-name - """Return a session-wide initialised database. - - Drops all existing tables - Meta follows Postgres FKs - """ - with app.app_context(): - # Clear out any existing tables - metadata = MetaData(_db.engine) - metadata.reflect() - for table in metadata.tables.values(): - for fk in table.foreign_keys: # pylint: disable=invalid-name - _db.engine.execute(DropConstraint(fk.constraint)) - metadata.drop_all() - _db.drop_all() - - sequence_sql = """SELECT sequence_name FROM information_schema.sequences - WHERE sequence_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(sequence_sql))]: - try: - sess.execute(text('DROP SEQUENCE public.%s ;' % seq)) - print('DROP SEQUENCE public.%s ' % seq) - except Exception as err: # noqa pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # ############################################ - # There are 2 approaches, an empty database, or the same one that the app will use - # create the tables - # _db.create_all() - # or - # Use Alembic to load all of the DB revisions including supporting lookup data - # This is the path we'll use in legal_api!! - - # even though this isn't referenced directly, it sets up the internal configs that upgrade - import sys - pay_api_folder = [folder for folder in sys.path if 'pay-api' in folder][0] - migration_path = pay_api_folder.replace('/pay-api/src', '/pay-api/migrations') - - Migrate(app, _db, directory=migration_path) - upgrade() - - return _db - - -@pytest.fixture -def config(app): - """Return the application config.""" - return app.config - - -@pytest.fixture(scope='session') -def client(app): # pylint: disable=redefined-outer-name - """Return a session-wide Flask test client.""" - return app.test_client() - - -@pytest.fixture(scope='session') -def client_ctx(app): # pylint: disable=redefined-outer-name - """Return session-wide Flask test client.""" - with app.test_client() as _client: - yield _client - - -@pytest.fixture(scope='function') -def client_id(): - """Return a unique client_id that can be used in tests.""" - _id = random.SystemRandom().getrandbits(0x58) - # _id = (base64.urlsafe_b64encode(uuid.uuid4().bytes)).replace('=', '') - - return f'client-{_id}' - - -@pytest.fixture(scope='function') -def session(app, db): # pylint: disable=redefined-outer-name, invalid-name - """Return a function-scoped session.""" - with app.app_context(): - conn = db.engine.connect() - txn = conn.begin() - - options = dict(bind=conn, binds={}) - sess = db.create_scoped_session(options=options) - - # establish a SAVEPOINT just before beginning the test - # (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) - sess.begin_nested() - - @event.listens_for(sess(), 'after_transaction_end') - def restart_savepoint(sess2, trans): # pylint: disable=unused-variable - # Detecting whether this is indeed the nested transaction of the test - if trans.nested and not trans._parent.nested: # pylint: disable=protected-access - # Handle where test DOESN'T session.commit(), - sess2.expire_all() - sess.begin_nested() - - db.session = sess - - sql = text('select 1') - sess.execute(sql) - - yield sess - - # Cleanup - sess.remove() - # This instruction rollsback any commit that were executed in the tests. - txn.rollback() - conn.close() - - -@pytest.fixture(scope='session') -def stan_server(docker_services): - """Create the nats / stan services that the integration tests will use.""" - if os.getenv('TEST_NATS_DOCKER'): - docker_services.start('nats') - time.sleep(2) - # TODO get the wait part working, as opposed to sleeping for 2s - # public_port = docker_services.wait_for_service("nats", 4222) - # dsn = "{docker_services.docker_ip}:{public_port}".format(**locals()) - # return dsn - - -@pytest.fixture(scope='function') -@pytest.mark.asyncio -async def stan(event_loop, client_id): - """Create a stan connection for each function, to be used in the tests.""" - nc = Nats() - sc = Stan() - cluster_name = 'test-cluster' - - await nc.connect(io_loop=event_loop, name='entity.filing.tester') - - await sc.connect(cluster_name, client_id, nats=nc) - - yield sc - - await sc.close() - await nc.close() - - -@pytest.fixture(scope='function') -@pytest.mark.asyncio -async def events_stan(app, event_loop, client_id): - """Create a stan connection for each function. - - Uses environment variables for the cluster name. - """ - nc = Nats() - sc = Stan() - - await nc.connect(io_loop=event_loop) - - cluster_name = os.getenv('STAN_CLUSTER_NAME') - - if not cluster_name: - raise ValueError('Missing env variable: STAN_CLUSTER_NAME') - - await sc.connect(cluster_name, client_id, nats=nc) - - yield sc - - await sc.close() - await nc.close() - - -@pytest.fixture(scope='function') -def future(event_loop): - """Return a future that is used for managing function tests.""" - _future = asyncio.Future(loop=event_loop) - return _future - - -@pytest.fixture -def create_mock_coro(mocker, monkeypatch): - """Return a mocked coroutine, and optionally patch-it in.""" - - def _create_mock_patch_coro(to_patch=None): - mock = mocker.Mock() - - async def _coro(*args, **kwargs): - return mock(*args, **kwargs) - - if to_patch: # <-- may not need/want to patch anything - monkeypatch.setattr(to_patch, _coro) - return mock, _coro - - return _create_mock_patch_coro diff --git a/queue_services/events-listener/tests/docker-compose.yml b/queue_services/events-listener/tests/docker-compose.yml deleted file mode 100644 index db7bd6eaa..000000000 --- a/queue_services/events-listener/tests/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '2.1' -services: - nats: - image: nats-streaming - restart: always - mem_limit: 512m - expose: - - 4222 - - 8222 - labels: - - entity.services=nats - ports: - - 4222:4222 - - 8222:8222 - tty: true \ No newline at end of file diff --git a/queue_services/events-listener/tests/integration/__init__.py b/queue_services/events-listener/tests/integration/__init__.py deleted file mode 100644 index 63792ce1a..000000000 --- a/queue_services/events-listener/tests/integration/__init__.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Test suite for the integrations to NATS Queue.""" - -from datetime import datetime - -from pay_api.models import CfsAccount, Invoice, InvoiceReference, Payment, PaymentAccount -from pay_api.utils.enums import InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, PaymentSystem - - -def factory_payment_account(payment_system_code: str = 'PAYBC', payment_method_code: str = 'CC', account_number='4101', - bcol_user_id='test', - auth_account_id: str = '1234'): - """Return Factory.""" - # Create a payment account - account = PaymentAccount( - auth_account_id=auth_account_id, - bcol_user_id=bcol_user_id, - bcol_account='TEST' - ).save() - - CfsAccount(cfs_party='11111', - cfs_account=account_number, - cfs_site='29921', payment_account=account).save() - - if payment_system_code == PaymentSystem.BCOL.value: - account.payment_method = PaymentMethod.DRAWDOWN.value - elif payment_system_code == PaymentSystem.PAYBC.value: - account.payment_method = payment_method_code - - return account - - -def factory_payment( - payment_system_code: str = 'PAYBC', payment_method_code: str = 'CC', - payment_status_code: str = PaymentStatus.CREATED.value, - created_on: datetime = datetime.now(), - invoice_number: str = None -): - """Return Factory.""" - return Payment( - payment_system_code=payment_system_code, - payment_method_code=payment_method_code, - payment_status_code=payment_status_code, - # created_on=created_on, - invoice_number=invoice_number - ).save() - - -def factory_invoice(payment_account, status_code: str = InvoiceStatus.CREATED.value, - corp_type_code='CP', - business_identifier: str = 'CP0001234', - service_fees: float = 0.0, total=0, - payment_method_code: str = PaymentMethod.DIRECT_PAY.value, - created_on: datetime = datetime.now()): - """Return Factory.""" - return Invoice( - invoice_status_code=status_code, - payment_account_id=payment_account.id, - total=total, - created_by='test', - # created_on=created_on, - business_identifier=business_identifier, - corp_type_code=corp_type_code, - folio_number='1234567890', - service_fees=service_fees, - bcol_account=payment_account.bcol_account, - payment_method_code=payment_method_code - ).save() - - -def factory_invoice_reference(invoice_id: int, invoice_number: str = '10021'): - """Return Factory.""" - return InvoiceReference(invoice_id=invoice_id, - status_code=InvoiceReferenceStatus.ACTIVE.value, - invoice_number=invoice_number).save() diff --git a/queue_services/events-listener/tests/integration/test_worker_queue.py b/queue_services/events-listener/tests/integration/test_worker_queue.py deleted file mode 100644 index 5c4792291..000000000 --- a/queue_services/events-listener/tests/integration/test_worker_queue.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Test Suite to ensure the worker routines are working as expected.""" - -import pytest -from entity_queue_common.service_utils import subscribe_to_queue -from pay_api.models import Invoice -from pay_api.utils.enums import PaymentMethod, PaymentSystem - -from tests.integration import factory_invoice, factory_invoice_reference, factory_payment, factory_payment_account - -from .utils import helper_add_event_to_queue - - -@pytest.mark.asyncio -async def test_events_listener_queue(app, session, stan_server, event_loop, client_id, events_stan, future): - """Assert that events can be retrieved and decoded from the Queue.""" - # Call back for the subscription - from events_listener.worker import cb_subscription_handler - - # vars - old_identifier = 'T000000000' - new_identifier = 'BC12345678' - - events_subject = 'test_subject' - events_queue = 'test_queue' - events_durable_name = 'test_durable' - - # Create a Credit Card Payment - - # register the handler to test it - await subscribe_to_queue(events_stan, - events_subject, - events_queue, - events_durable_name, - cb_subscription_handler) - - # add an event to queue - await helper_add_event_to_queue(events_stan, events_subject, old_identifier=old_identifier, - new_identifier=new_identifier) - - assert True - - -@pytest.mark.asyncio -async def test_update_internal_payment(app, session, stan_server, event_loop, client_id, events_stan, future): - """Assert that the update internal payment records works.""" - # Call back for the subscription - from events_listener.worker import cb_subscription_handler - - # vars - old_identifier = 'T000000000' - new_identifier = 'BC12345678' - - events_subject = 'test_subject' - events_queue = 'test_queue' - events_durable_name = 'test_durable' - - # Create an Internal Payment - payment_account = factory_payment_account(payment_system_code=PaymentSystem.BCOL.value).save() - - invoice: Invoice = factory_invoice(payment_account=payment_account, - business_identifier=old_identifier, - payment_method_code=PaymentMethod.INTERNAL.value).save() - - inv_ref = factory_invoice_reference(invoice_id=invoice.id) - factory_payment(invoice_number=inv_ref.invoice_number) - - invoice_id = invoice.id - - # register the handler to test it - await subscribe_to_queue(events_stan, - events_subject, - events_queue, - events_durable_name, - cb_subscription_handler) - - # add an event to queue - await helper_add_event_to_queue(events_stan, events_subject, old_identifier=old_identifier, - new_identifier=new_identifier) - - # Get the internal account and invoice and assert that the identifier is new identifier - invoice = Invoice.find_by_id(invoice_id) - - assert invoice.business_identifier == new_identifier - - -@pytest.mark.asyncio -async def test_update_credit_payment(app, session, stan_server, event_loop, client_id, events_stan, future): - """Assert that the update credit payment records works.""" - # Call back for the subscription - from events_listener.worker import cb_subscription_handler - - # vars - old_identifier = 'T000000000' - new_identifier = 'BC12345678' - - events_subject = 'test_subject' - events_queue = 'test_queue' - events_durable_name = 'test_durable' - - # Create an Internal Payment - - payment_account = factory_payment_account(payment_system_code=PaymentSystem.PAYBC.value, - payment_method_code=PaymentMethod.DIRECT_PAY.value).save() - - invoice: Invoice = factory_invoice(payment_account=payment_account, - business_identifier=old_identifier, - payment_method_code=PaymentMethod.DIRECT_PAY.value).save() - - inv_ref = factory_invoice_reference(invoice_id=invoice.id) - factory_payment(invoice_number=inv_ref.invoice_number) - - invoice_id = invoice.id - - # register the handler to test it - await subscribe_to_queue(events_stan, - events_subject, - events_queue, - events_durable_name, - cb_subscription_handler) - - # add an event to queue - await helper_add_event_to_queue(events_stan, events_subject, old_identifier=old_identifier, - new_identifier=new_identifier) - - # Get the internal account and invoice and assert that the identifier is new identifier - invoice = Invoice.find_by_id(invoice_id) - - assert invoice.business_identifier == new_identifier diff --git a/queue_services/events-listener/tests/integration/utils.py b/queue_services/events-listener/tests/integration/utils.py deleted file mode 100644 index 718c05b7a..000000000 --- a/queue_services/events-listener/tests/integration/utils.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Utilities used by the integration tests.""" -import json - -import stan - - -async def helper_add_event_to_queue(stan_client: stan.aio.client.Client, - subject: str, - old_identifier: str = 'T1234567890', - new_identifier: str = 'BC1234567890'): - """Add event to the Queue.""" - payload = { - 'specversion': '1.x-wip', - 'type': 'bc.registry.business.incorporationApplication', - 'source': 'https://api.business.bcregistry.gov.bc.ca/v1/business/BC1234567/filing/12345678', - 'id': 'C234-1234-1234', - 'time': '2020-08-28T17:37:34.651294+00:00', - 'datacontenttype': 'application/json', - 'identifier': new_identifier, - 'tempidentifier': old_identifier, - 'data': { - 'filing': { - 'header': {'filingId': '12345678'}, - 'business': {'identifier': 'BC1234567'} - } - } - } - - await stan_client.publish(subject=subject, - payload=json.dumps(payload).encode('utf-8')) diff --git a/queue_services/payment-reconciliations/.envrc b/queue_services/payment-reconciliations/.envrc deleted file mode 100644 index 64fff9a69..000000000 --- a/queue_services/payment-reconciliations/.envrc +++ /dev/null @@ -1,6 +0,0 @@ -while read -r line; do - echo $line - [[ "$line" =~ ^#.*$ ]] && continue - export $line -done < .env -source venv/bin/activate diff --git a/queue_services/payment-reconciliations/LICENSE b/queue_services/payment-reconciliations/LICENSE deleted file mode 100644 index 18b5abc34..000000000 --- a/queue_services/payment-reconciliations/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright © 2018 Province of British Columbia - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/queue_services/payment-reconciliations/MANIFEST.in b/queue_services/payment-reconciliations/MANIFEST.in deleted file mode 100644 index 1a342bdeb..000000000 --- a/queue_services/payment-reconciliations/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include requirements.txt -include config.py -include logging.conf -include LICENSE -include README.md \ No newline at end of file diff --git a/queue_services/payment-reconciliations/README.md b/queue_services/payment-reconciliations/README.md deleted file mode 100755 index 2ae0e90f6..000000000 --- a/queue_services/payment-reconciliations/README.md +++ /dev/null @@ -1,75 +0,0 @@ - -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) - - -# Application Name - -BC Registries Payments system API - -## Technology Stack Used -* Python, Flask -* Postgres - SQLAlchemy, psycopg2-binary & alembic - -## Third-Party Products/Libraries used and the the License they are covert by - -## Project Status -As of 2018-02-22 in **ALPHA** - -## Documnentation - -GitHub Pages (https://guides.github.com/features/pages/) are a neat way to document you application/project. - -## Security - -Future - BCGov Keycloak - -Current - JWT hack - -## Files in this repository - -``` -docs/ - Project Documentation -└── images -└── icons - -openshift/ - OpenShift-specific files -├── scripts - helper scripts -└── templates - application templates -``` - -## Deployment (Local Development) - -* Developer Workstation Requirements/Setup -* Application Specific Setup - -## Deployment (OpenShift) - -See (openshift/Readme.md) - -## Getting Help or Reporting an Issue - -To report bugs/issues/feature requests, please file an [issue](../../issues). - -## How to Contribute - -If you would like to contribute, please see our [CONTRIBUTING](./CONTRIBUTING.md) guidelines. - -Please note that this project is released with a [Contributor Code of Conduct](./CODE_OF_CONDUCT.md). -By participating in this project you agree to abide by its terms. - -## License - - Copyright 2018 Province of British Columbia - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/queue_services/payment-reconciliations/app.py b/queue_services/payment-reconciliations/app.py deleted file mode 100755 index 52754b9c5..000000000 --- a/queue_services/payment-reconciliations/app.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""s2i based launch script to run the service.""" -import asyncio -import os - -from reconciliations.worker import APP_CONFIG, cb_subscription_handler, qsm - - -if __name__ == '__main__': - - # my_config = config.get_named_config(os.getenv('DEPLOYMENT_ENV', 'production')) - - event_loop = asyncio.get_event_loop() - event_loop.run_until_complete(qsm.run(loop=event_loop, - config=APP_CONFIG, - callback=cb_subscription_handler)) - try: - event_loop.run_forever() - finally: - event_loop.close() diff --git a/queue_services/payment-reconciliations/q_cli.py b/queue_services/payment-reconciliations/q_cli.py deleted file mode 100755 index d05c10f2d..000000000 --- a/queue_services/payment-reconciliations/q_cli.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Service for listening and handling Queue Messages. - -This service registers interest in listening to a Queue and processing received messages. -""" -import asyncio -import functools -import getopt -import json -import os -import random -import signal -import sys - -from nats.aio.client import Client as NATS # noqa N814; by convention the name is NATS -from stan.aio.client import Client as STAN # noqa N814; by convention the name is STAN - - -async def run(loop, file_name: str, location: str, message_type: str): # pylint: disable=too-many-locals - """Run the main application loop for the service. - - This runs the main top level service functions for working with the Queue. - """ - from entity_queue_common.service_utils import error_cb, logger, signal_handler - - # NATS client connections - nc = NATS() - sc = STAN() - - async def close(): - """Close the stream and nats connections.""" - await sc.close() - await nc.close() - - # Connection and Queue configuration. - def nats_connection_options(): - return { - 'servers': os.getenv('NATS_SERVERS', 'nats://127.0.0.1:4222').split(','), - 'io_loop': loop, - 'error_cb': error_cb, - 'name': os.getenv('NATS_PAYMENT_RECONCILIATIONS_CLIENT_NAME', 'account.reconciliations.worker') - } - - def stan_connection_options(): - return { - 'cluster_id': os.getenv('NATS_CLUSTER_ID', 'test-cluster'), - 'client_id': str(random.SystemRandom().getrandbits(0x58)), - 'nats': nc - } - - def subscription_options(): - return { - 'subject': os.getenv('NATS_PAYMENT_RECONCILIATIONS_SUBJECT', 'payment.reconciliations'), - 'queue': os.getenv('NATS_PAYMENT_RECONCILIATIONS_QUEUE', 'payment-reconciliations-worker'), - 'durable_name': os.getenv('NATS_PAYMENT_RECONCILIATIONS_QUEUE', - 'payment-reconciliations-worker') + '_durable' - } - - try: - # Connect to the NATS server, and then use that for the streaming connection. - await nc.connect(**nats_connection_options()) - await sc.connect(**stan_connection_options()) - - # register the signal handler - for sig in ('SIGINT', 'SIGTERM'): - loop.add_signal_handler(getattr(signal, sig), - functools.partial(signal_handler, sig_loop=loop, sig_nc=nc, task=close) - ) - - payload = { - 'specversion': '1.x-wip', - 'type': f'{message_type}', - 'source': 'https://api.business.bcregistry.gov.bc.ca/v1/business/BC1234567/filing/12345678', - 'id': 'C234-1234-1234', - 'time': '2020-08-28T17:37:34.651294+00:00', - 'datacontenttype': 'application/json', - 'data': { - 'fileName': file_name, - 'source': 'MINIO', - 'location': location - - } - } - - print('payload-->', payload) - print(subscription_options()) - - await sc.publish(subject=subscription_options().get('subject'), - payload=json.dumps(payload).encode('utf-8')) - - except Exception as e: # pylint: disable=broad-except - # TODO tighten this error and decide when to bail on the infinite reconnect - logger.error(e) - - -if __name__ == '__main__': - try: - opts, args = getopt.getopt(sys.argv[1:], "hf:l:m:", ["file=", "location=", "message="]) - except getopt.GetoptError: - sys.exit(2) - - for opt, arg in opts: - if opt == '-h': - sys.exit() - elif opt in ("-f", "--file"): - file = arg - elif opt in ("-l", "--location"): - location = arg - if not location: - location = 'payment-sftp' - elif opt in ("-m", "--message"): - message = arg - if not message: - message = 'bc.registry.payment.casSettlementUploaded' - - event_loop = asyncio.get_event_loop() - event_loop.run_until_complete(run(event_loop, file, location, message)) diff --git a/queue_services/payment-reconciliations/requirements.txt b/queue_services/payment-reconciliations/requirements.txt deleted file mode 100644 index 5d330d3fb..000000000 --- a/queue_services/payment-reconciliations/requirements.txt +++ /dev/null @@ -1,93 +0,0 @@ --e git+https://github.com/bcgov/lear.git@30dba30463c99aaedfdcfd463213e71ba0d35b51#egg=entity_queue_common&subdirectory=queue_services/common --e git+https://github.com/bcgov/sbc-common-components.git@b93585ea3ac273b9e51c4dd5ddbc8190fd95da6a#egg=sbc_common_components&subdirectory=python --e git+https://github.com/bcgov/sbc-pay.git@6d4a94c8d78f11322487f93eb65ec6e9d5238b35#egg=pay_api&subdirectory=pay-api -Flask-Caching==2.0.2 -Flask-Migrate==2.7.0 -Flask-Moment==1.0.5 -Flask-OpenTracing==1.1.0 -Flask-SQLAlchemy==2.5.1 -Flask-Script==2.0.6 -Flask==1.1.2 -Jinja2==3.0.3 -Mako==1.2.4 -MarkupSafe==2.1.3 -PyMeeus==0.5.12 -SQLAlchemy-Continuum==1.3.15 -SQLAlchemy-Utils==0.41.1 -SQLAlchemy==1.3.24 -Werkzeug==1.0.1 -aiohttp==3.9.2 -aiosignal==1.3.1 -alembic==1.11.1 -aniso8601==9.0.1 -async-timeout==4.0.2 -asyncio-nats-client==0.11.5 -asyncio-nats-streaming==0.4.0 -attrs==23.1.0 -blinker==1.6.2 -cachelib==0.9.0 -cachetools==5.3.1 -cattrs==23.1.2 -certifi==2023.7.22 -cffi==1.15.1 -charset-normalizer==3.1.0 -click==8.1.3 -convertdate==2.4.0 -croniter==1.4.1 -cryptography==42.0.2 -dpath==2.1.6 -ecdsa==0.18.0 -exceptiongroup==1.1.1 -expiringdict==1.2.2 -flask-jwt-oidc==0.3.0 -flask-marshmallow==0.11.0 -flask-restx==1.1.0 -frozenlist==1.3.3 -google-api-core==2.11.1 -google-auth==2.18.1 -google-cloud-pubsub==2.17.0 -googleapis-common-protos==1.59.1 -grpc-google-iam-v1==0.12.6 -grpcio-status==1.48.2 -grpcio==1.54.3 -gunicorn==20.1.0 -hijri-converter==2.3.1 -holidays==0.37 -idna==3.4 -itsdangerous==2.0.1 -jaeger-client==4.8.0 -jsonschema==4.17.3 -korean-lunar-calendar==0.3.1 -launchdarkly-server-sdk==8.1.4 -marshmallow-sqlalchemy==0.25.0 -marshmallow==3.19.0 -minio==7.1.15 -multidict==6.0.4 -opentracing==2.4.0 -packaging==23.1 -proto-plus==1.22.2 -protobuf==3.19.6 -psycopg2-binary==2.9.6 -pyRFC3339==1.1 -pyasn1-modules==0.3.0 -pyasn1==0.5.0 -pycountry==22.3.5 -pycparser==2.21 -pyrsistent==0.19.3 -python-dateutil==2.8.2 -python-dotenv==1.0.0 -python-jose==3.3.0 -pytz==2023.3 -requests==2.31.0 -rsa==4.9 -semver==2.13.0 -sentry-sdk==1.26.0 -simple-cloudevent @ git+https://github.com/daxiom/simple-cloudevent.py.git@447cabb988202206ac69e71177d7cd11b6c0b002 -six==1.16.0 -strict-rfc3339==0.7 -threadloop==1.0.2 -thrift==0.16.0 -tornado==6.3.3 -typing_extensions==4.6.3 -urllib3==1.26.17 -yarl==1.9.2 diff --git a/queue_services/payment-reconciliations/requirements/bcregistry-libraries.txt b/queue_services/payment-reconciliations/requirements/bcregistry-libraries.txt deleted file mode 100644 index 623fffcbd..000000000 --- a/queue_services/payment-reconciliations/requirements/bcregistry-libraries.txt +++ /dev/null @@ -1,5 +0,0 @@ --e git+https://github.com/bcgov/lear.git#egg=entity_queue_common&subdirectory=queue_services/common --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -# -e git+https://github.com/bcgov/sbc-pay.git@refunds#egg=pay-api&subdirectory=pay-api --e git+https://github.com/bcgov/sbc-pay.git@main#egg=pay-api&subdirectory=pay-api -git+https://github.com/daxiom/simple-cloudevent.py.git diff --git a/queue_services/payment-reconciliations/requirements/dev.txt b/queue_services/payment-reconciliations/requirements/dev.txt deleted file mode 100755 index 04624060f..000000000 --- a/queue_services/payment-reconciliations/requirements/dev.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov -FreezeGun - -# Lint and code style -flake8==5.0.4 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -pydocstyle -isort - - -# docker -lovely-pytest-docker -pytest-asyncio==0.18.3 - diff --git a/queue_services/payment-reconciliations/requirements/prod.txt b/queue_services/payment-reconciliations/requirements/prod.txt deleted file mode 100644 index c4f830f82..000000000 --- a/queue_services/payment-reconciliations/requirements/prod.txt +++ /dev/null @@ -1,16 +0,0 @@ -Flask -jsonschema==4.17.3 -python-dotenv -sentry-sdk[flask] -asyncio-nats-client -asyncio-nats-streaming -pycountry -Werkzeug<2 -minio -jaeger-client -attrs -sqlalchemy<1.4 -itsdangerous==2.0.1 -Jinja2==3.0.3 -protobuf~=3.19.5 -launchdarkly-server-sdk diff --git a/queue_services/payment-reconciliations/src/reconciliations/__init__.py b/queue_services/payment-reconciliations/src/reconciliations/__init__.py deleted file mode 100644 index aef4cc171..000000000 --- a/queue_services/payment-reconciliations/src/reconciliations/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The Reconciliations queue service. - -This module is the service worker for applying payments, receipts and account balance to payment system. -""" diff --git a/queue_services/payment-reconciliations/src/reconciliations/utils.py b/queue_services/payment-reconciliations/src/reconciliations/utils.py deleted file mode 100644 index ca782b559..000000000 --- a/queue_services/payment-reconciliations/src/reconciliations/utils.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Supply version and commit hash info. - -When deployed in OKD, it adds the last commit hash onto the version info. -""" -import os - -from reconciliations.version import __version__ - - -def _get_build_openshift_commit_hash(): - return os.getenv('OPENSHIFT_BUILD_COMMIT', None) - - -def get_run_version(): - """Return a formatted version string for this service.""" - commit_hash = _get_build_openshift_commit_hash() - if commit_hash: - return f'{__version__}-{commit_hash}' - return __version__ diff --git a/queue_services/payment-reconciliations/src/reconciliations/worker.py b/queue_services/payment-reconciliations/src/reconciliations/worker.py deleted file mode 100644 index bc47ad577..000000000 --- a/queue_services/payment-reconciliations/src/reconciliations/worker.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The unique worker functionality for this service is contained here. - -The entry-point is the **cb_subscription_handler** - -The design and flow leverage a few constraints that are placed upon it -by NATS Streaming and using AWAIT on the default loop. -- NATS streaming queues require one message to be processed at a time. -- AWAIT on the default loop effectively runs synchronously - -If these constraints change, the use of Flask-SQLAlchemy would need to change. -Flask-SQLAlchemy currently allows the base model to be changed, or reworking -the model to a standalone SQLAlchemy usage with an async engine would need -to be pursued. -""" -import json -import os - -import nats -from entity_queue_common.service import QueueServiceManager -from entity_queue_common.service_utils import QueueException, logger -from flask import Flask -from pay_api.models import db -from pay_api.services import Flags - -from reconciliations import config -from reconciliations.cgi_reconciliations import reconcile_distributions -from reconciliations.eft.eft_reconciliation import reconcile_eft_payments -from reconciliations.enums import MessageType -from reconciliations.payment_reconciliations import reconcile_payments - - -qsm = QueueServiceManager() # pylint: disable=invalid-name -APP_CONFIG = config.get_named_config(os.getenv('DEPLOYMENT_ENV', 'production')) -FLASK_APP = Flask(__name__) -FLASK_APP.config.from_object(APP_CONFIG) -db.init_app(FLASK_APP) -flag_service = Flags(FLASK_APP) - - -async def process_event(event_message, flask_app): - """Render the payment status.""" - if not flask_app: - raise QueueException('Flask App not available.') - - with flask_app.app_context(): - if (message_type := event_message.get('type', None)) == MessageType.CAS_UPLOADED.value: - await reconcile_payments(event_message) - elif message_type == MessageType.CGI_ACK_RECEIVED.value: - await reconcile_distributions(event_message) - elif message_type == MessageType.CGI_FEEDBACK_RECEIVED.value: - await reconcile_distributions(event_message, is_feedback=True) - elif message_type == MessageType.EFT_FILE_UPLOADED.value: - await reconcile_eft_payments(event_message) - else: - raise Exception('Invalid type') # pylint: disable=broad-exception-raised - - -async def cb_subscription_handler(msg: nats.aio.client.Msg): - """Use Callback to process Queue Msg objects.""" - try: - logger.info('Received raw message seq:%s, data= %s', msg.sequence, msg.data.decode()) - event_message = json.loads(msg.data.decode('utf-8')) - logger.debug('Event Message Received: %s', event_message) - await process_event(event_message, FLASK_APP) - except Exception as e: # NOQA pylint: disable=broad-except - # Catch Exception so that any error is still caught and the message is removed from the queue - logger.error('Queue Error: %s', json.dumps(event_message), exc_info=True) - logger.error(e) diff --git a/queue_services/payment-reconciliations/tests/__init__.py b/queue_services/payment-reconciliations/tests/__init__.py deleted file mode 100644 index 3e44a42f5..000000000 --- a/queue_services/payment-reconciliations/tests/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The Test Suites to ensure that the service is built and operating correctly.""" -import datetime - - -EPOCH_DATETIME = datetime.datetime.utcfromtimestamp(0) -FROZEN_DATETIME = datetime.datetime(2001, 8, 5, 7, 7, 58, 272362) - - -def add_years(d, years): - """Return a date that's `years` years after the date (or datetime). - - Return the same calendar date (month and day) in the destination year, - if it exists, otherwise use the following day - (thus changing February 29 to February 28). - """ - try: - return d.replace(year=d.year + years) - except ValueError: - return d + (datetime.date(d.year + years, 3, 1) - datetime.date(d.year, 3, 1)) diff --git a/queue_services/payment-reconciliations/tests/conftest.py b/queue_services/payment-reconciliations/tests/conftest.py deleted file mode 100644 index 24c346ae3..000000000 --- a/queue_services/payment-reconciliations/tests/conftest.py +++ /dev/null @@ -1,278 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Common setup and fixtures for the pytest suite used by this service.""" -import asyncio -import os -import random -import time -from contextlib import contextmanager - -import pytest -from flask import Flask -from flask_migrate import Migrate, upgrade -from nats.aio.client import Client as Nats -from pay_api import db as _db -from sqlalchemy import event, text -from sqlalchemy.schema import DropConstraint, MetaData -from stan.aio.client import Client as Stan - -from reconciliations.config import get_named_config - - -@contextmanager -def not_raises(exception): - """Corallary to the pytest raises builtin. - - Assures that an exception is NOT thrown. - """ - try: - yield - except exception: # NOQA - raise pytest.fail(f'DID RAISE {exception}') - - -@pytest.fixture(scope='session') -def app(): - """Return a session-wide application configured in TEST mode.""" - # _app = create_app('testing') - _app = Flask(__name__) - _app.config.from_object(get_named_config('testing')) - _db.init_app(_app) - - return _app - - -@pytest.fixture(scope='session') -def db(app): # pylint: disable=redefined-outer-name, invalid-name - """Return a session-wide initialised database. - - Drops all existing tables - Meta follows Postgres FKs - """ - with app.app_context(): - # Clear out views - view_sql = """SELECT table_name FROM information_schema.views - WHERE table_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(view_sql))]: - try: - sess.execute(text('DROP VIEW public.%s ;' % seq)) - print('DROP VIEW public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # Clear out any existing tables - metadata = MetaData(_db.engine) - metadata.reflect() - for table in metadata.tables.values(): - for fk in table.foreign_keys: # pylint: disable=invalid-name - _db.engine.execute(DropConstraint(fk.constraint)) - metadata.drop_all() - _db.drop_all() - - sequence_sql = """SELECT sequence_name FROM information_schema.sequences - WHERE sequence_schema='public' - """ - - sess = _db.session() - for seq in [name for (name,) in sess.execute(text(sequence_sql))]: - try: - sess.execute(text('DROP SEQUENCE public.%s ;' % seq)) - print('DROP SEQUENCE public.%s ' % seq) - except Exception as err: # NOQA pylint: disable=broad-except - print(f'Error: {err}') - sess.commit() - - # ############################################ - # There are 2 approaches, an empty database, or the same one that the app will use - # create the tables - # _db.create_all() - # or - # Use Alembic to load all of the DB revisions including supporting lookup data - # This is the path we'll use in legal_api!! - - # even though this isn't referenced directly, it sets up the internal configs that upgrade - import sys - migrations_path = [folder for folder in sys.path if 'pay-api/pay-api' in folder] - if len(migrations_path) > 0: - migrations_path = migrations_path[0].replace('/pay-api/src', '/pay-api/migrations') - # Fix for windows. - else: - migrations_path = os.path.abspath('../../pay-api/migrations') - - Migrate(app, _db, directory=migrations_path) - upgrade() - - return _db - - -@pytest.fixture -def config(app): - """Return the application config.""" - return app.config - - -@pytest.fixture(scope='session') -def client(app): # pylint: disable=redefined-outer-name - """Return a session-wide Flask test client.""" - return app.test_client() - - -@pytest.fixture(scope='session') -def client_ctx(app): # pylint: disable=redefined-outer-name - """Return session-wide Flask test client.""" - with app.test_client() as _client: - yield _client - - -@pytest.fixture(scope='function') -def client_id(): - """Return a unique client_id that can be used in tests.""" - _id = random.SystemRandom().getrandbits(0x58) - # _id = (base64.urlsafe_b64encode(uuid.uuid4().bytes)).replace('=', '') - - return f'client-{_id}' - - -@pytest.fixture(scope='function') -def session(app, db): # pylint: disable=redefined-outer-name, invalid-name - """Return a function-scoped session.""" - with app.app_context(): - conn = db.engine.connect() - txn = conn.begin() - - options = dict(bind=conn, binds={}) - sess = db.create_scoped_session(options=options) - - # establish a SAVEPOINT just before beginning the test - # (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) - sess.begin_nested() - - @event.listens_for(sess(), 'after_transaction_end') - def restart_savepoint(sess2, trans): # pylint: disable=unused-variable - # Detecting whether this is indeed the nested transaction of the test - if trans.nested and not trans._parent.nested: # pylint: disable=protected-access - # Handle where test DOESN'T session.commit(), - # sess2.expire_all() - sess.begin_nested() - - db.session = sess - - sql = text('select 1') - sess.execute(sql) - - yield sess - - # Cleanup - sess.remove() - # This instruction rollsback any commit that were executed in the tests. - txn.rollback() - conn.close() - - -@pytest.fixture(scope='session') -def stan_server(docker_services): - """Create the nats / stan services that the integration tests will use.""" - if os.getenv('TEST_NATS_DOCKER'): - docker_services.start('nats') - time.sleep(2) - # TODO get the wait part working, as opposed to sleeping for 2s - # public_port = docker_services.wait_for_service("nats", 4222) - # dsn = "{docker_services.docker_ip}:{public_port}".format(**locals()) - # return dsn - - -@pytest.fixture(scope='session', autouse=True) -def auto(docker_services, app): - """Spin up docker containers.""" - if app.config['USE_DOCKER_MOCK']: - docker_services.start('minio') - docker_services.start('proxy') - docker_services.start('paybc') - docker_services.start('nats') - - -@pytest.fixture(scope='function') -@pytest.mark.asyncio -async def stan(event_loop, client_id): - """Create a stan connection for each function, to be used in the tests.""" - nc = Nats() - sc = Stan() - cluster_name = 'test-cluster' - - await nc.connect(io_loop=event_loop, name='entity.filing.tester') - - await sc.connect(cluster_name, client_id, nats=nc) - - yield sc - - await sc.close() - await nc.close() - - -@pytest.fixture(scope='function') -@pytest.mark.asyncio -async def events_stan(app, event_loop, client_id): - """Create a stan connection for each function. - - Uses environment variables for the cluster name. - """ - nc = Nats() - sc = Stan() - - await nc.connect(io_loop=event_loop) - - cluster_name = os.getenv('STAN_CLUSTER_NAME', 'test-cluster') - - if not cluster_name: - raise ValueError('Missing env variable: STAN_CLUSTER_NAME') - - await sc.connect(cluster_name, client_id, nats=nc) - - yield sc - - await sc.close() - await nc.close() - - -@pytest.fixture(scope='function') -def future(event_loop): - """Return a future that is used for managing function tests.""" - _future = asyncio.Future(loop=event_loop) - return _future - - -@pytest.fixture -def create_mock_coro(mocker, monkeypatch): - """Return a mocked coroutine, and optionally patch-it in.""" - - def _create_mock_patch_coro(to_patch=None): - mock = mocker.Mock() - - async def _coro(*args, **kwargs): - return mock(*args, **kwargs) - - if to_patch: # <-- may not need/want to patch anything - monkeypatch.setattr(to_patch, _coro) - return mock, _coro - - return _create_mock_patch_coro - - -@pytest.fixture() -def mock_publish(monkeypatch): - """Mock check_auth.""" - monkeypatch.setattr('pay_api.services.queue_publisher.publish', lambda *args, **kwargs: None) diff --git a/report-api/src/api/utils/logging.py b/report-api/src/api/utils/logging.py index 8b88ddcf2..8568f87dd 100755 --- a/report-api/src/api/utils/logging.py +++ b/report-api/src/api/utils/logging.py @@ -18,12 +18,7 @@ def setup_logging(conf): - """Create the services logger. - - TODO should be reworked to load in the proper loggers and remove others - """ - # log_file_path = path.join(path.abspath(path.dirname(__file__)), conf) - + """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) print(f'Configure logging, from conf:{conf}', file=sys.stdout) From 3552acd9be9e371a77522597c9282d39be79d44e Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Wed, 13 Mar 2024 16:22:27 -0700 Subject: [PATCH 16/87] 19724_2 - EFT - Create CFS account job (#1442) * update api and payment-jobs for EFT cfs_account create * add updated requirements.txt from PR#18263 * isort fix * EFT - Create CFS account job - 2 * remove redundant function * remove incorrect comment --- jobs/payment-jobs/tasks/cfs_create_account_task.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index 62e128ca3..1f0b72fa5 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -24,7 +24,7 @@ from pay_api.utils.constants import RECEIPT_METHOD_EFT_MONTHLY, RECEIPT_METHOD_PAD_DAILY from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, PaymentMethod from sentry_sdk import capture_message -from services import routing_slip +from services import eft_service, routing_slip from utils import mailer from utils.auth import get_token @@ -95,7 +95,11 @@ def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: Paym 'bankAccountName': pay_account.name } - if pending_account.cfs_account and pending_account.cfs_party and pending_account.cfs_site: + if pay_account.payment_method == PaymentMethod.EFT.value: + cfs_account_details = CFSService.create_cfs_account(identifier=pay_account.auth_account_id, + contact_info=contact_info, + receipt_method=RECEIPT_METHOD_EFT_MONTHLY) + elif pending_account.cfs_account and pending_account.cfs_party and pending_account.cfs_site: # This means, PAD account details have changed. So update banking details for this CFS account bank_details = CFSService.update_bank_details(name=pay_account.auth_account_id, party_number=pending_account.cfs_party, @@ -104,12 +108,8 @@ def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: Paym payment_info=payment_info) pending_account.payment_instrument_number = bank_details.get('payment_instrument_number', None) else: # It's a new account, now create - if pay_account.payment_method == PaymentMethod.EFT.value: - cfs_account_details = CFSService.create_cfs_account(identifier=pay_account.auth_account_id, - contact_info=contact_info, - receipt_method=RECEIPT_METHOD_EFT_MONTHLY) # If the account have banking information, then create a PAD account else a regular account. - elif pending_account.bank_number and pending_account.bank_branch_number \ + if pending_account.bank_number and pending_account.bank_branch_number \ and pending_account.bank_account_number: cfs_account_details = CFSService.create_cfs_account(identifier=pay_account.auth_account_id, contact_info=contact_info, From c16e890423bb072c08fd197810a3e25608d6cd56 Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Wed, 13 Mar 2024 16:58:11 -0700 Subject: [PATCH 17/87] Pay API CD upgrade. --- .github/workflows/pay-api-cd.yml | 119 ++----- pay-api/devops/gcp/clouddeploy-targets.yaml | 100 ++++++ pay-api/devops/vaults.gcp.env | 59 ++++ pay-api/devops/vaults.json | 49 --- pay-api/src/pay_api/config.py | 324 +++++++++++--------- 5 files changed, 354 insertions(+), 297 deletions(-) create mode 100644 pay-api/devops/gcp/clouddeploy-targets.yaml create mode 100644 pay-api/devops/vaults.gcp.env delete mode 100644 pay-api/devops/vaults.json diff --git a/.github/workflows/pay-api-cd.yml b/.github/workflows/pay-api-cd.yml index 09a994b61..4373a3d51 100644 --- a/.github/workflows/pay-api-cd.yml +++ b/.github/workflows/pay-api-cd.yml @@ -3,111 +3,28 @@ name: Pay API CD on: push: branches: - - main + - feature-queue-python-upgrade paths: - "pay-api/**" workflow_dispatch: inputs: - environment: - description: "Environment (dev/test/prod)" + target: + description: "Deploy To" required: true - default: "dev" - -defaults: - run: - shell: bash - working-directory: ./pay-api - -env: - APP_NAME: "pay-api" - TAG_NAME: "dev" + type: choice + options: + - dev + - test + - sandbox + - prod jobs: - pay-api-cd-by-push: - runs-on: ubuntu-20.04 - - if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' - environment: - name: "dev" - - steps: - - uses: actions/checkout@v3 - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Pay API Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} - - pay-api-cd-by-dispatch: - runs-on: ubuntu-20.04 - - if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' - environment: - name: "${{ github.event.inputs.environment }}" - - steps: - - uses: actions/checkout@v3 - - name: Set env by input - run: | - echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Pay API Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} + pay-api-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-api" + working_directory: "./pay-api" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/pay-api/devops/gcp/clouddeploy-targets.yaml b/pay-api/devops/gcp/clouddeploy-targets.yaml new file mode 100644 index 000000000..45fffe18f --- /dev/null +++ b/pay-api/devops/gcp/clouddeploy-targets.yaml @@ -0,0 +1,100 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: dev +description: Dev Environment +deployParameters: + deploy-project-id: "gtksf3-dev" + service-name: "pay-api-dev" + container-name: "pay-api-dev" + app-env: "dev" + cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" + service-account: "sa-api@gtksf3-dev.iam.gserviceaccount.com" +run: + location: projects/gtksf3-dev/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: test +description: Test Environment +deployParameters: + deploy-project-id: "gtksf3-test" + service-name: "pay-api-test" + container-name: "pay-api-test" + app-env: "test" + cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" + service-account: "sa-api@gtksf3-test.iam.gserviceaccount.com" +run: + location: projects/gtksf3-test/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: sandbox +description: Sandbox Environment +requireApproval: true +deployParameters: + deploy-project-id: "gtksf3-tools" + service-name: "pay-api-sandbox" + container-name: "pay-api-sandbox" + app-env: "sandbox" + cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" + service-account: "sa-api@gtksf3-tools.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-tools/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: prod +description: Production Environment +requireApproval: true +deployParameters: + deploy-project-id: "gtksf3-prod" + service-name: "pay-api-prod" + container-name: "pay-api-prod" + app-env: "production" + cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" + service-account: "sa-api@gtksf3-prod.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-prod/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' \ No newline at end of file diff --git a/pay-api/devops/vaults.gcp.env b/pay-api/devops/vaults.gcp.env new file mode 100644 index 000000000..e40127bab --- /dev/null +++ b/pay-api/devops/vaults.gcp.env @@ -0,0 +1,59 @@ +PAY_LD_SDK_KEY="op://launchdarkly/$APP_ENV/pay/PAY_LD_SDK_KEY" +DATABASE_NAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_NAME" +DATABASE_PASSWORD="op://database/$APP_ENV/pay-db-gcp/DATABASE_PASSWORD" +DATABASE_PORT="op://database/$APP_ENV/pay-db-gcp/DATABASE_PORT" +DATABASE_UNIX_SOCKET="op://database/$APP_ENV/pay-db-gcp/DATABASE_UNIX_SOCKET" +DATABASE_USERNAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_USERNAME" +JWT_OIDC_AUDIENCE="op://keycloak/$APP_ENV/account-services-account/ACCOUNT_SERVICES_SERVICE_ACCOUNT_CLIENT_ID" +JWT_OIDC_JWKS_CACHE_TIMEOUT="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_JWKS_CACHE_TIMEOUT" +JWT_OIDC_WELL_KNOWN_CONFIG="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_WELL_KNOWN_CONFIG" +JWT_OIDC_ISSUER="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_ISSUER" +JWT_OIDC_CACHING_ENABLED="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_CACHING_ENABLED" +JWT_OIDC_ALGORITHMS="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_ALGORITHMS" +SBC_AUTH_ADMIN_CLIENT_ID="op://keycloak/$APP_ENV/sbc-auth-admin/SBC_AUTH_ADMIN_CLIENT_ID" +SBC_AUTH_ADMIN_CLIENT_SECRET="op://keycloak/$APP_ENV/sbc-auth-admin/SBC_AUTH_ADMIN_CLIENT_SECRET" +CFS_BASE_URL="op://payment-external-services/$APP_ENV/cfs/CFS_BASE_URL" +CFS_CLIENT_ID="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_ID" +CFS_CLIENT_SECRET="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_SECRET" +PAYBC_PORTAL_URL="op://payment-external-services/$APP_ENV/cfs/PAYBC_PORTAL_URL" +CONNECT_TIMEOUT="op://payment-external-services/$APP_ENV/cfs/CONNECT_TIMEOUT" +CFS_GENERATE_RANDOM_INVOICE_NUMBER="op://payment-external-services/$APP_ENV/cfs/CFS_GENERATE_RANDOM_INVOICE_NUMBER" +CFS_ACCOUNT_DESCRIPTION="op://payment-external-services/$APP_ENV/cfs/CFS_ACCOUNT_DESCRIPTION" +CFS_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_INVOICE_PREFIX" +CFS_RECEIPT_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_RECEIPT_PREFIX" +CFS_PARTY_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_PARTY_PREFIX" +EFT_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/eft/EFT_INVOICE_PREFIX" +PAYBC_DIRECT_PAY_REF_NUMBER="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_REF_NUMBER" +PAYBC_DIRECT_PAY_API_KEY="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_API_KEY" +PAYBC_DIRECT_PAY_PORTAL_URL="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_PORTAL_URL" +PAYBC_DIRECT_PAY_BASE_URL="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_BASE_URL" +PAYBC_DIRECT_PAY_CLIENT_ID="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CLIENT_ID" +PAYBC_DIRECT_PAY_CLIENT_SECRET="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CLIENT_SECRET" +PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL" +AUDIENCE="op://gcp-queue/$APP_ENV/base/AUDIENCE" +GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/base/GCP_AUTH_KEY" +PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" +ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" +EVENT_LISTENER_TOPIC="op://gcp-queue/$APP_ENV/topics/EVENT_LISTENER_TOPIC" +NAMEX_PAY_TOPIC="op://gcp-queue/$APP_ENV/topics/NAMEX_PAY_TOPIC" +BUSINESS_PAY_TOPIC="op://gcp-queue/$APP_ENV/topics/BUSINESS_PAY_TOPIC" +AUTH_API_URL="op://API/$APP_ENV/auth-api/AUTH_API_URL" +AUTH_API_VERSION="op://API/$APP_ENV/auth-api/AUTH_API_VERSION" +BCOL_API_URL="op://API/$APP_ENV/bcol-api/BCOL_API_URL" +BCOL_API_VERSION="op://API/$APP_ENV/bcol-api/BCOL_API_VERSION" +REPORT_API_URL="op://API/$APP_ENV/report-api/REPORT_API_URL" +REPORT_API_VERSION="op://API/$APP_ENV/report-api/REPORT_API_VERSION" +SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" +SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" +DISABLE_VALID_REDIRECT_URLS="op://relationship/$APP_ENV/pay-api/DISABLE_VALID_REDIRECT_URLS" +VALID_REDIRECT_URLS="op://relationship/$APP_ENV/pay-api/VALID_REDIRECT_URLS" +TRANSACTION_REPORT_DEFAULT_TOTAL="op://relationship/$APP_ENV/pay-api/TRANSACTION_REPORT_DEFAULT_TOTAL" +ROUTING_SLIP_DEFAULT_TOTAL="op://relationship/$APP_ENV/pay-api/ROUTING_SLIP_DEFAULT_TOTAL" +PAD_CONFIRMATION_PERIOD_IN_DAYS="op://relationship/$APP_ENV/pay-api/PAD_CONFIRMATION_PERIOD_IN_DAYS" +LEGISLATIVE_TIMEZONE="op://relationship/$APP_ENV/pay-api/LEGISLATIVE_TIMEZONE" +BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS="op://relationship/$APP_ENV/pay-api/BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS" +MASK_LEN="op://relationship/$APP_ENV/pay-api/MASK_LEN" +DISABLE_ACTIVITY_LOGS="op://relationship/$APP_ENV/pay-api/DISABLE_ACTIVITY_LOGS" +ACCOUNT_SECRET_KEY="op://relationship/$APP_ENV/pay-api/ACCOUNT_SECRET_KEY" +OUTSTANDING_TRANSACTION_DAYS="op://relationship/$APP_ENV/pay-api/OUTSTANDING_TRANSACTION_DAYS" +ALLOW_LEGACY_ROUTING_SLIPS="op://relationship/$APP_ENV/pay-api/ALLOW_LEGACY_ROUTING_SLIPS" \ No newline at end of file diff --git a/pay-api/devops/vaults.json b/pay-api/devops/vaults.json deleted file mode 100644 index 3bcf4805a..000000000 --- a/pay-api/devops/vaults.json +++ /dev/null @@ -1,49 +0,0 @@ -[ - { - "vault": "shared", - "application": [ - "api-endpoints", - "encryption-key" - ] - }, - { - "vault": "keycloak", - "application": [ - "jwt-base", - "sbc-auth-admin" - ] - }, - { - "vault": "gcp-queue", - "application": [ - "payment" - ] - }, - { - "vault": "payment-external-services", - "application": [ - "paybc", - "cfs" - ] - }, - { - "vault": "relationship", - "application": [ - "postgres-pay", - "pay-api", - "jwt" - ] - }, - { - "vault": "sentry", - "application": [ - "relationship-api" - ] - }, - { - "vault": "launchdarkly", - "application": [ - "pay" - ] - } -] diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 0ecc06a19..9de922871 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -29,26 +29,26 @@ load_dotenv(find_dotenv()) CONFIGURATION = { - 'development': 'pay_api.config.DevConfig', - 'testing': 'pay_api.config.TestConfig', - 'production': 'pay_api.config.ProdConfig', - 'default': 'pay_api.config.ProdConfig', - 'migration': 'pay_api.config.MigrationConfig', + "development": "pay_api.config.DevConfig", + "testing": "pay_api.config.TestConfig", + "production": "pay_api.config.ProdConfig", + "default": "pay_api.config.ProdConfig", + "migration": "pay_api.config.MigrationConfig", } -def get_named_config(config_name: str = 'production'): +def get_named_config(config_name: str = "production"): """Return the configuration object based on the name. :raise: KeyError: if an unknown configuration is requested """ - if config_name in ['production', 'staging', 'default']: + if config_name in ["production", "staging", "default"]: config = ProdConfig() - elif config_name == 'testing': + elif config_name == "testing": config = TestConfig() - elif config_name == 'development': + elif config_name == "development": config = DevConfig() - elif config_name == 'migration': + elif config_name == "migration": config = MigrationConfig() else: raise KeyError(f"Unknown configuration '{config_name}'") @@ -57,129 +57,157 @@ def get_named_config(config_name: str = 'production'): def _get_config(config_key: str, **kwargs): """Get the config from environment, and throw error if there are no default values and if the value is None.""" - if 'default' in kwargs: - value = os.getenv(config_key, kwargs.get('default')) + if "default" in kwargs: + value = os.getenv(config_key, kwargs.get("default")) else: value = os.getenv(config_key) return value -class _Config(): # pylint: disable=too-few-public-methods +class _Config: # pylint: disable=too-few-public-methods """Base class configuration that should set reasonable defaults for all the other configurations.""" PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) - SECRET_KEY = 'a secret' + SECRET_KEY = "a secret" SQLALCHEMY_TRACK_MODIFICATIONS = False - ALEMBIC_INI = 'migrations/alembic.ini' + ALEMBIC_INI = "migrations/alembic.ini" - PAY_LD_SDK_KEY = _get_config('PAY_LD_SDK_KEY') + PAY_LD_SDK_KEY = _get_config("PAY_LD_SDK_KEY") # POSTGRESQL - DB_USER = _get_config('DATABASE_USERNAME') - DB_PASSWORD = _get_config('DATABASE_PASSWORD') - DB_NAME = _get_config('DATABASE_NAME') - DB_HOST = _get_config('DATABASE_HOST') - DB_PORT = _get_config('DATABASE_PORT', default='5432') - SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' - SQLALCHEMY_ECHO = _get_config('SQLALCHEMY_ECHO', default='False').lower() == 'true' + DB_USER = _get_config("DATABASE_USERNAME") + DB_PASSWORD = _get_config("DATABASE_PASSWORD") + DB_NAME = _get_config("DATABASE_NAME") + DB_HOST = _get_config("DATABASE_HOST") + DB_PORT = _get_config("DATABASE_PORT", default="5432") + SQLALCHEMY_ECHO = _get_config("SQLALCHEMY_ECHO", default="False").lower() == "true" + + # POSTGRESQL + if DB_UNIX_SOCKET := os.getenv("DATABASE_UNIX_SOCKET", None): + SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432" + else: + SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}" # JWT_OIDC Settings - JWT_OIDC_WELL_KNOWN_CONFIG = _get_config('JWT_OIDC_WELL_KNOWN_CONFIG') - JWT_OIDC_ALGORITHMS = _get_config('JWT_OIDC_ALGORITHMS') - JWT_OIDC_JWKS_URI = _get_config('JWT_OIDC_JWKS_URI', default=None) - JWT_OIDC_ISSUER = _get_config('JWT_OIDC_ISSUER') - JWT_OIDC_AUDIENCE = _get_config('JWT_OIDC_AUDIENCE') - JWT_OIDC_CLIENT_SECRET = _get_config('JWT_OIDC_CLIENT_SECRET') - JWT_OIDC_CACHING_ENABLED = _get_config('JWT_OIDC_CACHING_ENABLED', default=False) - JWT_OIDC_JWKS_CACHE_TIMEOUT = int(_get_config('JWT_OIDC_JWKS_CACHE_TIMEOUT', default=300)) + JWT_OIDC_WELL_KNOWN_CONFIG = _get_config("JWT_OIDC_WELL_KNOWN_CONFIG") + JWT_OIDC_ALGORITHMS = _get_config("JWT_OIDC_ALGORITHMS") + JWT_OIDC_ISSUER = _get_config("JWT_OIDC_ISSUER") + JWT_OIDC_AUDIENCE = _get_config("JWT_OIDC_AUDIENCE") + JWT_OIDC_CLIENT_SECRET = _get_config("JWT_OIDC_CLIENT_SECRET") + JWT_OIDC_CACHING_ENABLED = _get_config("JWT_OIDC_CACHING_ENABLED", default=False) + JWT_OIDC_JWKS_CACHE_TIMEOUT = int( + _get_config("JWT_OIDC_JWKS_CACHE_TIMEOUT", default=300) + ) # CFS API Settings - CFS_BASE_URL = _get_config('CFS_BASE_URL') - CFS_CLIENT_ID = _get_config('CFS_CLIENT_ID') - CFS_CLIENT_SECRET = _get_config('CFS_CLIENT_SECRET') - PAYBC_PORTAL_URL = _get_config('PAYBC_PORTAL_URL') - CONNECT_TIMEOUT = int(_get_config('CONNECT_TIMEOUT', default=10)) - GENERATE_RANDOM_INVOICE_NUMBER = _get_config('CFS_GENERATE_RANDOM_INVOICE_NUMBER', default='False') - CFS_ACCOUNT_DESCRIPTION = _get_config('CFS_ACCOUNT_DESCRIPTION', default='BCR') - CFS_INVOICE_PREFIX = os.getenv('CFS_INVOICE_PREFIX', 'REG') - CFS_RECEIPT_PREFIX = os.getenv('CFS_RECEIPT_PREFIX', 'RCPT') - CFS_PARTY_PREFIX = os.getenv('CFS_PARTY_PREFIX', 'BCR-') + CFS_BASE_URL = _get_config("CFS_BASE_URL") + CFS_CLIENT_ID = _get_config("CFS_CLIENT_ID") + CFS_CLIENT_SECRET = _get_config("CFS_CLIENT_SECRET") + PAYBC_PORTAL_URL = _get_config("PAYBC_PORTAL_URL") + CONNECT_TIMEOUT = int(_get_config("CONNECT_TIMEOUT", default=10)) + GENERATE_RANDOM_INVOICE_NUMBER = _get_config( + "CFS_GENERATE_RANDOM_INVOICE_NUMBER", default="False" + ) + CFS_ACCOUNT_DESCRIPTION = _get_config("CFS_ACCOUNT_DESCRIPTION", default="BCR") + CFS_INVOICE_PREFIX = os.getenv("CFS_INVOICE_PREFIX", "REG") + CFS_RECEIPT_PREFIX = os.getenv("CFS_RECEIPT_PREFIX", "RCPT") + CFS_PARTY_PREFIX = os.getenv("CFS_PARTY_PREFIX", "BCR-") # EFT Config - EFT_INVOICE_PREFIX = os.getenv('EFT_INVOICE_PREFIX', 'REG') + EFT_INVOICE_PREFIX = os.getenv("EFT_INVOICE_PREFIX", "REG") # PAYBC Direct Pay Settings - PAYBC_DIRECT_PAY_REF_NUMBER = _get_config('PAYBC_DIRECT_PAY_REF_NUMBER') - PAYBC_DIRECT_PAY_API_KEY = _get_config('PAYBC_DIRECT_PAY_API_KEY') - PAYBC_DIRECT_PAY_PORTAL_URL = _get_config('PAYBC_DIRECT_PAY_PORTAL_URL') - PAYBC_DIRECT_PAY_BASE_URL = _get_config('PAYBC_DIRECT_PAY_BASE_URL') - PAYBC_DIRECT_PAY_CLIENT_ID = _get_config('PAYBC_DIRECT_PAY_CLIENT_ID') - PAYBC_DIRECT_PAY_CLIENT_SECRET = _get_config('PAYBC_DIRECT_PAY_CLIENT_SECRET') - PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL = _get_config('PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL') + PAYBC_DIRECT_PAY_REF_NUMBER = _get_config("PAYBC_DIRECT_PAY_REF_NUMBER") + PAYBC_DIRECT_PAY_API_KEY = _get_config("PAYBC_DIRECT_PAY_API_KEY") + PAYBC_DIRECT_PAY_PORTAL_URL = _get_config("PAYBC_DIRECT_PAY_PORTAL_URL") + PAYBC_DIRECT_PAY_BASE_URL = _get_config("PAYBC_DIRECT_PAY_BASE_URL") + PAYBC_DIRECT_PAY_CLIENT_ID = _get_config("PAYBC_DIRECT_PAY_CLIENT_ID") + PAYBC_DIRECT_PAY_CLIENT_SECRET = _get_config("PAYBC_DIRECT_PAY_CLIENT_SECRET") + PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL = _get_config( + "PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL" + ) # GCP PubSub - AUDIENCE = os.getenv('AUDIENCE', None) - GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) - PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) - ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) - EVENT_LISTENER_TOPIC = os.getenv('EVENT_LISTENER_TOPIC', None) - NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', None) - BUSINESS_PAY_TOPIC = os.getenv('BUSINESS_PAY_TOPIC', None) - - # Auth API Endpoint - AUTH_API_ENDPOINT = f'{_get_config("AUTH_API_URL")}/' - - # REPORT API Settings - REPORT_API_BASE_URL = f'{_get_config("REPORT_API_URL")}/reports' - - # BCOL Service - BCOL_API_ENDPOINT = _get_config('BCOL_API_URL') + AUDIENCE = os.getenv("AUDIENCE", None) + GCP_AUTH_KEY = os.getenv("GCP_AUTH_KEY", None) + PUBLISHER_AUDIENCE = os.getenv("PUBLISHER_AUDIENCE", None) + ACCOUNT_MAILER_TOPIC = os.getenv("ACCOUNT_MAILER_TOPIC", None) + EVENT_LISTENER_TOPIC = os.getenv("EVENT_LISTENER_TOPIC", None) + NAMEX_PAY_TOPIC = os.getenv("NAMEX_PAY_TOPIC", None) + BUSINESS_PAY_TOPIC = os.getenv("BUSINESS_PAY_TOPIC", None) + + # API Endpoints + AUTH_API_URL = os.getenv("AUTH_API_URL", "") + AUTH_API_VERSION = os.getenv("AUTH_API_VERSION", "") + BCOL_API_URL = os.getenv("BCOL_API_URL", "") + BCOL_API_VERSION = os.getenv("BCOL_API_VERSION", "") + REPORT_API_URL = os.getenv("REPORT_API_URL", "") + REPORT_API_VERSION = os.getenv("REPORT_API_VERSION", "") + + AUTH_API_ENDPOINT = f"{AUTH_API_URL + AUTH_API_VERSION}/" + REPORT_API_BASE_URL = f"{REPORT_API_URL + REPORT_API_VERSION}/reports" + BCOL_API_ENDPOINT = f"{BCOL_API_URL + BCOL_API_VERSION}/" # Sentry Config - SENTRY_ENABLE = _get_config('SENTRY_ENABLE', default=False) - SENTRY_DSN = _get_config('SENTRY_DSN', default=None) + SENTRY_ENABLE = _get_config("SENTRY_ENABLE", default=False) + SENTRY_DSN = _get_config("SENTRY_DSN", default=None) # Disable valid redirect URLs - for DEV only - DISABLE_VALID_REDIRECT_URLS = _get_config('DISABLE_VALID_REDIRECT_URLS', default='False').lower() == 'true' + DISABLE_VALID_REDIRECT_URLS = ( + _get_config("DISABLE_VALID_REDIRECT_URLS", default="False").lower() == "true" + ) # Valid Payment redirect URLs - VALID_REDIRECT_URLS = [(val.strip() if val != '' else None) - for val in _get_config('VALID_REDIRECT_URLS', default='').split(',')] + VALID_REDIRECT_URLS = [ + (val.strip() if val != "" else None) + for val in _get_config("VALID_REDIRECT_URLS", default="").split(",") + ] # Service account details - KEYCLOAK_SERVICE_ACCOUNT_ID = _get_config('SBC_AUTH_ADMIN_CLIENT_ID') - KEYCLOAK_SERVICE_ACCOUNT_SECRET = _get_config('SBC_AUTH_ADMIN_CLIENT_SECRET') + KEYCLOAK_SERVICE_ACCOUNT_ID = _get_config("SBC_AUTH_ADMIN_CLIENT_ID") + KEYCLOAK_SERVICE_ACCOUNT_SECRET = _get_config("SBC_AUTH_ADMIN_CLIENT_SECRET") # Default number of transactions to be returned for transaction reporting - TRANSACTION_REPORT_DEFAULT_TOTAL = int(_get_config('TRANSACTION_REPORT_DEFAULT_TOTAL', default=50)) + TRANSACTION_REPORT_DEFAULT_TOTAL = int( + _get_config("TRANSACTION_REPORT_DEFAULT_TOTAL", default=50) + ) # Default number of routing slips to be returned for routing slip search - ROUTING_SLIP_DEFAULT_TOTAL = int(_get_config('ROUTING_SLIP_DEFAULT_TOTAL', default=50)) + ROUTING_SLIP_DEFAULT_TOTAL = int( + _get_config("ROUTING_SLIP_DEFAULT_TOTAL", default=50) + ) - PAD_CONFIRMATION_PERIOD_IN_DAYS = int(_get_config('PAD_CONFIRMATION_PERIOD_IN_DAYS', default=3)) + PAD_CONFIRMATION_PERIOD_IN_DAYS = int( + _get_config("PAD_CONFIRMATION_PERIOD_IN_DAYS", default=3) + ) # legislative timezone for future effective dating - LEGISLATIVE_TIMEZONE = os.getenv('LEGISLATIVE_TIMEZONE', 'America/Vancouver') + LEGISLATIVE_TIMEZONE = os.getenv("LEGISLATIVE_TIMEZONE", "America/Vancouver") # BCOL user name for Service account payments - BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS = os.getenv('BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS', - 'BCROS SERVICE ACCOUNT') + BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS = os.getenv( + "BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS", "BCROS SERVICE ACCOUNT" + ) # The number of characters which can be exposed to admins for a bank account number - MASK_LEN = int(_get_config('MASK_LEN', default=3)) + MASK_LEN = int(_get_config("MASK_LEN", default=3)) # Config value to disable activity logs - DISABLE_ACTIVITY_LOGS = os.getenv('DISABLE_ACTIVITY_LOGS', 'False').lower() == 'true' + DISABLE_ACTIVITY_LOGS = ( + os.getenv("DISABLE_ACTIVITY_LOGS", "False").lower() == "true" + ) # Secret key for encrypting bank account - ACCOUNT_SECRET_KEY = os.getenv('ACCOUNT_SECRET_KEY') + ACCOUNT_SECRET_KEY = os.getenv("ACCOUNT_SECRET_KEY") - OUTSTANDING_TRANSACTION_DAYS = int(os.getenv('OUTSTANDING_TRANSACTION_DAYS', '10')) + OUTSTANDING_TRANSACTION_DAYS = int(os.getenv("OUTSTANDING_TRANSACTION_DAYS", "10")) - ALLOW_LEGACY_ROUTING_SLIPS = os.getenv('ALLOW_LEGACY_ROUTING_SLIPS', 'True').lower() == 'true' + ALLOW_LEGACY_ROUTING_SLIPS = ( + os.getenv("ALLOW_LEGACY_ROUTING_SLIPS", "True").lower() == "true" + ) TESTING = False DEBUG = True @@ -198,61 +226,61 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DEBUG = True TESTING = True - USE_TEST_KEYCLOAK_DOCKER = _get_config('USE_TEST_KEYCLOAK_DOCKER', default=None) - USE_DOCKER_MOCK = _get_config('USE_DOCKER_MOCK', default=None) + USE_TEST_KEYCLOAK_DOCKER = _get_config("USE_TEST_KEYCLOAK_DOCKER", default=None) + USE_DOCKER_MOCK = _get_config("USE_DOCKER_MOCK", default=None) # POSTGRESQL - DB_USER = _get_config('DATABASE_TEST_USERNAME', default='postgres') - DB_PASSWORD = _get_config('DATABASE_TEST_PASSWORD', default='postgres') - DB_NAME = _get_config('DATABASE_TEST_NAME', default='paytestdb') - DB_HOST = _get_config('DATABASE_TEST_HOST', default='localhost') - DB_PORT = _get_config('DATABASE_TEST_PORT', default='5432') + DB_USER = _get_config("DATABASE_TEST_USERNAME", default="postgres") + DB_PASSWORD = _get_config("DATABASE_TEST_PASSWORD", default="postgres") + DB_NAME = _get_config("DATABASE_TEST_NAME", default="paytestdb") + DB_HOST = _get_config("DATABASE_TEST_HOST", default="localhost") + DB_PORT = _get_config("DATABASE_TEST_PORT", default="5432") SQLALCHEMY_DATABASE_URI = _get_config( - 'DATABASE_TEST_URL', - default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + "DATABASE_TEST_URL", + default=f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}", ) JWT_OIDC_TEST_MODE = True # JWT_OIDC_ISSUER = _get_config('JWT_OIDC_TEST_ISSUER') - JWT_OIDC_TEST_AUDIENCE = _get_config('JWT_OIDC_TEST_AUDIENCE') - JWT_OIDC_TEST_CLIENT_SECRET = _get_config('JWT_OIDC_TEST_CLIENT_SECRET') - JWT_OIDC_TEST_ISSUER = _get_config('JWT_OIDC_TEST_ISSUER') - JWT_OIDC_WELL_KNOWN_CONFIG = _get_config('JWT_OIDC_WELL_KNOWN_CONFIG') - JWT_OIDC_TEST_ALGORITHMS = _get_config('JWT_OIDC_TEST_ALGORITHMS') - JWT_OIDC_TEST_JWKS_URI = _get_config('JWT_OIDC_TEST_JWKS_URI', default=None) + JWT_OIDC_TEST_AUDIENCE = _get_config("JWT_OIDC_TEST_AUDIENCE") + JWT_OIDC_TEST_CLIENT_SECRET = _get_config("JWT_OIDC_TEST_CLIENT_SECRET") + JWT_OIDC_TEST_ISSUER = _get_config("JWT_OIDC_TEST_ISSUER") + JWT_OIDC_WELL_KNOWN_CONFIG = _get_config("JWT_OIDC_WELL_KNOWN_CONFIG") + JWT_OIDC_TEST_ALGORITHMS = _get_config("JWT_OIDC_TEST_ALGORITHMS") + JWT_OIDC_TEST_JWKS_URI = _get_config("JWT_OIDC_TEST_JWKS_URI", default=None) JWT_OIDC_TEST_KEYS = { - 'keys': [ + "keys": [ { - 'kid': 'sbc-auth-web', - 'kty': 'RSA', - 'alg': 'RS256', - 'use': 'sig', - 'n': 'AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-' - 'TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR', - 'e': 'AQAB' + "kid": "sbc-auth-web", + "kty": "RSA", + "alg": "RS256", + "use": "sig", + "n": "AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-" + "TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR", + "e": "AQAB", } ] } JWT_OIDC_TEST_PRIVATE_KEY_JWKS = { - 'keys': [ + "keys": [ { - 'kid': 'sbc-auth-web', - 'kty': 'RSA', - 'alg': 'RS256', - 'use': 'sig', - 'n': 'AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-' - 'TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR', - 'e': 'AQAB', - 'd': 'C0G3QGI6OQ6tvbCNYGCqq043YI_8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhskURaDwk4-' - '8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh_' - 'xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0', - 'p': 'APXcusFMQNHjh6KVD_hOUIw87lvK13WkDEeeuqAydai9Ig9JKEAAfV94W6Aftka7tGgE7ulg1vo3eJoLWJ1zvKM', - 'q': 'AOjX3OnPJnk0ZFUQBwhduCweRi37I6DAdLTnhDvcPTrrNWuKPg9uGwHjzFCJgKd8KBaDQ0X1rZTZLTqi3peT43s', - 'dp': 'AN9kBoA5o6_Rl9zeqdsIdWFmv4DB5lEqlEnC7HlAP-3oo3jWFO9KQqArQL1V8w2D4aCd0uJULiC9pCP7aTHvBhc', - 'dq': 'ANtbSY6njfpPploQsF9sU26U0s7MsuLljM1E8uml8bVJE1mNsiu9MgpUvg39jEu9BtM2tDD7Y51AAIEmIQex1nM', - 'qi': 'XLE5O360x-MhsdFXx8Vwz4304-MJg-oGSJXCK_ZWYOB_FGXFRTfebxCsSYi0YwJo-oNu96bvZCuMplzRI1liZw' + "kid": "sbc-auth-web", + "kty": "RSA", + "alg": "RS256", + "use": "sig", + "n": "AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-" + "TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR", + "e": "AQAB", + "d": "C0G3QGI6OQ6tvbCNYGCqq043YI_8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhskURaDwk4-" + "8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh_" + "xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0", + "p": "APXcusFMQNHjh6KVD_hOUIw87lvK13WkDEeeuqAydai9Ig9JKEAAfV94W6Aftka7tGgE7ulg1vo3eJoLWJ1zvKM", + "q": "AOjX3OnPJnk0ZFUQBwhduCweRi37I6DAdLTnhDvcPTrrNWuKPg9uGwHjzFCJgKd8KBaDQ0X1rZTZLTqi3peT43s", + "dp": "AN9kBoA5o6_Rl9zeqdsIdWFmv4DB5lEqlEnC7HlAP-3oo3jWFO9KQqArQL1V8w2D4aCd0uJULiC9pCP7aTHvBhc", + "dq": "ANtbSY6njfpPploQsF9sU26U0s7MsuLljM1E8uml8bVJE1mNsiu9MgpUvg39jEu9BtM2tDD7Y51AAIEmIQex1nM", + "qi": "XLE5O360x-MhsdFXx8Vwz4304-MJg-oGSJXCK_ZWYOB_FGXFRTfebxCsSYi0YwJo-oNu96bvZCuMplzRI1liZw", } ] } @@ -273,60 +301,62 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods 4H8UZcVFN95vEKxJiLRjAmj6g273pu9kK4ymXNEjWWJn -----END RSA PRIVATE KEY-----""" - CFS_BASE_URL = 'http://localhost:8080/paybc-api' - CFS_CLIENT_ID = 'TEST' - CFS_CLIENT_SECRET = 'TEST' - PAYBC_PORTAL_URL = 'https://paydev.gov.bc.ca/public/directpay' + CFS_BASE_URL = "http://localhost:8080/paybc-api" + CFS_CLIENT_ID = "TEST" + CFS_CLIENT_SECRET = "TEST" + PAYBC_PORTAL_URL = "https://paydev.gov.bc.ca/public/directpay" - SERVER_NAME = 'auth-web.dev.com' + SERVER_NAME = "auth-web.dev.com" - REPORT_API_BASE_URL = 'http://localhost:8080/reports-api/api/v1/reports' + REPORT_API_BASE_URL = "http://localhost:8080/reports-api/api/v1/reports" - AUTH_API_ENDPOINT = 'http://localhost:8080/auth-api/' + AUTH_API_ENDPOINT = "http://localhost:8080/auth-api/" - BCOL_API_ENDPOINT = 'http://localhost:8080/bcol-api' + BCOL_API_ENDPOINT = "http://localhost:8080/bcol-api" - VALID_REDIRECT_URLS = ['http://localhost:8080/*'] + VALID_REDIRECT_URLS = ["http://localhost:8080/*"] TRANSACTION_REPORT_DEFAULT_TOTAL = 10 - PAYBC_DIRECT_PAY_API_KEY = 'TESTKEYSECRET' - PAYBC_DIRECT_PAY_REF_NUMBER = 'REF1234' - PAYBC_DIRECT_PAY_PORTAL_URL = 'https://paydev.gov.bc.ca/public/directsale' - PAYBC_DIRECT_PAY_BASE_URL = 'http://localhost:8080/paybc-api' + PAYBC_DIRECT_PAY_API_KEY = "TESTKEYSECRET" + PAYBC_DIRECT_PAY_REF_NUMBER = "REF1234" + PAYBC_DIRECT_PAY_PORTAL_URL = "https://paydev.gov.bc.ca/public/directsale" + PAYBC_DIRECT_PAY_BASE_URL = "http://localhost:8080/paybc-api" PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL = PAYBC_DIRECT_PAY_BASE_URL - PAYBC_DIRECT_PAY_CLIENT_ID = 'TEST' - PAYBC_DIRECT_PAY_CLIENT_SECRET = 'TEST' + PAYBC_DIRECT_PAY_CLIENT_ID = "TEST" + PAYBC_DIRECT_PAY_CLIENT_SECRET = "TEST" PAD_CONFIRMATION_PERIOD_IN_DAYS = 3 # Secret key for encrypting bank account - ACCOUNT_SECRET_KEY = 'mysecretkeyforbank' + ACCOUNT_SECRET_KEY = "mysecretkeyforbank" class ProdConfig(_Config): # pylint: disable=too-few-public-methods """Production environment configuration.""" - SECRET_KEY = _get_config('SECRET_KEY', default=None) + SECRET_KEY = _get_config("SECRET_KEY", default=None) if not SECRET_KEY: SECRET_KEY = os.urandom(24) - print('WARNING: SECRET_KEY being set as a one-shot', file=sys.stderr) + print("WARNING: SECRET_KEY being set as a one-shot", file=sys.stderr) TESTING = False DEBUG = False -class MigrationConfig(): # pylint: disable=too-few-public-methods +class MigrationConfig: # pylint: disable=too-few-public-methods """Config for db migration.""" TESTING = False DEBUG = True # POSTGRESQL - DB_USER = _get_config('DATABASE_USERNAME') - DB_PASSWORD = _get_config('DATABASE_PASSWORD') - DB_NAME = _get_config('DATABASE_NAME') - DB_HOST = _get_config('DATABASE_HOST') - DB_PORT = _get_config('DATABASE_PORT', default='5432') - SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + DB_USER = _get_config("DATABASE_USERNAME") + DB_PASSWORD = _get_config("DATABASE_PASSWORD") + DB_NAME = _get_config("DATABASE_NAME") + DB_HOST = _get_config("DATABASE_HOST") + DB_PORT = _get_config("DATABASE_PORT", default="5432") + SQLALCHEMY_DATABASE_URI = ( + f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}" + ) SQLALCHEMY_TRACK_MODIFICATIONS = False From 09dd93b8c71d1f7598617140ae961095a20d3756 Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Wed, 13 Mar 2024 22:41:28 -0700 Subject: [PATCH 18/87] Rollback the auto formatting. --- pay-api/src/pay_api/config.py | 296 +++++++++++++++++----------------- 1 file changed, 148 insertions(+), 148 deletions(-) diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 9de922871..ad785498f 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -1,13 +1,13 @@ # Copyright © 2019 Province of British Columbia # -# Licensed under the Apache License, Version 2.0 (the "License"); +# Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, +# distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. @@ -29,36 +29,36 @@ load_dotenv(find_dotenv()) CONFIGURATION = { - "development": "pay_api.config.DevConfig", - "testing": "pay_api.config.TestConfig", - "production": "pay_api.config.ProdConfig", - "default": "pay_api.config.ProdConfig", - "migration": "pay_api.config.MigrationConfig", + 'development': 'pay_api.config.DevConfig', + 'testing': 'pay_api.config.TestConfig', + 'production': 'pay_api.config.ProdConfig', + 'default': 'pay_api.config.ProdConfig', + 'migration': 'pay_api.config.MigrationConfig', } -def get_named_config(config_name: str = "production"): +def get_named_config(config_name: str = 'production'): """Return the configuration object based on the name. :raise: KeyError: if an unknown configuration is requested """ - if config_name in ["production", "staging", "default"]: + if config_name in ['production', 'staging', 'default']: config = ProdConfig() - elif config_name == "testing": + elif config_name == 'testing': config = TestConfig() - elif config_name == "development": + elif config_name == 'development': config = DevConfig() - elif config_name == "migration": + elif config_name == 'migration': config = MigrationConfig() else: - raise KeyError(f"Unknown configuration '{config_name}'") + raise KeyError(f'Unknown configuration "{config_name}"') return config def _get_config(config_key: str, **kwargs): """Get the config from environment, and throw error if there are no default values and if the value is None.""" - if "default" in kwargs: - value = os.getenv(config_key, kwargs.get("default")) + if 'default' in kwargs: + value = os.getenv(config_key, kwargs.get('default')) else: value = os.getenv(config_key) return value @@ -69,144 +69,144 @@ class _Config: # pylint: disable=too-few-public-methods PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) - SECRET_KEY = "a secret" + SECRET_KEY = 'a secret' SQLALCHEMY_TRACK_MODIFICATIONS = False - ALEMBIC_INI = "migrations/alembic.ini" + ALEMBIC_INI = 'migrations/alembic.ini' - PAY_LD_SDK_KEY = _get_config("PAY_LD_SDK_KEY") + PAY_LD_SDK_KEY = _get_config('PAY_LD_SDK_KEY') # POSTGRESQL - DB_USER = _get_config("DATABASE_USERNAME") - DB_PASSWORD = _get_config("DATABASE_PASSWORD") - DB_NAME = _get_config("DATABASE_NAME") - DB_HOST = _get_config("DATABASE_HOST") - DB_PORT = _get_config("DATABASE_PORT", default="5432") - SQLALCHEMY_ECHO = _get_config("SQLALCHEMY_ECHO", default="False").lower() == "true" + DB_USER = _get_config('DATABASE_USERNAME') + DB_PASSWORD = _get_config('DATABASE_PASSWORD') + DB_NAME = _get_config('DATABASE_NAME') + DB_HOST = _get_config('DATABASE_HOST') + DB_PORT = _get_config('DATABASE_PORT', default='5432') + SQLALCHEMY_ECHO = _get_config('SQLALCHEMY_ECHO', default='False').lower() == 'true' # POSTGRESQL - if DB_UNIX_SOCKET := os.getenv("DATABASE_UNIX_SOCKET", None): - SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432" + if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): + SQLALCHEMY_DATABASE_URI = f'postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' else: - SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}" + SQLALCHEMY_DATABASE_URI = f'postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}' # JWT_OIDC Settings - JWT_OIDC_WELL_KNOWN_CONFIG = _get_config("JWT_OIDC_WELL_KNOWN_CONFIG") - JWT_OIDC_ALGORITHMS = _get_config("JWT_OIDC_ALGORITHMS") - JWT_OIDC_ISSUER = _get_config("JWT_OIDC_ISSUER") - JWT_OIDC_AUDIENCE = _get_config("JWT_OIDC_AUDIENCE") - JWT_OIDC_CLIENT_SECRET = _get_config("JWT_OIDC_CLIENT_SECRET") - JWT_OIDC_CACHING_ENABLED = _get_config("JWT_OIDC_CACHING_ENABLED", default=False) + JWT_OIDC_WELL_KNOWN_CONFIG = _get_config('JWT_OIDC_WELL_KNOWN_CONFIG') + JWT_OIDC_ALGORITHMS = _get_config('JWT_OIDC_ALGORITHMS') + JWT_OIDC_ISSUER = _get_config('JWT_OIDC_ISSUER') + JWT_OIDC_AUDIENCE = _get_config('JWT_OIDC_AUDIENCE') + JWT_OIDC_CLIENT_SECRET = _get_config('JWT_OIDC_CLIENT_SECRET') + JWT_OIDC_CACHING_ENABLED = _get_config('JWT_OIDC_CACHING_ENABLED', default=False) JWT_OIDC_JWKS_CACHE_TIMEOUT = int( - _get_config("JWT_OIDC_JWKS_CACHE_TIMEOUT", default=300) + _get_config('JWT_OIDC_JWKS_CACHE_TIMEOUT', default=300) ) # CFS API Settings - CFS_BASE_URL = _get_config("CFS_BASE_URL") - CFS_CLIENT_ID = _get_config("CFS_CLIENT_ID") - CFS_CLIENT_SECRET = _get_config("CFS_CLIENT_SECRET") - PAYBC_PORTAL_URL = _get_config("PAYBC_PORTAL_URL") - CONNECT_TIMEOUT = int(_get_config("CONNECT_TIMEOUT", default=10)) + CFS_BASE_URL = _get_config('CFS_BASE_URL') + CFS_CLIENT_ID = _get_config('CFS_CLIENT_ID') + CFS_CLIENT_SECRET = _get_config('CFS_CLIENT_SECRET') + PAYBC_PORTAL_URL = _get_config('PAYBC_PORTAL_URL') + CONNECT_TIMEOUT = int(_get_config('CONNECT_TIMEOUT', default=10)) GENERATE_RANDOM_INVOICE_NUMBER = _get_config( - "CFS_GENERATE_RANDOM_INVOICE_NUMBER", default="False" + 'CFS_GENERATE_RANDOM_INVOICE_NUMBER', default='False' ) - CFS_ACCOUNT_DESCRIPTION = _get_config("CFS_ACCOUNT_DESCRIPTION", default="BCR") - CFS_INVOICE_PREFIX = os.getenv("CFS_INVOICE_PREFIX", "REG") - CFS_RECEIPT_PREFIX = os.getenv("CFS_RECEIPT_PREFIX", "RCPT") - CFS_PARTY_PREFIX = os.getenv("CFS_PARTY_PREFIX", "BCR-") + CFS_ACCOUNT_DESCRIPTION = _get_config('CFS_ACCOUNT_DESCRIPTION', default='BCR') + CFS_INVOICE_PREFIX = os.getenv('CFS_INVOICE_PREFIX', 'REG') + CFS_RECEIPT_PREFIX = os.getenv('CFS_RECEIPT_PREFIX', 'RCPT') + CFS_PARTY_PREFIX = os.getenv('CFS_PARTY_PREFIX', 'BCR-') # EFT Config - EFT_INVOICE_PREFIX = os.getenv("EFT_INVOICE_PREFIX", "REG") + EFT_INVOICE_PREFIX = os.getenv('EFT_INVOICE_PREFIX', 'REG') # PAYBC Direct Pay Settings - PAYBC_DIRECT_PAY_REF_NUMBER = _get_config("PAYBC_DIRECT_PAY_REF_NUMBER") - PAYBC_DIRECT_PAY_API_KEY = _get_config("PAYBC_DIRECT_PAY_API_KEY") - PAYBC_DIRECT_PAY_PORTAL_URL = _get_config("PAYBC_DIRECT_PAY_PORTAL_URL") - PAYBC_DIRECT_PAY_BASE_URL = _get_config("PAYBC_DIRECT_PAY_BASE_URL") - PAYBC_DIRECT_PAY_CLIENT_ID = _get_config("PAYBC_DIRECT_PAY_CLIENT_ID") - PAYBC_DIRECT_PAY_CLIENT_SECRET = _get_config("PAYBC_DIRECT_PAY_CLIENT_SECRET") + PAYBC_DIRECT_PAY_REF_NUMBER = _get_config('PAYBC_DIRECT_PAY_REF_NUMBER') + PAYBC_DIRECT_PAY_API_KEY = _get_config('PAYBC_DIRECT_PAY_API_KEY') + PAYBC_DIRECT_PAY_PORTAL_URL = _get_config('PAYBC_DIRECT_PAY_PORTAL_URL') + PAYBC_DIRECT_PAY_BASE_URL = _get_config('PAYBC_DIRECT_PAY_BASE_URL') + PAYBC_DIRECT_PAY_CLIENT_ID = _get_config('PAYBC_DIRECT_PAY_CLIENT_ID') + PAYBC_DIRECT_PAY_CLIENT_SECRET = _get_config('PAYBC_DIRECT_PAY_CLIENT_SECRET') PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL = _get_config( - "PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL" + 'PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL' ) # GCP PubSub - AUDIENCE = os.getenv("AUDIENCE", None) - GCP_AUTH_KEY = os.getenv("GCP_AUTH_KEY", None) - PUBLISHER_AUDIENCE = os.getenv("PUBLISHER_AUDIENCE", None) - ACCOUNT_MAILER_TOPIC = os.getenv("ACCOUNT_MAILER_TOPIC", None) - EVENT_LISTENER_TOPIC = os.getenv("EVENT_LISTENER_TOPIC", None) - NAMEX_PAY_TOPIC = os.getenv("NAMEX_PAY_TOPIC", None) - BUSINESS_PAY_TOPIC = os.getenv("BUSINESS_PAY_TOPIC", None) + AUDIENCE = os.getenv('AUDIENCE', None) + GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) + PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) + EVENT_LISTENER_TOPIC = os.getenv('EVENT_LISTENER_TOPIC', None) + NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', None) + BUSINESS_PAY_TOPIC = os.getenv('BUSINESS_PAY_TOPIC', None) # API Endpoints - AUTH_API_URL = os.getenv("AUTH_API_URL", "") - AUTH_API_VERSION = os.getenv("AUTH_API_VERSION", "") - BCOL_API_URL = os.getenv("BCOL_API_URL", "") - BCOL_API_VERSION = os.getenv("BCOL_API_VERSION", "") - REPORT_API_URL = os.getenv("REPORT_API_URL", "") - REPORT_API_VERSION = os.getenv("REPORT_API_VERSION", "") + AUTH_API_URL = os.getenv('AUTH_API_URL', '') + AUTH_API_VERSION = os.getenv('AUTH_API_VERSION', '') + BCOL_API_URL = os.getenv('BCOL_API_URL', '') + BCOL_API_VERSION = os.getenv('BCOL_API_VERSION', '') + REPORT_API_URL = os.getenv('REPORT_API_URL', '') + REPORT_API_VERSION = os.getenv('REPORT_API_VERSION', '') - AUTH_API_ENDPOINT = f"{AUTH_API_URL + AUTH_API_VERSION}/" - REPORT_API_BASE_URL = f"{REPORT_API_URL + REPORT_API_VERSION}/reports" - BCOL_API_ENDPOINT = f"{BCOL_API_URL + BCOL_API_VERSION}/" + AUTH_API_ENDPOINT = f'{AUTH_API_URL + AUTH_API_VERSION}/' + REPORT_API_BASE_URL = f'{REPORT_API_URL + REPORT_API_VERSION}/reports' + BCOL_API_ENDPOINT = f'{BCOL_API_URL + BCOL_API_VERSION}/' # Sentry Config - SENTRY_ENABLE = _get_config("SENTRY_ENABLE", default=False) - SENTRY_DSN = _get_config("SENTRY_DSN", default=None) + SENTRY_ENABLE = _get_config('SENTRY_ENABLE', default=False) + SENTRY_DSN = _get_config('SENTRY_DSN', default=None) # Disable valid redirect URLs - for DEV only DISABLE_VALID_REDIRECT_URLS = ( - _get_config("DISABLE_VALID_REDIRECT_URLS", default="False").lower() == "true" + _get_config('DISABLE_VALID_REDIRECT_URLS', default='False').lower() == 'true' ) # Valid Payment redirect URLs VALID_REDIRECT_URLS = [ - (val.strip() if val != "" else None) - for val in _get_config("VALID_REDIRECT_URLS", default="").split(",") + (val.strip() if val != '' else None) + for val in _get_config('VALID_REDIRECT_URLS', default='').split(',') ] # Service account details - KEYCLOAK_SERVICE_ACCOUNT_ID = _get_config("SBC_AUTH_ADMIN_CLIENT_ID") - KEYCLOAK_SERVICE_ACCOUNT_SECRET = _get_config("SBC_AUTH_ADMIN_CLIENT_SECRET") + KEYCLOAK_SERVICE_ACCOUNT_ID = _get_config('SBC_AUTH_ADMIN_CLIENT_ID') + KEYCLOAK_SERVICE_ACCOUNT_SECRET = _get_config('SBC_AUTH_ADMIN_CLIENT_SECRET') # Default number of transactions to be returned for transaction reporting TRANSACTION_REPORT_DEFAULT_TOTAL = int( - _get_config("TRANSACTION_REPORT_DEFAULT_TOTAL", default=50) + _get_config('TRANSACTION_REPORT_DEFAULT_TOTAL', default=50) ) # Default number of routing slips to be returned for routing slip search ROUTING_SLIP_DEFAULT_TOTAL = int( - _get_config("ROUTING_SLIP_DEFAULT_TOTAL", default=50) + _get_config('ROUTING_SLIP_DEFAULT_TOTAL', default=50) ) PAD_CONFIRMATION_PERIOD_IN_DAYS = int( - _get_config("PAD_CONFIRMATION_PERIOD_IN_DAYS", default=3) + _get_config('PAD_CONFIRMATION_PERIOD_IN_DAYS', default=3) ) # legislative timezone for future effective dating - LEGISLATIVE_TIMEZONE = os.getenv("LEGISLATIVE_TIMEZONE", "America/Vancouver") + LEGISLATIVE_TIMEZONE = os.getenv('LEGISLATIVE_TIMEZONE', 'America/Vancouver') # BCOL user name for Service account payments BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS = os.getenv( - "BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS", "BCROS SERVICE ACCOUNT" + 'BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS', 'BCROS SERVICE ACCOUNT' ) # The number of characters which can be exposed to admins for a bank account number - MASK_LEN = int(_get_config("MASK_LEN", default=3)) + MASK_LEN = int(_get_config('MASK_LEN', default=3)) # Config value to disable activity logs DISABLE_ACTIVITY_LOGS = ( - os.getenv("DISABLE_ACTIVITY_LOGS", "False").lower() == "true" + os.getenv('DISABLE_ACTIVITY_LOGS', 'False').lower() == 'true' ) # Secret key for encrypting bank account - ACCOUNT_SECRET_KEY = os.getenv("ACCOUNT_SECRET_KEY") + ACCOUNT_SECRET_KEY = os.getenv('ACCOUNT_SECRET_KEY') - OUTSTANDING_TRANSACTION_DAYS = int(os.getenv("OUTSTANDING_TRANSACTION_DAYS", "10")) + OUTSTANDING_TRANSACTION_DAYS = int(os.getenv('OUTSTANDING_TRANSACTION_DAYS', '10')) ALLOW_LEGACY_ROUTING_SLIPS = ( - os.getenv("ALLOW_LEGACY_ROUTING_SLIPS", "True").lower() == "true" + os.getenv('ALLOW_LEGACY_ROUTING_SLIPS', 'True').lower() == 'true' ) TESTING = False @@ -226,61 +226,61 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DEBUG = True TESTING = True - USE_TEST_KEYCLOAK_DOCKER = _get_config("USE_TEST_KEYCLOAK_DOCKER", default=None) - USE_DOCKER_MOCK = _get_config("USE_DOCKER_MOCK", default=None) + USE_TEST_KEYCLOAK_DOCKER = _get_config('USE_TEST_KEYCLOAK_DOCKER', default=None) + USE_DOCKER_MOCK = _get_config('USE_DOCKER_MOCK', default=None) # POSTGRESQL - DB_USER = _get_config("DATABASE_TEST_USERNAME", default="postgres") - DB_PASSWORD = _get_config("DATABASE_TEST_PASSWORD", default="postgres") - DB_NAME = _get_config("DATABASE_TEST_NAME", default="paytestdb") - DB_HOST = _get_config("DATABASE_TEST_HOST", default="localhost") - DB_PORT = _get_config("DATABASE_TEST_PORT", default="5432") + DB_USER = _get_config('DATABASE_TEST_USERNAME', default='postgres') + DB_PASSWORD = _get_config('DATABASE_TEST_PASSWORD', default='postgres') + DB_NAME = _get_config('DATABASE_TEST_NAME', default='paytestdb') + DB_HOST = _get_config('DATABASE_TEST_HOST', default='localhost') + DB_PORT = _get_config('DATABASE_TEST_PORT', default='5432') SQLALCHEMY_DATABASE_URI = _get_config( - "DATABASE_TEST_URL", - default=f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}", + 'DATABASE_TEST_URL', + default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}', ) JWT_OIDC_TEST_MODE = True # JWT_OIDC_ISSUER = _get_config('JWT_OIDC_TEST_ISSUER') - JWT_OIDC_TEST_AUDIENCE = _get_config("JWT_OIDC_TEST_AUDIENCE") - JWT_OIDC_TEST_CLIENT_SECRET = _get_config("JWT_OIDC_TEST_CLIENT_SECRET") - JWT_OIDC_TEST_ISSUER = _get_config("JWT_OIDC_TEST_ISSUER") - JWT_OIDC_WELL_KNOWN_CONFIG = _get_config("JWT_OIDC_WELL_KNOWN_CONFIG") - JWT_OIDC_TEST_ALGORITHMS = _get_config("JWT_OIDC_TEST_ALGORITHMS") - JWT_OIDC_TEST_JWKS_URI = _get_config("JWT_OIDC_TEST_JWKS_URI", default=None) + JWT_OIDC_TEST_AUDIENCE = _get_config('JWT_OIDC_TEST_AUDIENCE') + JWT_OIDC_TEST_CLIENT_SECRET = _get_config('JWT_OIDC_TEST_CLIENT_SECRET') + JWT_OIDC_TEST_ISSUER = _get_config('JWT_OIDC_TEST_ISSUER') + JWT_OIDC_WELL_KNOWN_CONFIG = _get_config('JWT_OIDC_WELL_KNOWN_CONFIG') + JWT_OIDC_TEST_ALGORITHMS = _get_config('JWT_OIDC_TEST_ALGORITHMS') + JWT_OIDC_TEST_JWKS_URI = _get_config('JWT_OIDC_TEST_JWKS_URI', default=None) JWT_OIDC_TEST_KEYS = { - "keys": [ + 'keys': [ { - "kid": "sbc-auth-web", - "kty": "RSA", - "alg": "RS256", - "use": "sig", - "n": "AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-" - "TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR", - "e": "AQAB", + 'kid': 'sbc-auth-web', + 'kty': 'RSA', + 'alg': 'RS256', + 'use': 'sig', + 'n': 'AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-' + 'TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR', + 'e': 'AQAB', } ] } JWT_OIDC_TEST_PRIVATE_KEY_JWKS = { - "keys": [ + 'keys': [ { - "kid": "sbc-auth-web", - "kty": "RSA", - "alg": "RS256", - "use": "sig", - "n": "AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-" - "TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR", - "e": "AQAB", - "d": "C0G3QGI6OQ6tvbCNYGCqq043YI_8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhskURaDwk4-" - "8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh_" - "xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0", - "p": "APXcusFMQNHjh6KVD_hOUIw87lvK13WkDEeeuqAydai9Ig9JKEAAfV94W6Aftka7tGgE7ulg1vo3eJoLWJ1zvKM", - "q": "AOjX3OnPJnk0ZFUQBwhduCweRi37I6DAdLTnhDvcPTrrNWuKPg9uGwHjzFCJgKd8KBaDQ0X1rZTZLTqi3peT43s", - "dp": "AN9kBoA5o6_Rl9zeqdsIdWFmv4DB5lEqlEnC7HlAP-3oo3jWFO9KQqArQL1V8w2D4aCd0uJULiC9pCP7aTHvBhc", - "dq": "ANtbSY6njfpPploQsF9sU26U0s7MsuLljM1E8uml8bVJE1mNsiu9MgpUvg39jEu9BtM2tDD7Y51AAIEmIQex1nM", - "qi": "XLE5O360x-MhsdFXx8Vwz4304-MJg-oGSJXCK_ZWYOB_FGXFRTfebxCsSYi0YwJo-oNu96bvZCuMplzRI1liZw", + 'kid': 'sbc-auth-web', + 'kty': 'RSA', + 'alg': 'RS256', + 'use': 'sig', + 'n': 'AN-fWcpCyE5KPzHDjigLaSUVZI0uYrcGcc40InVtl-rQRDmAh-C2W8H4_Hxhr5VLc6crsJ2LiJTV_E72S03pzpOOaaYV6-' + 'TzAjCou2GYJIXev7f6Hh512PuG5wyxda_TlBSsI-gvphRTPsKCnPutrbiukCYrnPuWxX5_cES9eStR', + 'e': 'AQAB', + 'd': 'C0G3QGI6OQ6tvbCNYGCqq043YI_8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhskURaDwk4-' + '8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh_' + 'xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0', + 'p': 'APXcusFMQNHjh6KVD_hOUIw87lvK13WkDEeeuqAydai9Ig9JKEAAfV94W6Aftka7tGgE7ulg1vo3eJoLWJ1zvKM', + 'q': 'AOjX3OnPJnk0ZFUQBwhduCweRi37I6DAdLTnhDvcPTrrNWuKPg9uGwHjzFCJgKd8KBaDQ0X1rZTZLTqi3peT43s', + 'dp': 'AN9kBoA5o6_Rl9zeqdsIdWFmv4DB5lEqlEnC7HlAP-3oo3jWFO9KQqArQL1V8w2D4aCd0uJULiC9pCP7aTHvBhc', + 'dq': 'ANtbSY6njfpPploQsF9sU26U0s7MsuLljM1E8uml8bVJE1mNsiu9MgpUvg39jEu9BtM2tDD7Y51AAIEmIQex1nM', + 'qi': 'XLE5O360x-MhsdFXx8Vwz4304-MJg-oGSJXCK_ZWYOB_FGXFRTfebxCsSYi0YwJo-oNu96bvZCuMplzRI1liZw', } ] } @@ -301,44 +301,44 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods 4H8UZcVFN95vEKxJiLRjAmj6g273pu9kK4ymXNEjWWJn -----END RSA PRIVATE KEY-----""" - CFS_BASE_URL = "http://localhost:8080/paybc-api" - CFS_CLIENT_ID = "TEST" - CFS_CLIENT_SECRET = "TEST" - PAYBC_PORTAL_URL = "https://paydev.gov.bc.ca/public/directpay" + CFS_BASE_URL = 'http://localhost:8080/paybc-api' + CFS_CLIENT_ID = 'TEST' + CFS_CLIENT_SECRET = 'TEST' + PAYBC_PORTAL_URL = 'https://paydev.gov.bc.ca/public/directpay' - SERVER_NAME = "auth-web.dev.com" + SERVER_NAME = 'auth-web.dev.com' - REPORT_API_BASE_URL = "http://localhost:8080/reports-api/api/v1/reports" + REPORT_API_BASE_URL = 'http://localhost:8080/reports-api/api/v1/reports' - AUTH_API_ENDPOINT = "http://localhost:8080/auth-api/" + AUTH_API_ENDPOINT = 'http://localhost:8080/auth-api/' - BCOL_API_ENDPOINT = "http://localhost:8080/bcol-api" + BCOL_API_ENDPOINT = 'http://localhost:8080/bcol-api' - VALID_REDIRECT_URLS = ["http://localhost:8080/*"] + VALID_REDIRECT_URLS = ['http://localhost:8080/*'] TRANSACTION_REPORT_DEFAULT_TOTAL = 10 - PAYBC_DIRECT_PAY_API_KEY = "TESTKEYSECRET" - PAYBC_DIRECT_PAY_REF_NUMBER = "REF1234" - PAYBC_DIRECT_PAY_PORTAL_URL = "https://paydev.gov.bc.ca/public/directsale" - PAYBC_DIRECT_PAY_BASE_URL = "http://localhost:8080/paybc-api" + PAYBC_DIRECT_PAY_API_KEY = 'TESTKEYSECRET' + PAYBC_DIRECT_PAY_REF_NUMBER = 'REF1234' + PAYBC_DIRECT_PAY_PORTAL_URL = 'https://paydev.gov.bc.ca/public/directsale' + PAYBC_DIRECT_PAY_BASE_URL = 'http://localhost:8080/paybc-api' PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL = PAYBC_DIRECT_PAY_BASE_URL - PAYBC_DIRECT_PAY_CLIENT_ID = "TEST" - PAYBC_DIRECT_PAY_CLIENT_SECRET = "TEST" + PAYBC_DIRECT_PAY_CLIENT_ID = 'TEST' + PAYBC_DIRECT_PAY_CLIENT_SECRET = 'TEST' PAD_CONFIRMATION_PERIOD_IN_DAYS = 3 # Secret key for encrypting bank account - ACCOUNT_SECRET_KEY = "mysecretkeyforbank" + ACCOUNT_SECRET_KEY = 'mysecretkeyforbank' class ProdConfig(_Config): # pylint: disable=too-few-public-methods """Production environment configuration.""" - SECRET_KEY = _get_config("SECRET_KEY", default=None) + SECRET_KEY = _get_config('SECRET_KEY', default=None) if not SECRET_KEY: SECRET_KEY = os.urandom(24) - print("WARNING: SECRET_KEY being set as a one-shot", file=sys.stderr) + print('WARNING: SECRET_KEY being set as a one-shot', file=sys.stderr) TESTING = False DEBUG = False @@ -351,12 +351,12 @@ class MigrationConfig: # pylint: disable=too-few-public-methods DEBUG = True # POSTGRESQL - DB_USER = _get_config("DATABASE_USERNAME") - DB_PASSWORD = _get_config("DATABASE_PASSWORD") - DB_NAME = _get_config("DATABASE_NAME") - DB_HOST = _get_config("DATABASE_HOST") - DB_PORT = _get_config("DATABASE_PORT", default="5432") + DB_USER = _get_config('DATABASE_USERNAME') + DB_PASSWORD = _get_config('DATABASE_PASSWORD') + DB_NAME = _get_config('DATABASE_NAME') + DB_HOST = _get_config('DATABASE_HOST') + DB_PORT = _get_config('DATABASE_PORT', default='5432') SQLALCHEMY_DATABASE_URI = ( - f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}" + f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' ) SQLALCHEMY_TRACK_MODIFICATIONS = False From fb1ea12872d39a51e0319d14ac56de0d7ea4f71c Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Wed, 13 Mar 2024 22:47:40 -0700 Subject: [PATCH 19/87] Fixed flake8. --- pay-api/src/pay_api/config.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index ad785498f..9e3ad40af 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -87,7 +87,9 @@ class _Config: # pylint: disable=too-few-public-methods # POSTGRESQL if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): - SQLALCHEMY_DATABASE_URI = f'postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + SQLALCHEMY_DATABASE_URI = ( + f'postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + ) else: SQLALCHEMY_DATABASE_URI = f'postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}' From 4ac1650b93d84d530764493790284bf205ec92d4 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 14 Mar 2024 10:53:10 -0700 Subject: [PATCH 20/87] 20299 - Poetry upgrade (#1443) * Migrate to Poetry * More cleanup, add dev deps * Init poetry, add in deps * Docker file poetry for bcol-api * Revert makefile changes * Fix make files * Docker file + Makefile update * makefile update * Makefile update * Fix makefiles * Fix pay-admin docker * update pay queue dockerfile * minor nudge * one more nudge * Fix missing port * Fix poetry for docker * attempt to fix CI * attempt to fix pay-queue migrations * attempt to fix CI #999 * Update paths * Fix pay queues migration path * Fix path for jobs * rename payment-jobs -> jobs * Update maintainer * CI fix --- bcol-api/Dockerfile | 83 +- bcol-api/Makefile | 33 +- bcol-api/docker-entrypoint.sh | 4 - bcol-api/poetry.lock | 1785 +++++++++++ bcol-api/pyproject.toml | 55 + jobs/ftp-poller/Dockerfile | 59 +- jobs/ftp-poller/Makefile | 30 +- jobs/ftp-poller/poetry.lock | 2593 ++++++++++++++++ jobs/ftp-poller/pyproject.toml | 53 + jobs/ftp-poller/requirements.txt | 50 - jobs/ftp-poller/requirements/dev.txt | 26 - jobs/ftp-poller/requirements/prod.txt | 16 - .../requirements/repo-libraries.txt | 4 - jobs/payment-jobs/Dockerfile | 56 +- jobs/payment-jobs/Makefile | 29 +- jobs/payment-jobs/poetry.lock | 2636 +++++++++++++++++ jobs/payment-jobs/pyproject.toml | 60 + jobs/payment-jobs/requirements.txt | 89 - .../requirements/bcregistry-libraries.txt | 4 - jobs/payment-jobs/requirements/dev.txt | 28 - jobs/payment-jobs/requirements/prod.txt | 20 - .../tasks/cfs_create_account_task.py | 6 +- jobs/payment-jobs/tests/jobs/conftest.py | 10 +- pay-admin/Dockerfile | 83 +- pay-admin/Makefile | 33 +- pay-admin/poetry.lock | 2433 +++++++++++++++ pay-admin/pyproject.toml | 54 + pay-admin/requirements/dev.txt | 24 - pay-admin/requirements/prod.txt | 15 - pay-admin/requirements/repo-libraries.txt | 4 - pay-api/Dockerfile | 83 +- pay-api/Makefile | 32 +- pay-api/docker-entrypoint.sh | 4 - pay-api/poetry.lock | 2315 +++++++++++++++ pay-api/pyproject.toml | 113 + pay-api/requirements.txt | 76 - pay-api/requirements/dev.txt | 31 - pay-api/requirements/prod.txt | 31 - pay-api/requirements/repo-libraries.txt | 3 - pay-queue/Dockerfile | 83 +- pay-queue/Makefile | 29 +- pay-queue/poetry.lock | 2566 ++++++++++++++++ pay-queue/pyproject.toml | 56 + pay-queue/requirements.txt | 85 - .../requirements/bcregistry-libraries.txt | 5 - pay-queue/requirements/dev.txt | 31 - pay-queue/requirements/prod.txt | 15 - pay-queue/tests/conftest.py | 9 +- report-api/src/api/resources/__init__.py | 1 - 49 files changed, 15151 insertions(+), 792 deletions(-) delete mode 100755 bcol-api/docker-entrypoint.sh create mode 100644 bcol-api/poetry.lock create mode 100644 bcol-api/pyproject.toml create mode 100644 jobs/ftp-poller/poetry.lock create mode 100644 jobs/ftp-poller/pyproject.toml delete mode 100644 jobs/ftp-poller/requirements.txt delete mode 100644 jobs/ftp-poller/requirements/dev.txt delete mode 100644 jobs/ftp-poller/requirements/prod.txt delete mode 100644 jobs/ftp-poller/requirements/repo-libraries.txt create mode 100644 jobs/payment-jobs/poetry.lock create mode 100644 jobs/payment-jobs/pyproject.toml delete mode 100644 jobs/payment-jobs/requirements.txt delete mode 100644 jobs/payment-jobs/requirements/bcregistry-libraries.txt delete mode 100644 jobs/payment-jobs/requirements/dev.txt delete mode 100644 jobs/payment-jobs/requirements/prod.txt create mode 100644 pay-admin/poetry.lock create mode 100644 pay-admin/pyproject.toml delete mode 100644 pay-admin/requirements/dev.txt delete mode 100644 pay-admin/requirements/prod.txt delete mode 100644 pay-admin/requirements/repo-libraries.txt delete mode 100755 pay-api/docker-entrypoint.sh create mode 100644 pay-api/poetry.lock create mode 100644 pay-api/pyproject.toml delete mode 100644 pay-api/requirements.txt delete mode 100755 pay-api/requirements/dev.txt delete mode 100644 pay-api/requirements/prod.txt delete mode 100644 pay-api/requirements/repo-libraries.txt create mode 100644 pay-queue/poetry.lock create mode 100644 pay-queue/pyproject.toml delete mode 100644 pay-queue/requirements.txt delete mode 100644 pay-queue/requirements/bcregistry-libraries.txt delete mode 100755 pay-queue/requirements/dev.txt delete mode 100644 pay-queue/requirements/prod.txt diff --git a/bcol-api/Dockerfile b/bcol-api/Dockerfile index 288189b4e..c849df874 100644 --- a/bcol-api/Dockerfile +++ b/bcol-api/Dockerfile @@ -1,39 +1,94 @@ -FROM python:3.12.2-bullseye +FROM python:3.12.2-bullseye as development_build ARG VCS_REF="missing" ARG BUILD_DATE="missing" ENV VCS_REF=${VCS_REF} ENV BUILD_DATE=${BUILD_DATE} +ENV PORT=8080 LABEL org.label-schema.vcs-ref=${VCS_REF} \ org.label-schema.build-date=${BUILD_DATE} USER root +LABEL maintainer="travissemple" +LABEL vendor="BCROS" + +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 + +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.3.2 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' + +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] + +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* + RUN apt-get update && apt-get install -y libsasl2-dev python-dev libldap2-dev libssl-dev libsnmp-dev WORKDIR / COPY ["scripts/openssl.cnf","/etc/ssl/"] -# Create working directory -RUN mkdir /opt/app-root && chmod 755 /opt/app-root -WORKDIR /opt/app-root +WORKDIR /code -# Install the requirements -COPY ./requirements.txt . +RUN groupadd -g "${GID}" -r web \ + && useradd -d '/code' -g web -l -r -u "${UID}" web \ + && chown web:web -R '/code' -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml /code/ -COPY . . +COPY --chown=web:web ./src /code/src +COPY --chown=web:web ./README.md /code -RUN pip install . +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + # Install deps: + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi -USER 1001 +# Running as non-root user: +USER web -# Set Python path -ENV PYTHONPATH=/opt/app-root/src +# The following stage is only for production: +FROM development_build AS production_build +COPY --chown=web:web . /code -ENTRYPOINT ["bash", "docker-entrypoint.sh"] +CMD gunicorn --bind 0.0.0.0:${PORT} --config /code/gunicorn_config.py wsgi:app diff --git a/bcol-api/Makefile b/bcol-api/Makefile index 6d77e1353..5678903cc 100755 --- a/bcol-api/Makefile +++ b/bcol-api/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=bcol-api ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,26 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip freeze | sort > requirements.txt ;\ - cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . - +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -63,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME) + poetry run pylint --rcfile=setup.cfg src/$(PROJECT_NAME) flake8: ## Linting with flake8 - . venv/bin/activate && flake8 src/$(PROJECT_NAME) tests + poetry run flake8 src/$(PROJECT_NAME) tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html @@ -131,7 +114,7 @@ tag: push ## tag image # COMMANDS - Local # ################################################################################# run: ## Run the project in local - . venv/bin/activate && python -m flask run -p 5000 + poetry run flask run -p 5000 ################################################################################# # Self Documenting Commands # diff --git a/bcol-api/docker-entrypoint.sh b/bcol-api/docker-entrypoint.sh deleted file mode 100755 index ef5abdf26..000000000 --- a/bcol-api/docker-entrypoint.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -echo 'starting application' -gunicorn -b 0.0.0.0:8080 wsgi:application diff --git a/bcol-api/poetry.lock b/bcol-api/poetry.lock new file mode 100644 index 000000000..7c1b76992 --- /dev/null +++ b/bcol-api/poetry.lock @@ -0,0 +1,1785 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "aniso8601" +version = "9.0.1" +description = "A library for parsing ISO 8601 strings." +optional = false +python-versions = "*" +files = [ + {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, + {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, +] + +[package.extras] +dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" + +[[package]] +name = "blinker" +version = "1.7.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "cachelib" +version = "0.12.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cachelib-0.12.0-py3-none-any.whl", hash = "sha256:038f4d855afc3eb8caab10458f6eac55c328911f9055824c22c2f259ef9ed3a3"}, + {file = "cachelib-0.12.0.tar.gz", hash = "sha256:8243029a028436fd23229113dee517c0700bb43a8a289ec5a963e4af9ca2b194"}, +] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" + +[package.source] +type = "git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-restx" +version = "1.3.0" +description = "Fully featured framework for fast, easy and documented API development with Flask" +optional = false +python-versions = "*" +files = [ + {file = "flask-restx-1.3.0.tar.gz", hash = "sha256:4f3d3fa7b6191fcc715b18c201a12cd875176f92ba4acc61626ccfd571ee1728"}, + {file = "flask_restx-1.3.0-py2.py3-none-any.whl", hash = "sha256:636c56c3fb3f2c1df979e748019f084a938c4da2035a3e535a4673e4fc177691"}, +] + +[package.dependencies] +aniso8601 = ">=0.82" +Flask = ">=0.8,<2.0.0 || >2.0.0" +importlib-resources = "*" +jsonschema = "*" +pytz = "*" +werkzeug = "!=2.0.0" + +[package.extras] +dev = ["Faker (==2.0.0)", "black", "blinker", "invoke (==2.2.0)", "mock (==3.0.5)", "pytest (==7.0.1)", "pytest-benchmark (==3.4.1)", "pytest-cov (==4.0.0)", "pytest-flask (==1.3.0)", "pytest-mock (==3.6.1)", "pytest-profiling (==1.7.0)", "setuptools", "tox", "twine (==3.8.0)", "tzlocal"] +doc = ["Sphinx (==5.3.0)", "alabaster (==0.7.12)", "sphinx-issues (==3.0.1)"] +test = ["Faker (==2.0.0)", "blinker", "invoke (==2.2.0)", "mock (==3.0.5)", "pytest (==7.0.1)", "pytest-benchmark (==3.4.1)", "pytest-cov (==4.0.0)", "pytest-flask (==1.3.0)", "pytest-mock (==3.6.1)", "pytest-profiling (==1.7.0)", "setuptools", "twine (==3.8.0)", "tzlocal"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "importlib-resources" +version = "6.3.0" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.3.0-py3-none-any.whl", hash = "sha256:783407aa1cd05550e3aa123e8f7cfaebee35ffa9cb0242919e2d1e4172222705"}, + {file = "importlib_resources-6.3.0.tar.gz", hash = "sha256:166072a97e86917a9025876f34286f549b9caf1d10b35a1b372bffa1600c6569"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.collections", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isodate" +version = "0.6.1" +description = "An ISO 8601 date/time/duration parser and formatter" +optional = false +python-versions = "*" +files = [ + {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, + {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "lxml" +version = "5.1.0" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=3.6" +files = [ + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, + {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2befa20a13f1a75c751f47e00929fb3433d67eb9923c2c0b364de449121f447c"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22b7ee4c35f374e2c20337a95502057964d7e35b996b1c667b5c65c567d2252a"}, + {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf8443781533b8d37b295016a4b53c1494fa9a03573c09ca5104550c138d5c05"}, + {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, + {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, + {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af8920ce4a55ff41167ddbc20077f5698c2e710ad3353d32a07d3264f3a2021e"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cfced4a069003d8913408e10ca8ed092c49a7f6cefee9bb74b6b3e860683b45"}, + {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9e5ac3437746189a9b4121db2a7b86056ac8786b12e88838696899328fc44bb2"}, + {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, + {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, + {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1"}, + {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3"}, + {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8"}, + {file = "lxml-5.1.0-cp312-cp312-win32.whl", hash = "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01"}, + {file = "lxml-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623"}, + {file = "lxml-5.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:24ef5a4631c0b6cceaf2dbca21687e29725b7c4e171f33a8f8ce23c12558ded1"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d2900b7f5318bc7ad8631d3d40190b95ef2aa8cc59473b73b294e4a55e9f30f"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:601f4a75797d7a770daed8b42b97cd1bb1ba18bd51a9382077a6a247a12aa38d"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4b68c961b5cc402cbd99cca5eb2547e46ce77260eb705f4d117fd9c3f932b95"}, + {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:afd825e30f8d1f521713a5669b63657bcfe5980a916c95855060048b88e1adb7"}, + {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:262bc5f512a66b527d026518507e78c2f9c2bd9eb5c8aeeb9f0eb43fcb69dc67"}, + {file = "lxml-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:e856c1c7255c739434489ec9c8aa9cdf5179785d10ff20add308b5d673bed5cd"}, + {file = "lxml-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:c7257171bb8d4432fe9d6fdde4d55fdbe663a63636a17f7f9aaba9bcb3153ad7"}, + {file = "lxml-5.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9e240ae0ba96477682aa87899d94ddec1cc7926f9df29b1dd57b39e797d5ab5"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a96f02ba1bcd330807fc060ed91d1f7a20853da6dd449e5da4b09bfcc08fdcf5"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3898ae2b58eeafedfe99e542a17859017d72d7f6a63de0f04f99c2cb125936"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61c5a7edbd7c695e54fca029ceb351fc45cd8860119a0f83e48be44e1c464862"}, + {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3aeca824b38ca78d9ee2ab82bd9883083d0492d9d17df065ba3b94e88e4d7ee6"}, + {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, + {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, + {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, + {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:98f3f020a2b736566c707c8e034945c02aa94e124c24f77ca097c446f81b01f1"}, + {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, + {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, + {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8b0c78e7aac24979ef09b7f50da871c2de2def043d468c4b41f512d831e912"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bcf86dfc8ff3e992fed847c077bd875d9e0ba2fa25d859c3a0f0f76f07f0c8d"}, + {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:49a9b4af45e8b925e1cd6f3b15bbba2c81e7dba6dce170c677c9cda547411e14"}, + {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:280f3edf15c2a967d923bcfb1f8f15337ad36f93525828b40a0f9d6c2ad24890"}, + {file = "lxml-5.1.0-cp39-cp39-win32.whl", hash = "sha256:ed7326563024b6e91fef6b6c7a1a2ff0a71b97793ac33dbbcf38f6005e51ff6e"}, + {file = "lxml-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8d7b4beebb178e9183138f552238f7e6613162a42164233e2bda00cb3afac58f"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9bd0ae7cc2b85320abd5e0abad5ccee5564ed5f0cc90245d2f9a8ef330a8deae"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c1d679df4361408b628f42b26a5d62bd3e9ba7f0c0e7969f925021554755aa"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ad3a8ce9e8a767131061a22cd28fdffa3cd2dc193f399ff7b81777f3520e372"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:304128394c9c22b6569eba2a6d98392b56fbdfbad58f83ea702530be80d0f9df"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d74fcaf87132ffc0447b3c685a9f862ffb5b43e70ea6beec2fb8057d5d2a1fea"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8cf5877f7ed384dabfdcc37922c3191bf27e55b498fecece9fd5c2c7aaa34c33"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:877efb968c3d7eb2dad540b6cabf2f1d3c0fbf4b2d309a3c141f79c7e0061324"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f14a4fb1c1c402a22e6a341a24c1341b4a3def81b41cd354386dcb795f83897"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:25663d6e99659544ee8fe1b89b1a8c0aaa5e34b103fab124b17fa958c4a324a6"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8b9f19df998761babaa7f09e6bc169294eefafd6149aaa272081cbddc7ba4ca3"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e53d7e6a98b64fe54775d23a7c669763451340c3d44ad5e3a3b48a1efbdc96f"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c3cd1fc1dc7c376c54440aeaaa0dcc803d2126732ff5c6b68ccd619f2e64be4f"}, + {file = "lxml-5.1.0.tar.gz", hash = "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=3.0.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycountry" +version = "23.12.11" +description = "ISO country, subdivision, language, currency and script definitions and their translations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycountry-23.12.11-py3-none-any.whl", hash = "sha256:2ff91cff4f40ff61086e773d61e72005fe95de4a57bfc765509db05695dc50ab"}, + {file = "pycountry-23.12.11.tar.gz", hash = "sha256:00569d82eaefbc6a490a311bfa84a9c571cff9ddbf8b0a4f4e7b4f868b4ad925"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.12.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "python-ldap" +version = "3.4.4" +description = "Python modules for implementing LDAP clients" +optional = false +python-versions = ">=3.6" +files = [ + {file = "python-ldap-3.4.4.tar.gz", hash = "sha256:7edb0accec4e037797705f3a05cbf36a9fde50d08c8f67f2aef99a2628fab828"}, +] + +[package.dependencies] +pyasn1 = ">=0.3.7" +pyasn1_modules = ">=0.1.5" + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-file" +version = "2.0.0" +description = "File transport adapter for Requests" +optional = false +python-versions = "*" +files = [ + {file = "requests-file-2.0.0.tar.gz", hash = "sha256:20c5931629c558fda566cacc10cfe2cd502433e628f568c34c80d96a0cc95972"}, + {file = "requests_file-2.0.0-py2.py3-none-any.whl", hash = "sha256:3e493d390adb44aa102ebea827a48717336d5268968c370eaf19abaf5cae13bf"}, +] + +[package.dependencies] +requests = ">=1.0.0" + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +subdirectory = "python" + +[[package]] +name = "sentry-sdk" +version = "1.42.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, + {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, +] + +[package.dependencies] +blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} +certifi = "*" +flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} +markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "werkzeug" +version = "3.0.1" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[[package]] +name = "zeep" +version = "4.2.1" +description = "A Python SOAP client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zeep-4.2.1-py3-none-any.whl", hash = "sha256:6754feb4c34a4b6d65fbc359252bf6654dcce3937bf1d95aae4402a60a8f5939"}, + {file = "zeep-4.2.1.tar.gz", hash = "sha256:72093acfdb1d8360ed400869b73fbf1882b95c4287f798084c42ee0c1ff0e425"}, +] + +[package.dependencies] +attrs = ">=17.2.0" +isodate = ">=0.5.4" +lxml = ">=4.6.0" +platformdirs = ">=1.4.0" +pytz = "*" +requests = ">=2.7.0" +requests-file = ">=1.5.1" +requests-toolbelt = ">=0.7.1" + +[package.extras] +async = ["httpx (>=0.15.0)"] +docs = ["sphinx (>=1.4.0)"] +test = ["coverage[toml] (==5.2.1)", "flake8 (==3.8.3)", "flake8-blind-except (==0.1.1)", "flake8-debugger (==3.2.1)", "flake8-imports (==0.1.1)", "freezegun (==0.3.15)", "isort (==5.3.2)", "pretend (==1.0.9)", "pytest (==6.2.5)", "pytest-asyncio", "pytest-cov (==2.8.1)", "pytest-httpx", "requests-mock (>=0.7.0)"] +xmlsec = ["xmlsec (>=0.6.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "bf95b2ec5b5f61f7f67c38f71ebdee994d018d51ebe73d16759363cc358c6f0c" diff --git a/bcol-api/pyproject.toml b/bcol-api/pyproject.toml new file mode 100644 index 000000000..ec6d9a19e --- /dev/null +++ b/bcol-api/pyproject.toml @@ -0,0 +1,55 @@ +[tool.poetry] +name = "bcol-api" +version = "0.1.0" +description = "" +authors = ["BC Registries"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +gunicorn = "^21.2.0" +flask = "^3.0.2" +flask-script = "^2.0.6" +flask-moment = "^1.0.5" +flask-restx = "^1.3.0" +python-dotenv = "^1.0.1" +psycopg2-binary = "^2.9.9" +jsonschema = "4.17.3" +requests = "^2.31.0" +zeep = "^4.2.1" +python-ldap = "^3.4.4" +sentry-sdk = {extras = ["flask"], version = "^1.42.0"} +attrs = "^23.2.0" +werkzeug = "^3.0.1" +jaeger-client = "^4.8.0" +pycountry = "^23.12.11" +itsdangerous = "^2.1.2" +jinja2 = "^3.1.3" + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +pydocstyle = "^6.3.0" +lovely-pytest-docker = "^0.3.1" +isort = "^5.13.2" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/jobs/ftp-poller/Dockerfile b/jobs/ftp-poller/Dockerfile index 26740742e..fd4eada0e 100644 --- a/jobs/ftp-poller/Dockerfile +++ b/jobs/ftp-poller/Dockerfile @@ -36,14 +36,61 @@ RUN mkdir $HOME && chmod 755 $HOME WORKDIR $HOME -# Install the requirements -COPY ./requirements.txt . -COPY ./requirements/ ./requirements/ -COPY . . -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 + +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.3.2 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' + +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] + +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml . + +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +COPY . . # Set ownership and permissions diff --git a/jobs/ftp-poller/Makefile b/jobs/ftp-poller/Makefile index 42610f6ae..46dd918d4 100644 --- a/jobs/ftp-poller/Makefile +++ b/jobs/ftp-poller/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=ftp-poller ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,25 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip freeze | sort > requirements.txt ;\ - cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -62,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg tasks tests utils + poetry run pylint --rcfile=setup.cfg tasks tests utils flake8: ## Linting with flake8 - . venv/bin/activate && flake8 tasks tests utils + poetry run flake8 tasks tests utils lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html diff --git a/jobs/ftp-poller/poetry.lock b/jobs/ftp-poller/poetry.lock new file mode 100644 index 000000000..c49f113da --- /dev/null +++ b/jobs/ftp-poller/poetry.lock @@ -0,0 +1,2593 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "aniso8601" +version = "9.0.1" +description = "A library for parsing ISO 8601 strings." +optional = false +python-versions = "*" +files = [ + {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, + {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, +] + +[package.extras] +dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"] + +[[package]] +name = "argon2-cffi" +version = "23.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, + {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["argon2-cffi[tests,typing]", "tox (>4)"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] +tests = ["hypothesis", "pytest"] +typing = ["mypy"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" + +[[package]] +name = "bcrypt" +version = "4.1.2" +description = "Modern password hashing for your software and your servers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bcrypt-4.1.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0"}, + {file = "bcrypt-4.1.2-cp37-abi3-win32.whl", hash = "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"}, + {file = "bcrypt-4.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551"}, + {file = "bcrypt-4.1.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a"}, + {file = "bcrypt-4.1.2-cp39-abi3-win32.whl", hash = "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f"}, + {file = "bcrypt-4.1.2-cp39-abi3-win_amd64.whl", hash = "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb"}, + {file = "bcrypt-4.1.2.tar.gz", hash = "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258"}, +] + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] +typecheck = ["mypy"] + +[[package]] +name = "blinker" +version = "1.7.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "cachelib" +version = "0.9.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-caching" +version = "2.1.0" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, +] + +[package.dependencies] +cachelib = ">=0.9.0,<0.10.0" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" + +[package.source] +type = "git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-restplus" +version = "0.13.0" +description = "Fully featured framework for fast, easy and documented API development with Flask" +optional = false +python-versions = "*" +files = [ + {file = "flask-restplus-0.13.0.tar.gz", hash = "sha256:a66e442d0bca08f389fc3d07b4d808fc89961285d12fb8013f7cf15516fa9f5c"}, + {file = "flask_restplus-0.13.0-py2.py3-none-any.whl", hash = "sha256:a15d251923a8feb09a5d805c2f4d188555910a42c64d58f7dd281b8cac095f1b"}, +] + +[package.dependencies] +aniso8601 = ">=0.82" +Flask = ">=0.8" +jsonschema = "*" +pytz = "*" +six = ">=1.3.0" + +[package.extras] +dev = ["blinker", "flake8 (==3.5.0)", "invoke (==0.21.0)", "mock (==2.0.0)", "pytest (==3.2.3)", "pytest-benchmark (==3.1.1)", "pytest-cov (==2.5.1)", "pytest-faker (==2.0.0)", "pytest-flask (==0.10.0)", "pytest-mock (==1.6.3)", "pytest-profiling (==1.2.11)", "pytest-sugar (==0.9.0)", "readme-renderer (==17.2)", "tox (==2.9.1)", "tzlocal"] +doc = ["Sphinx (==2.0.1)", "alabaster (==0.7.10)", "sphinx-issues (==1.2.0)"] +test = ["blinker", "mock (==2.0.0)", "pytest (==3.2.3)", "pytest-benchmark (==3.1.1)", "pytest-cov (==2.5.1)", "pytest-faker (==2.0.0)", "pytest-flask (==0.10.0)", "pytest-mock (==1.6.3)", "pytest-profiling (==1.2.11)", "pytest-sugar (==0.9.0)", "tzlocal"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "google-api-core" +version = "2.17.1" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.28.1" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.20.0" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.62.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.62.1)"] + +[[package]] +name = "grpcio-status" +version = "1.62.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.1" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "9.2.2" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.26.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "minio" +version = "7.2.5" +description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" +optional = false +python-versions = "*" +files = [ + {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, + {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, +] + +[package.dependencies] +argon2-cffi = "*" +certifi = "*" +pycryptodome = "*" +typing-extensions = "*" +urllib3 = "*" + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "paramiko" +version = "3.4.0" +description = "SSH2 protocol library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "paramiko-3.4.0-py3-none-any.whl", hash = "sha256:43f0b51115a896f9c00f59618023484cb3a14b98bbceab43394a39c6739b7ee7"}, + {file = "paramiko-3.4.0.tar.gz", hash = "sha256:aac08f26a31dc4dffd92821527d1682d99d52f9ef6851968114a8728f3c274d3"}, +] + +[package.dependencies] +bcrypt = ">=3.2" +cryptography = ">=3.3" +pynacl = ">=1.5" + +[package.extras] +all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +invoke = ["invoke (>=2.0)"] + +[[package]] +name = "pay_api" +version = "0.0.0" +description = "A short description of the project" +optional = false +python-versions = ">=3.12" +files = [] +develop = false + +[package.dependencies] +cattrs = "*" +croniter = "*" +cryptography = "*" +dpath = "*" +Flask = "*" +Flask-Caching = "*" +Flask-Cors = "*" +flask-jwt-oidc = "*" +flask-marshmallow = "*" +Flask-Migrate = "*" +Flask-Moment = "*" +Flask-Script = "*" +Flask-SQLAlchemy = "*" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +gunicorn = "*" +holidays = "0.37" +itsdangerous = "*" +jaeger-client = "*" +Jinja2 = "*" +jsonschema = "4.17.3" +launchdarkly-server-sdk = "*" +marshmallow-sqlalchemy = "*" +psycopg2-binary = "*" +pyhumps = "*" +python-dotenv = "*" +requests = "*" +sentry-sdk = {version = "*", extras = ["flask"]} +sqlalchemy = "*" +sqlalchemy_utils = "*" +Werkzeug = "*" + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "18263" +resolved_reference = "fe42bf81c86c77946a1df238f69f12ad6b83d804" +subdirectory = "pay-api" + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pycryptodome" +version = "3.20.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, + {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pysftp" +version = "0.2.9" +description = "A friendly face on SFTP" +optional = false +python-versions = "*" +files = [ + {file = "pysftp-0.2.9.tar.gz", hash = "sha256:fbf55a802e74d663673400acd92d5373c1c7ee94d765b428d9f977567ac4854a"}, +] + +[package.dependencies] +paramiko = ">=1.17" + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.5.post1" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, + {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.12.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +subdirectory = "python" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.42.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, + {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, +] + +[package.dependencies] +blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} +certifi = "*" +flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} +markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "werkzeug" +version = "3.0.1" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "f048f3fd232a6fb3c61c8d1f8651e55f81e6c283799dfde0ed451cea62a6e3a6" diff --git a/jobs/ftp-poller/pyproject.toml b/jobs/ftp-poller/pyproject.toml new file mode 100644 index 000000000..cf312a280 --- /dev/null +++ b/jobs/ftp-poller/pyproject.toml @@ -0,0 +1,53 @@ +[tool.poetry] +name = "ftp-poller" +version = "0.1.0" +description = "" +authors = ["Travis Semple "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +gunicorn = "^21.2.0" +flask = "^3.0.2" +flask-restplus = "^0.13.0" +python-dotenv = "^1.0.1" +psycopg2-binary = "^2.9.9" +jsonschema = "4.17.3" +requests = "^2.31.0" +werkzeug = "^3.0.1" +pysftp = "^0.2.9" +minio = "^7.2.5" +jaeger-client = "^4.8.0" +itsdangerous = "^2.1.2" +jinja2 = "^3.1.3" +protobuf = "4.25.3" +launchdarkly-server-sdk = "^9.2.2" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "18263", subdirectory = "pay-api"} + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +lovely-pytest-docker = "^0.3.1" +pytest-asyncio = "^0.23.5.post1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/jobs/ftp-poller/requirements.txt b/jobs/ftp-poller/requirements.txt deleted file mode 100644 index 44675066f..000000000 --- a/jobs/ftp-poller/requirements.txt +++ /dev/null @@ -1,50 +0,0 @@ -Flask==3.0.2 -Jinja2==3.1.3 -MarkupSafe==2.1.5 -PyNaCl==1.5.0 -Werkzeug==3.0.1 -aniso8601==9.0.1 -argon2-cffi-bindings==21.2.0 -argon2-cffi==23.1.0 -attrs==23.2.0 -bcrypt==4.1.2 -blinker==1.7.0 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -cryptography==42.0.5 -expiringdict==1.2.2 -flask-restplus==0.13.0 -gunicorn==21.2.0 -idna==3.6 -itsdangerous==2.1.2 -jaeger-client==4.8.0 -jsonschema==4.17.3 -launchdarkly-eventsource==1.1.1 -launchdarkly-server-sdk==9.2.1 -minio==7.2.5 -opentracing==2.4.0 -packaging==23.2 -paramiko==3.4.0 -protobuf==4.25.3 -psycopg2-binary==2.9.9 -pyRFC3339==1.1 -pycparser==2.21 -pycryptodome==3.20.0 -pyrsistent==0.20.0 -pysftp==0.2.9 -python-dotenv==1.0.1 -pytz==2024.1 -requests==2.31.0 -semver==3.0.2 -six==1.16.0 -threadloop==1.0.2 -thrift==0.16.0 -tornado==6.4 -typing_extensions==4.10.0 -urllib3==2.2.1 --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@18263#egg=pay-api&subdirectory=pay-api -git+https://github.com/daxiom/simple-cloudevent.py.git -git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/jobs/ftp-poller/requirements/dev.txt b/jobs/ftp-poller/requirements/dev.txt deleted file mode 100644 index e98fcbca3..000000000 --- a/jobs/ftp-poller/requirements/dev.txt +++ /dev/null @@ -1,26 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov - -# Lint and code style -flake8==5.0.4 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask - -# docker -lovely-pytest-docker -pytest-asyncio diff --git a/jobs/ftp-poller/requirements/prod.txt b/jobs/ftp-poller/requirements/prod.txt deleted file mode 100644 index e9bfc70fc..000000000 --- a/jobs/ftp-poller/requirements/prod.txt +++ /dev/null @@ -1,16 +0,0 @@ -gunicorn -Flask -Flask-RESTplus -python-dotenv -psycopg2-binary -jsonschema==4.17.3 -requests -Werkzeug -pysftp -minio -jaeger-client -itsdangerous -Jinja2 -protobuf -launchdarkly-server-sdk - diff --git a/jobs/ftp-poller/requirements/repo-libraries.txt b/jobs/ftp-poller/requirements/repo-libraries.txt deleted file mode 100644 index f71c16b32..000000000 --- a/jobs/ftp-poller/requirements/repo-libraries.txt +++ /dev/null @@ -1,4 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@18263#egg=pay-api&subdirectory=pay-api -git+https://github.com/daxiom/simple-cloudevent.py.git -git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/jobs/payment-jobs/Dockerfile b/jobs/payment-jobs/Dockerfile index 8b940bf5f..a2f8fd6d8 100644 --- a/jobs/payment-jobs/Dockerfile +++ b/jobs/payment-jobs/Dockerfile @@ -50,16 +50,60 @@ RUN mkdir $HOME && chmod 755 $HOME WORKDIR $HOME -# Install the requirements -COPY ./requirements.txt . -COPY ./requirements/ ./requirements/ -COPY . . +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 + +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.3.2 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml . +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +COPY . . # Set ownership and permissions # Set scripts as executable (make files and python files do not have to be marked) # Make /etc/passwd writable for the root group so an entry can be created for an OpenShift assigned user account. diff --git a/jobs/payment-jobs/Makefile b/jobs/payment-jobs/Makefile index 898950b96..942f7e719 100644 --- a/jobs/payment-jobs/Makefile +++ b/jobs/payment-jobs/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=payment-job ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,24 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip install -Ur requirements/bcregistry-libraries.txt ;\ - pip freeze | sort > requirements.txt ;\ - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -61,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg tasks + poetry run pylint --rcfile=setup.cfg tasks flake8: ## Linting with flake8 - . venv/bin/activate && flake8 tasks tests + poetry run flake8 tasks tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock new file mode 100644 index 000000000..fbc3c7b50 --- /dev/null +++ b/jobs/payment-jobs/poetry.lock @@ -0,0 +1,2636 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "argon2-cffi" +version = "23.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, + {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["argon2-cffi[tests,typing]", "tox (>4)"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] +tests = ["hypothesis", "pytest"] +typing = ["mypy"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" + +[[package]] +name = "bcrypt" +version = "4.1.2" +description = "Modern password hashing for your software and your servers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bcrypt-4.1.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0"}, + {file = "bcrypt-4.1.2-cp37-abi3-win32.whl", hash = "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"}, + {file = "bcrypt-4.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551"}, + {file = "bcrypt-4.1.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a"}, + {file = "bcrypt-4.1.2-cp39-abi3-win32.whl", hash = "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f"}, + {file = "bcrypt-4.1.2-cp39-abi3-win_amd64.whl", hash = "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb"}, + {file = "bcrypt-4.1.2.tar.gz", hash = "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258"}, +] + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] +typecheck = ["mypy"] + +[[package]] +name = "blinker" +version = "1.7.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "cachelib" +version = "0.12.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cachelib-0.12.0-py3-none-any.whl", hash = "sha256:038f4d855afc3eb8caab10458f6eac55c328911f9055824c22c2f259ef9ed3a3"}, + {file = "cachelib-0.12.0.tar.gz", hash = "sha256:8243029a028436fd23229113dee517c0700bb43a8a289ec5a963e4af9ca2b194"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "cx-oracle" +version = "8.3.0" +description = "Python interface to Oracle" +optional = false +python-versions = "*" +files = [ + {file = "cx_Oracle-8.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b6a23da225f03f50a81980c61dbd6a358c3575f212ca7f4c22bb65a9faf94f7f"}, + {file = "cx_Oracle-8.3.0-cp310-cp310-win32.whl", hash = "sha256:715a8bbda5982af484ded14d184304cc552c1096c82471dd2948298470e88a04"}, + {file = "cx_Oracle-8.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:07f01608dfb6603a8f2a868fc7c7bdc951480f187df8dbc50f4d48c884874e6a"}, + {file = "cx_Oracle-8.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4b3afe7a911cebaceda908228d36839f6441cbd38e5df491ec25960562bb01a0"}, + {file = "cx_Oracle-8.3.0-cp36-cp36m-win32.whl", hash = "sha256:076ffb71279d6b2dcbf7df028f62a01e18ce5bb73d8b01eab582bf14a62f4a61"}, + {file = "cx_Oracle-8.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:b82e4b165ffd807a2bd256259a6b81b0a2452883d39f987509e2292d494ea163"}, + {file = "cx_Oracle-8.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b902db61dcdcbbf8dd981f5a46d72fef40c5150c7fc0eb0f0698b462d6eb834e"}, + {file = "cx_Oracle-8.3.0-cp37-cp37m-win32.whl", hash = "sha256:4c82ca74442c298ceec56d207450c192e06ecf8ad52eb4aaad0812e147ceabf7"}, + {file = "cx_Oracle-8.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:54164974d526b76fdefb0b66a42b68e1fca5df78713d0eeb8c1d0047b83f6bcf"}, + {file = "cx_Oracle-8.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:410747d542e5f94727f5f0e42e9706c772cf9094fb348ce965ab88b3a9e4d2d8"}, + {file = "cx_Oracle-8.3.0-cp38-cp38-win32.whl", hash = "sha256:3baa878597c5fadb2c72f359f548431c7be001e722ce4a4ebdf3d2293a1bb70b"}, + {file = "cx_Oracle-8.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:de42bdc882abdc5cea54597da27a05593b44143728e5b629ad5d35decb1a2036"}, + {file = "cx_Oracle-8.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:df412238a9948340591beee9ec64fa62a2efacc0d91107034a7023e2991fba97"}, + {file = "cx_Oracle-8.3.0-cp39-cp39-win32.whl", hash = "sha256:70d3cf030aefd71f99b45beba77237b2af448adf5e26be0db3d0d3dee6ea4230"}, + {file = "cx_Oracle-8.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:bf01ce87edb4ef663b2e5bd604e1e0154d2cc2f12b60301f788b569d9db8a900"}, + {file = "cx_Oracle-8.3.0.tar.gz", hash = "sha256:3b2d215af4441463c97ea469b9cc307460739f89fdfa8ea222ea3518f1a424d9"}, +] + +[[package]] +name = "dataclass-wizard" +version = "0.22.3" +description = "Marshal dataclasses to/from JSON. Use field properties with initial values. Construct a dataclass schema with JSON input." +optional = false +python-versions = "*" +files = [ + {file = "dataclass-wizard-0.22.3.tar.gz", hash = "sha256:4c46591782265058f1148cfd1f54a3a91221e63986fdd04c9d59f4ced61f4424"}, + {file = "dataclass_wizard-0.22.3-py2.py3-none-any.whl", hash = "sha256:63751203e54b9b9349212cc185331da73c1adc99c51312575eb73bb5c00c1962"}, +] + +[package.extras] +dev = ["Sphinx (==5.3.0)", "bump2version (==1.0.1)", "coverage (>=6.2)", "dataclass-factory (==2.12)", "dataclasses-json (==0.5.6)", "flake8 (>=3)", "jsons (==1.6.1)", "pip (>=21.3.1)", "pytest (==7.0.1)", "pytest-cov (==3.0.0)", "pytest-mock (>=3.6.1)", "pytimeparse (==1.1.8)", "sphinx-issues (==3.0.1)", "sphinx-issues (==4.0.0)", "tox (==3.24.5)", "twine (==3.8.0)", "watchdog[watchmedo] (==2.1.6)", "wheel (==0.37.1)", "wheel (==0.42.0)"] +timedelta = ["pytimeparse (>=1.1.7)"] +yaml = ["PyYAML (>=5.3)"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "faker" +version = "24.2.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Faker-24.2.0-py3-none-any.whl", hash = "sha256:dce4754921f9fa7e2003c26834093361b8f45072e0f46f172d6ca1234774ecd4"}, + {file = "Faker-24.2.0.tar.gz", hash = "sha256:87d5e7730426e7b36817921679c4eaf3d810cedb8c81194f47adc3df2122ca18"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-caching" +version = "1.11.1" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Flask-Caching-1.11.1.tar.gz", hash = "sha256:28af189e97defb9e39b43ebe197b54a58aaee81bdeb759f46d969c26d7aa7810"}, + {file = "Flask_Caching-1.11.1-py3-none-any.whl", hash = "sha256:36592812eec6cba86eca48bcda74eff24bfd6c8eaf6056ca0184474bb78c0dc4"}, +] + +[package.dependencies] +cachelib = "*" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" + +[package.source] +type = "git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "freezegun" +version = "1.4.0" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, + {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "google-api-core" +version = "2.17.1" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.28.1" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.20.0" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.62.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.62.1)"] + +[[package]] +name = "grpcio-status" +version = "1.62.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.1" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "9.2.2" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.26.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "minio" +version = "7.2.5" +description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" +optional = false +python-versions = "*" +files = [ + {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, + {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, +] + +[package.dependencies] +argon2-cffi = "*" +certifi = "*" +pycryptodome = "*" +typing-extensions = "*" +urllib3 = "*" + +[[package]] +name = "more-itertools" +version = "10.2.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.8" +files = [ + {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"}, + {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "paramiko" +version = "3.4.0" +description = "SSH2 protocol library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "paramiko-3.4.0-py3-none-any.whl", hash = "sha256:43f0b51115a896f9c00f59618023484cb3a14b98bbceab43394a39c6739b7ee7"}, + {file = "paramiko-3.4.0.tar.gz", hash = "sha256:aac08f26a31dc4dffd92821527d1682d99d52f9ef6851968114a8728f3c274d3"}, +] + +[package.dependencies] +bcrypt = ">=3.2" +cryptography = ">=3.3" +pynacl = ">=1.5" + +[package.extras] +all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +invoke = ["invoke (>=2.0)"] + +[[package]] +name = "pay_api" +version = "0.0.0" +description = "A short description of the project" +optional = false +python-versions = ">=3.12" +files = [] +develop = false + +[package.dependencies] +cattrs = "*" +croniter = "*" +cryptography = "*" +dpath = "*" +Flask = "*" +Flask-Caching = "*" +Flask-Cors = "*" +flask-jwt-oidc = "*" +flask-marshmallow = "*" +Flask-Migrate = "*" +Flask-Moment = "*" +Flask-Script = "*" +Flask-SQLAlchemy = "*" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +gunicorn = "*" +holidays = "0.37" +itsdangerous = "*" +jaeger-client = "*" +Jinja2 = "*" +jsonschema = "4.17.3" +launchdarkly-server-sdk = "*" +marshmallow-sqlalchemy = "*" +psycopg2-binary = "*" +pyhumps = "*" +python-dotenv = "*" +requests = "*" +sentry-sdk = {version = "*", extras = ["flask"]} +sqlalchemy = "*" +sqlalchemy_utils = "*" +Werkzeug = "*" + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "18263" +resolved_reference = "fe42bf81c86c77946a1df238f69f12ad6b83d804" +subdirectory = "pay-api" + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pycryptodome" +version = "3.20.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, + {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pysftp" +version = "0.2.9" +description = "A friendly face on SFTP" +optional = false +python-versions = "*" +files = [ + {file = "pysftp-0.2.9.tar.gz", hash = "sha256:fbf55a802e74d663673400acd92d5373c1c7ee94d765b428d9f977567ac4854a"}, +] + +[package.dependencies] +paramiko = ">=1.17" + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.5.post1" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, + {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.12.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +subdirectory = "python" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.42.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, + {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, +] + +[package.dependencies] +blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} +certifi = "*" +flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} +markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "werkzeug" +version = "3.0.1" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "f9361d9a2eb005b26cfdd2bf2a3dd01ef810f515f50061722bf54b3d436978c2" diff --git a/jobs/payment-jobs/pyproject.toml b/jobs/payment-jobs/pyproject.toml new file mode 100644 index 000000000..23ab857ba --- /dev/null +++ b/jobs/payment-jobs/pyproject.toml @@ -0,0 +1,60 @@ +[tool.poetry] +name = "payment-jobs" +version = "0.1.0" +description = "" +authors = ["Travis Semple "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "18263", subdirectory = "pay-api"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +gunicorn = "^21.2.0" +flask = "^3.0.2" +flask-sqlalchemy = "^3.1.1" +sqlalchemy = "^2.0.28" +flask-marshmallow = "^1.2.0" +marshmallow-sqlalchemy = "^1.0.0" +python-dotenv = "^1.0.1" +psycopg2-binary = "^2.9.9" +jsonschema = "4.17.3" +requests = "^2.31.0" +werkzeug = "^3.0.1" +jaeger-client = "^4.8.0" +minio = "^7.2.5" +pysftp = "^0.2.9" +flask-migrate = "^4.0.7" +itsdangerous = "^2.1.2" +dataclass-wizard = "^0.22.3" +launchdarkly-server-sdk = "^9.2.2" +cx-oracle = "^8.3.0" +more-itertools = "^10.2.0" + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +faker = "^24.2.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +freezegun = "^1.4.0" +lovely-pytest-docker = "^0.3.1" +pytest-asyncio = "^0.23.5.post1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/jobs/payment-jobs/requirements.txt b/jobs/payment-jobs/requirements.txt deleted file mode 100644 index 0210163c4..000000000 --- a/jobs/payment-jobs/requirements.txt +++ /dev/null @@ -1,89 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git@5f99e135214ae949c9af951d4aa0b88b1067d853#egg=sbc_common_components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@5bf7ab87481a96a72c59db7d6a88c32f712f1ab6#egg=pay_api&subdirectory=pay-api --e git+https://github.com/thorwolpert/flask-jwt-oidc.git@40cc811ccf70e838c5f7522fe8d83b7e58853539#egg=flask_jwt_oidc -Flask-Caching==2.1.0 -Flask-Cors==4.0.0 -Flask-Migrate==4.0.7 -Flask-Moment==1.0.5 -Flask-OpenTracing==1.1.0 -Flask-SQLAlchemy==3.1.1 -Flask-Script==2.0.6 -Flask==3.0.2 -Jinja2==3.1.3 -Mako==1.3.2 -MarkupSafe==2.1.5 -PyNaCl==1.5.0 -SQLAlchemy-Utils==0.41.1 -SQLAlchemy==2.0.28 -Werkzeug==3.0.1 -alembic==1.13.1 -argon2-cffi-bindings==21.2.0 -argon2-cffi==23.1.0 -attrs==23.2.0 -bcrypt==4.1.2 -blinker==1.7.0 -cachelib==0.9.0 -cachetools==5.3.3 -cattrs==23.2.3 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -croniter==2.0.2 -cryptography==42.0.5 -cx_Oracle==8.3.0 -dataclass-wizard==0.22.3 -dpath==2.1.6 -ecdsa==0.18.0 -expiringdict==1.2.2 -flask-marshmallow==1.2.0 -google-api-core==2.17.1 -google-auth==2.28.1 -google-cloud-pubsub==2.20.0 -googleapis-common-protos==1.63.0 -greenlet==3.0.3 -grpc-google-iam-v1==0.13.0 -grpcio-status==1.62.1 -grpcio==1.62.1 -gunicorn==21.2.0 -holidays==0.37 -idna==3.6 -itsdangerous==2.1.2 -jaeger-client==4.8.0 -jsonschema==4.17.3 -launchdarkly-eventsource==1.1.1 -launchdarkly-server-sdk==9.2.2 -marshmallow-sqlalchemy==1.0.0 -marshmallow==3.21.1 -minio==7.2.5 -more-itertools==10.2.0 -opentracing==2.4.0 -packaging==24.0 -paramiko==3.4.0 -proto-plus==1.23.0 -protobuf==4.25.3 -psycopg2-binary==2.9.9 -pyRFC3339==1.1 -pyasn1-modules==0.3.0 -pyasn1==0.5.1 -pycparser==2.21 -pycryptodome==3.20.0 -pyhumps==3.8.0 -pyrsistent==0.20.0 -pysftp==0.2.9 -python-dateutil==2.9.0.post0 -python-dotenv==1.0.1 -python-jose==3.3.0 -pytz==2024.1 -requests==2.31.0 -rsa==4.9 -semver==3.0.2 -sentry-sdk==1.41.0 -simple-cloudevent @ git+https://github.com/daxiom/simple-cloudevent.py.git@447cabb988202206ac69e71177d7cd11b6c0b002 -six==1.16.0 -strict-rfc3339==0.7 -threadloop==1.0.2 -thrift==0.16.0 -tornado==6.4 -typing_extensions==4.10.0 -urllib3==2.2.1 diff --git a/jobs/payment-jobs/requirements/bcregistry-libraries.txt b/jobs/payment-jobs/requirements/bcregistry-libraries.txt deleted file mode 100644 index 06d616dd6..000000000 --- a/jobs/payment-jobs/requirements/bcregistry-libraries.txt +++ /dev/null @@ -1,4 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@18263#egg=pay-api&subdirectory=pay-api --e git+https://github.com/thorwolpert/flask-jwt-oidc.git#egg=flask-jwt-oidc -git+https://github.com/daxiom/simple-cloudevent.py.git diff --git a/jobs/payment-jobs/requirements/dev.txt b/jobs/payment-jobs/requirements/dev.txt deleted file mode 100644 index f84689dbe..000000000 --- a/jobs/payment-jobs/requirements/dev.txt +++ /dev/null @@ -1,28 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov -Faker - -# Lint and code style -flake8 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -FreezeGun - -# docker -lovely-pytest-docker -pytest-asyncio diff --git a/jobs/payment-jobs/requirements/prod.txt b/jobs/payment-jobs/requirements/prod.txt deleted file mode 100644 index 06a70bb0d..000000000 --- a/jobs/payment-jobs/requirements/prod.txt +++ /dev/null @@ -1,20 +0,0 @@ -gunicorn -Flask -Flask-SQLAlchemy -SQLAlchemy -flask-marshmallow -marshmallow-sqlalchemy -python-dotenv -psycopg2-binary -jsonschema==4.17.3 -requests -Werkzeug -jaeger-client -minio -pysftp -Flask-Migrate -itsdangerous -dataclass_wizard -launchdarkly-server-sdk -cx_Oracle -more_itertools diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index 1f0b72fa5..0be3475be 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -24,7 +24,7 @@ from pay_api.utils.constants import RECEIPT_METHOD_EFT_MONTHLY, RECEIPT_METHOD_PAD_DAILY from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, PaymentMethod from sentry_sdk import capture_message -from services import eft_service, routing_slip +from services import routing_slip from utils import mailer from utils.auth import get_token @@ -97,8 +97,8 @@ def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: Paym if pay_account.payment_method == PaymentMethod.EFT.value: cfs_account_details = CFSService.create_cfs_account(identifier=pay_account.auth_account_id, - contact_info=contact_info, - receipt_method=RECEIPT_METHOD_EFT_MONTHLY) + contact_info=contact_info, + receipt_method=RECEIPT_METHOD_EFT_MONTHLY) elif pending_account.cfs_account and pending_account.cfs_party and pending_account.cfs_site: # This means, PAD account details have changed. So update banking details for this CFS account bank_details = CFSService.update_bank_details(name=pay_account.auth_account_id, diff --git a/jobs/payment-jobs/tests/jobs/conftest.py b/jobs/payment-jobs/tests/jobs/conftest.py index cb217630a..0f6bcfeb3 100644 --- a/jobs/payment-jobs/tests/jobs/conftest.py +++ b/jobs/payment-jobs/tests/jobs/conftest.py @@ -15,7 +15,6 @@ """Common setup and fixtures for the py-test suite used by this service.""" import os -import sys import time import pytest @@ -57,16 +56,13 @@ def client_ctx(app): def db(app): # pylint: disable=redefined-outer-name, invalid-name """Return a session-wide initialised database.""" with app.app_context(): - # even though this isn't referenced directly, it sets up the internal configs that upgrade needs - migrations_path = [folder for folder in sys.path if 'pay-api/pay-api' in folder] - if len(migrations_path) > 0: - migrations_path = migrations_path[0].replace('/pay-api/src', '/pay-api/migrations') - if database_exists(_db.engine.url): drop_database(_db.engine.url) create_database(_db.engine.url) _db.session().execute(text('SET TIME ZONE "UTC";')) - Migrate(app, _db, directory=migrations_path) + pay_api_dir = os.path.abspath('..').replace('jobs', 'pay-api') + pay_api_dir = os.path.join(pay_api_dir, 'migrations') + Migrate(app, _db, directory=pay_api_dir) upgrade() # Restore the logging, alembic and sqlalchemy have their own logging from alembic.ini. setup_logging(os.path.abspath('logging.conf')) diff --git a/pay-admin/Dockerfile b/pay-admin/Dockerfile index a6fbe5388..9b1480f8c 100644 --- a/pay-admin/Dockerfile +++ b/pay-admin/Dockerfile @@ -1,33 +1,88 @@ -FROM python:3.12.2-bullseye +FROM python:3.12.2-bullseye as development_build ARG VCS_REF="missing" ARG BUILD_DATE="missing" ENV VCS_REF=${VCS_REF} ENV BUILD_DATE=${BUILD_DATE} +ENV PORT=8080 LABEL org.label-schema.vcs-ref=${VCS_REF} \ org.label-schema.build-date=${BUILD_DATE} USER root -# Create working directory -RUN mkdir /opt/app-root && chmod 755 /opt/app-root -WORKDIR /opt/app-root +LABEL maintainer="travissemple" +LABEL vendor="BCROS" -# Install the requirements -COPY ./requirements.txt . +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.3.2 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' -COPY . . +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] -RUN pip install . +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* -USER 1001 +WORKDIR /code -# Set Python path -ENV PYTHONPATH=/opt/app-root/src +RUN groupadd -g "${GID}" -r web \ + && useradd -d '/code' -g web -l -r -u "${UID}" web \ + && chown web:web -R '/code' -ENTRYPOINT ["bash", "docker-entrypoint.sh"] +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml /code/ + +COPY --chown=web:web ./admin /code/admin +COPY --chown=web:web ./README.md /code + +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + # Install deps: + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +# Running as non-root user: +USER web + +# The following stage is only for production: +FROM development_build AS production_build +COPY --chown=web:web . /code + +CMD gunicorn --bind 0.0.0.0:${PORT} --config /code/gunicorn_config.py wsgi:app diff --git a/pay-admin/Makefile b/pay-admin/Makefile index 237377444..c8bffa188 100755 --- a/pay-admin/Makefile +++ b/pay-admin/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=pay-admin ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,26 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip freeze | sort > requirements.txt ;\ - cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . - +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -63,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg $(PROJECT_NAME) + poetry run pylint --rcfile=setup.cfg $(PROJECT_NAME) flake8: ## Linting with flake8 - . venv/bin/activate && flake8 $(PROJECT_NAME) tests + poetry run flake8 $(PROJECT_NAME) tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html @@ -132,7 +115,7 @@ tag: push ## tag image # COMMANDS - Local # ################################################################################# run: ## Run the project in local - . venv/bin/activate && python -m flask run -p 5000 + poetry run flask run -p 5000 ################################################################################# # Self Documenting Commands # diff --git a/pay-admin/poetry.lock b/pay-admin/poetry.lock new file mode 100644 index 000000000..38966f8e4 --- /dev/null +++ b/pay-admin/poetry.lock @@ -0,0 +1,2433 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "authlib" +version = "1.3.0" +description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Authlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:9637e4de1fb498310a56900b3e2043a206b03cb11c05422014b0302cbc814be3"}, + {file = "Authlib-1.3.0.tar.gz", hash = "sha256:959ea62a5b7b5123c5059758296122b57cd2585ae2ed1c0622c21b371ffdae06"}, +] + +[package.dependencies] +cryptography = "*" + +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" + +[[package]] +name = "blinker" +version = "1.7.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "cachelib" +version = "0.9.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-admin" +version = "1.6.1" +description = "Simple and extensible admin interface framework for Flask" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Admin-1.6.1.tar.gz", hash = "sha256:24cae2af832b6a611a01d7dc35f42d266c1d6c75a426b869d8cb241b78233369"}, + {file = "Flask_Admin-1.6.1-py3-none-any.whl", hash = "sha256:fd8190f1ec3355913a22739c46ed3623f1d82b8112cde324c60a6fc9b21c9406"}, +] + +[package.dependencies] +Flask = ">=0.7" +wtforms = "*" + +[package.extras] +aws = ["boto"] +azure = ["azure-storage-blob"] + +[[package]] +name = "flask-caching" +version = "2.1.0" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, +] + +[package.dependencies] +cachelib = ">=0.9.0,<0.10.0" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" + +[package.source] +type = "git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-oidc" +version = "2.1.1" +description = "OpenID Connect extension for Flask" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "flask_oidc-2.1.1-py3-none-any.whl", hash = "sha256:379be8fa15c275a2cbbb0e179eb1e937d3c9cf0788f39bad047cdd99f40613b1"}, + {file = "flask_oidc-2.1.1.tar.gz", hash = "sha256:9a283cf760f9d3053ef71280705fbefeb62d1baa0fdceb4a99784ab01ba8e307"}, +] + +[package.dependencies] +authlib = ">=1.2.0,<2.0.0" +flask = ">=2.0.0,<4.0.0" +requests = ">=2.24.0,<3.0.0" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "freezegun" +version = "1.4.0" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, + {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "google-api-core" +version = "2.17.1" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.28.1" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.20.0" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.62.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.62.1)"] + +[[package]] +name = "grpcio-status" +version = "1.62.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.1" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, + {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, +] + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "9.2.2" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.26.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pay_api" +version = "0.0.0" +description = "A short description of the project" +optional = false +python-versions = ">=3.12" +files = [] +develop = false + +[package.dependencies] +cattrs = "*" +croniter = "*" +cryptography = "*" +dpath = "*" +Flask = "*" +Flask-Caching = "*" +Flask-Cors = "*" +flask-jwt-oidc = "*" +flask-marshmallow = "*" +Flask-Migrate = "*" +Flask-Moment = "*" +Flask-Script = "*" +Flask-SQLAlchemy = "*" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +gunicorn = "*" +holidays = "0.37" +itsdangerous = "*" +jaeger-client = "*" +Jinja2 = "*" +jsonschema = "4.17.3" +launchdarkly-server-sdk = "*" +marshmallow-sqlalchemy = "*" +psycopg2-binary = "*" +pyhumps = "*" +python-dotenv = "*" +requests = "*" +sentry-sdk = {version = "*", extras = ["flask"]} +sqlalchemy = "*" +sqlalchemy_utils = "*" +Werkzeug = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-pay.git" +reference = "feature-queue-python-upgrade" +resolved_reference = "06673215d07ac896fdc84e134f424de4d89c87b5" +subdirectory = "pay-api" + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.12.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-futures" +version = "1.0.1" +description = "Asynchronous Python HTTP for Humans." +optional = false +python-versions = "*" +files = [ + {file = "requests-futures-1.0.1.tar.gz", hash = "sha256:f55a4ef80070e2858e7d1e73123d2bfaeaf25b93fd34384d8ddf148e2b676373"}, + {file = "requests_futures-1.0.1-py2.py3-none-any.whl", hash = "sha256:4a2f5472e9911a79532137d156aa937cd9cd90fec55677f71b2976d1f7a66d38"}, +] + +[package.dependencies] +requests = ">=1.2.0" + +[package.extras] +dev = ["black (>=22.3.0)", "build (>=0.7.0)", "isort (>=5.11.4)", "pyflakes (>=2.2.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "pytest-network (>=0.0.1)", "readme-renderer[rst] (>=26.0)", "twine (>=3.4.2)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +subdirectory = "python" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.42.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, + {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, +] + +[package.dependencies] +blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} +certifi = "*" +flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} +markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "werkzeug" +version = "3.0.1" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[[package]] +name = "wtforms" +version = "3.1.2" +description = "Form validation and rendering for Python web development." +optional = false +python-versions = ">=3.8" +files = [ + {file = "wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07"}, + {file = "wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9"}, +] + +[package.dependencies] +markupsafe = "*" + +[package.extras] +email = ["email-validator"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "89b1ff4a2b3b813184e1f2bd3dbe358134e0dc202139e7521f843d223503b850" diff --git a/pay-admin/pyproject.toml b/pay-admin/pyproject.toml new file mode 100644 index 000000000..d0aac6299 --- /dev/null +++ b/pay-admin/pyproject.toml @@ -0,0 +1,54 @@ +[tool.poetry] +name = "pay-admin" +version = "0.1.0" +description = "" +authors = ["Travis Semple "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +gunicorn = "^21.2.0" +flask = "^3.0.2" +flask-admin = "^1.6.1" +flask-oidc = "^2.1.1" +flask-sqlalchemy = "^3.1.1" +httplib2 = "^0.22.0" +psycopg2-binary = "^2.9.9" +python-dotenv = "^1.0.1" +requests = "^2.31.0" +requests-futures = "^1.0.1" +wtforms = "^3.1.2" +werkzeug = "^3.0.1" +sqlalchemy = "^2.0.28" +itsdangerous = "^2.1.2" +jinja2 = "^3.1.3" +pay-api = {git = "https://github.com/bcgov/sbc-pay.git", rev = "feature-queue-python-upgrade", subdirectory = "pay-api"} +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +freezegun = "^1.4.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +pydocstyle = "^6.3.0" +isort = "^5.13.2" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pay-admin/requirements/dev.txt b/pay-admin/requirements/dev.txt deleted file mode 100644 index b40ddf1cb..000000000 --- a/pay-admin/requirements/dev.txt +++ /dev/null @@ -1,24 +0,0 @@ --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov -FreezeGun - -# Lint and code style -flake8 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -pydocstyle -isort diff --git a/pay-admin/requirements/prod.txt b/pay-admin/requirements/prod.txt deleted file mode 100644 index 20354853f..000000000 --- a/pay-admin/requirements/prod.txt +++ /dev/null @@ -1,15 +0,0 @@ -gunicorn -Flask -Flask-Admin -Flask-OIDC -Flask-SQLAlchemy -httplib2 -psycopg2-binary -python-dotenv -requests -requests-futures -wtforms -Werkzeug -sqlalchemy -itsdangerous -Jinja2 diff --git a/pay-admin/requirements/repo-libraries.txt b/pay-admin/requirements/repo-libraries.txt deleted file mode 100644 index 3cffb3f2b..000000000 --- a/pay-admin/requirements/repo-libraries.txt +++ /dev/null @@ -1,4 +0,0 @@ --e git+https://github.com/bcgov/sbc-pay.git@feature-queue-python-upgrade#egg=pay-api&subdirectory=pay-api --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -git+https://github.com/daxiom/simple-cloudevent.py.git -git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-api/Dockerfile b/pay-api/Dockerfile index a6fbe5388..25fcc6c90 100644 --- a/pay-api/Dockerfile +++ b/pay-api/Dockerfile @@ -1,33 +1,88 @@ -FROM python:3.12.2-bullseye +FROM python:3.12.2-bullseye as development_build ARG VCS_REF="missing" ARG BUILD_DATE="missing" ENV VCS_REF=${VCS_REF} ENV BUILD_DATE=${BUILD_DATE} +ENV PORT=8080 LABEL org.label-schema.vcs-ref=${VCS_REF} \ org.label-schema.build-date=${BUILD_DATE} USER root -# Create working directory -RUN mkdir /opt/app-root && chmod 755 /opt/app-root -WORKDIR /opt/app-root +LABEL maintainer="travissemple" +LABEL vendor="BCROS" -# Install the requirements -COPY ./requirements.txt . +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.3.2 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' -COPY . . +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] -RUN pip install . +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* -USER 1001 +WORKDIR /code -# Set Python path -ENV PYTHONPATH=/opt/app-root/src +RUN groupadd -g "${GID}" -r web \ + && useradd -d '/code' -g web -l -r -u "${UID}" web \ + && chown web:web -R '/code' -ENTRYPOINT ["bash", "docker-entrypoint.sh"] +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml /code/ + +COPY --chown=web:web ./src /code/src +COPY --chown=web:web ./README.md /code + +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + # Install deps: + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +# Running as non-root user: +USER web + +# The following stage is only for production: +FROM development_build AS production_build +COPY --chown=web:web . /code + +CMD gunicorn --bind 0.0.0.0:${PORT} --config /code/gunicorn_config.py wsgi:app diff --git a/pay-api/Makefile b/pay-api/Makefile index 7c2976754..03af18905 100755 --- a/pay-api/Makefile +++ b/pay-api/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=pay-api ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,25 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip freeze | sort > requirements.txt ;\ - cat requirements/repo-libraries.txt >> requirements.txt ;\ - pip install -Ur requirements/repo-libraries.txt - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -62,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME) + poetry run pylint --rcfile=setup.cfg src/$(PROJECT_NAME) flake8: ## Linting with flake8 - . venv/bin/activate && flake8 src/$(PROJECT_NAME) tests + poetry run flake8 src/$(PROJECT_NAME) tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html @@ -131,7 +115,7 @@ tag: push ## tag image # COMMANDS - Local # ################################################################################# run: ## Run the project in local - . venv/bin/activate && python -m flask run -p 5000 + poetry run flask run -p 5000 ################################################################################# # Self Documenting Commands # diff --git a/pay-api/docker-entrypoint.sh b/pay-api/docker-entrypoint.sh deleted file mode 100755 index 0cf2a64b2..000000000 --- a/pay-api/docker-entrypoint.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -echo 'starting application' -gunicorn --bind 0.0.0.0:8080 --config /opt/app-root/gunicorn_config.py wsgi:application diff --git a/pay-api/poetry.lock b/pay-api/poetry.lock new file mode 100644 index 000000000..abeb53a08 --- /dev/null +++ b/pay-api/poetry.lock @@ -0,0 +1,2315 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" + +[[package]] +name = "blinker" +version = "1.7.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "cachelib" +version = "0.9.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "faker" +version = "24.2.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Faker-24.2.0-py3-none-any.whl", hash = "sha256:dce4754921f9fa7e2003c26834093361b8f45072e0f46f172d6ca1234774ecd4"}, + {file = "Faker-24.2.0.tar.gz", hash = "sha256:87d5e7730426e7b36817921679c4eaf3d810cedb8c81194f47adc3df2122ca18"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-caching" +version = "2.1.0" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, +] + +[package.dependencies] +cachelib = ">=0.9.0,<0.10.0" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" + +[package.source] +type = "git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "freezegun" +version = "1.4.0" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, + {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "google-api-core" +version = "2.17.1" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.28.1" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.20.0" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.62.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.62.1)"] + +[[package]] +name = "grpcio-status" +version = "1.62.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.1" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "9.2.2" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.26.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.18.3" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-asyncio-0.18.3.tar.gz", hash = "sha256:7659bdb0a9eb9c6e3ef992eef11a2b3e69697800ad02fb06374a210d85b29f91"}, + {file = "pytest_asyncio-0.18.3-1-py3-none-any.whl", hash = "sha256:16cf40bdf2b4fb7fc8e4b82bd05ce3fbcd454cbf7b92afc445fe299dabb88213"}, + {file = "pytest_asyncio-0.18.3-py3-none-any.whl", hash = "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84"}, +] + +[package.dependencies] +pytest = ">=6.1.0" + +[package.extras] +testing = ["coverage (==6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (==0.931)", "pytest-trio (>=0.7.0)"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.12.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +subdirectory = "python" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.41.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, + {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, +] + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "werkzeug" +version = "3.0.1" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "0c5418d826d95e899b3996724ab8887f08935df2e03e11a289d5f8d9e022b3e2" diff --git a/pay-api/pyproject.toml b/pay-api/pyproject.toml new file mode 100644 index 000000000..3abc57aad --- /dev/null +++ b/pay-api/pyproject.toml @@ -0,0 +1,113 @@ +[tool.poetry] +name = "pay-api" +version = "0.1.0" +description = "" +authors = ["BC Registries and Online Services"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +flask-caching = "2.1.0" +flask-cors = "4.0.0" +flask-migrate = "4.0.7" +flask-moment = "1.0.5" +flask-sqlalchemy = "3.1.1" +flask-script = "2.0.6" +flask = "3.0.2" +jinja2 = "3.1.3" +mako = "1.3.2" +markupsafe = "2.1.5" +sqlalchemy-utils = "0.41.1" +sqlalchemy = "2.0.28" +werkzeug = "3.0.1" +alembic = "1.13.1" +attrs = "23.2.0" +blinker = "1.7.0" +cachelib = "0.9.0" +cachetools = "5.3.3" +cattrs = "23.2.3" +certifi = "2024.2.2" +cffi = "1.16.0" +charset-normalizer = "3.3.2" +click = "8.1.7" +croniter = "2.0.2" +cryptography = "42.0.5" +dpath = "2.1.6" +ecdsa = "0.18.0" +expiringdict = "1.2.2" +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +flask-marshmallow = "1.2.0" +google-api-core = "2.17.1" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +googleapis-common-protos = "1.63.0" +greenlet = "3.0.3" +grpc-google-iam-v1 = "0.13.0" +grpcio-status = "1.62.1" +grpcio = "1.62.1" +gunicorn = "21.2.0" +holidays = "0.37" +idna = "3.6" +itsdangerous = "2.1.2" +jaeger-client = "4.8.0" +jsonschema = "4.17.3" +launchdarkly-eventsource = "1.1.1" +launchdarkly-server-sdk = "9.2.2" +marshmallow-sqlalchemy = "1.0.0" +marshmallow = "3.21.1" +opentracing = "2.4.0" +packaging = "24.0" +proto-plus = "1.23.0" +protobuf = "4.25.3" +psycopg2-binary = "2.9.9" +pyrfc3339 = "1.1" +pyasn1-modules = "0.3.0" +pyasn1 = "0.5.1" +pycparser = "2.21" +pyhumps = "3.8.0" +pyrsistent = "0.20.0" +python-dateutil = "2.9.0.post0" +python-dotenv = "1.0.1" +python-jose = "3.3.0" +pytz = "2024.1" +requests = "2.31.0" +rsa = "4.9" +semver = "3.0.2" +sentry-sdk = "1.41.0" +six = "1.16.0" +threadloop = "1.0.2" +thrift = "0.16.0" +tornado = "6.4" +typing-extensions = "4.10.0" +urllib3 = "2.2.1" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +freezegun = "^1.4.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +pydocstyle = "^6.3.0" +isort = "^5.13.2" +lovely-pytest-docker = "^0.3.1" +pytest-asyncio = "0.18.3" +faker = "^24.2.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pay-api/requirements.txt b/pay-api/requirements.txt deleted file mode 100644 index 41d1229bb..000000000 --- a/pay-api/requirements.txt +++ /dev/null @@ -1,76 +0,0 @@ -Flask-Caching==2.1.0 -Flask-Cors==4.0.0 -Flask-Migrate==4.0.7 -Flask-Moment==1.0.5 -Flask-SQLAlchemy==3.1.1 -Flask-Script==2.0.6 -Flask==3.0.2 -Jinja2==3.1.3 -Mako==1.3.2 -MarkupSafe==2.1.5 -SQLAlchemy-Utils==0.41.1 -SQLAlchemy==2.0.28 -Werkzeug==3.0.1 -alembic==1.13.1 -attrs==23.2.0 -blinker==1.7.0 -cachelib==0.9.0 -cachetools==5.3.3 -cattrs==23.2.3 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -croniter==2.0.2 -cryptography==42.0.5 -dpath==2.1.6 -ecdsa==0.18.0 -expiringdict==1.2.2 -flask-jwt-oidc==0.3.0 -flask-marshmallow==1.2.0 -google-api-core==2.17.1 -google-auth==2.28.1 -google-cloud-pubsub==2.20.0 -googleapis-common-protos==1.63.0 -greenlet==3.0.3 -grpc-google-iam-v1==0.13.0 -grpcio-status==1.62.1 -grpcio==1.62.1 -gunicorn==21.2.0 -holidays==0.37 -idna==3.6 -itsdangerous==2.1.2 -jaeger-client==4.8.0 -jsonschema==4.17.3 -launchdarkly-eventsource==1.1.1 -launchdarkly-server-sdk==9.2.2 -marshmallow-sqlalchemy==1.0.0 -marshmallow==3.21.1 -opentracing==2.4.0 -packaging==24.0 -proto-plus==1.23.0 -protobuf==4.25.3 -psycopg2-binary==2.9.9 -pyRFC3339==1.1 -pyasn1-modules==0.3.0 -pyasn1==0.5.1 -pycparser==2.21 -pyhumps==3.8.0 -pyrsistent==0.20.0 -python-dateutil==2.9.0.post0 -python-dotenv==1.0.1 -python-jose==3.3.0 -pytz==2024.1 -requests==2.31.0 -rsa==4.9 -semver==3.0.2 -sentry-sdk==1.41.0 -six==1.16.0 -threadloop==1.0.2 -thrift==0.16.0 -tornado==6.4 -typing_extensions==4.10.0 -urllib3==2.2.1 --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -git+https://github.com/daxiom/simple-cloudevent.py.git -git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-api/requirements/dev.txt b/pay-api/requirements/dev.txt deleted file mode 100755 index 0938b0432..000000000 --- a/pay-api/requirements/dev.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov -FreezeGun - -# Lint and code style -flake8 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -pydocstyle -isort - -# docker -lovely-pytest-docker -pytest-asyncio==0.18.3 - -Faker diff --git a/pay-api/requirements/prod.txt b/pay-api/requirements/prod.txt deleted file mode 100644 index 9c4c234fc..000000000 --- a/pay-api/requirements/prod.txt +++ /dev/null @@ -1,31 +0,0 @@ -gunicorn -Flask -Flask-Caching -Flask-Cors -Flask-Migrate -Flask-Script -Flask-Moment -Flask-SQLAlchemy -flask-marshmallow -flask-jwt-oidc -python-dotenv -psycopg2-binary -marshmallow-sqlalchemy -jsonschema==4.17.3 -requests -croniter -sentry-sdk[flask] -cattrs -jaeger-client -dpath -Werkzeug -cryptography -sqlalchemy_utils -sqlalchemy -itsdangerous -Jinja2 -launchdarkly-server-sdk -holidays==0.37 -google-auth==2.28.1 -google-cloud-pubsub==2.20.0 -pyhumps diff --git a/pay-api/requirements/repo-libraries.txt b/pay-api/requirements/repo-libraries.txt deleted file mode 100644 index c1903a023..000000000 --- a/pay-api/requirements/repo-libraries.txt +++ /dev/null @@ -1,3 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -git+https://github.com/daxiom/simple-cloudevent.py.git -git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-queue/Dockerfile b/pay-queue/Dockerfile index 525967f9c..c857284c1 100644 --- a/pay-queue/Dockerfile +++ b/pay-queue/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12.2-bullseye +FROM python:3.12.2-bullseye as development_build ARG VCS_REF="missing" ARG BUILD_DATE="missing" @@ -11,26 +11,77 @@ LABEL org.label-schema.vcs-ref=${VCS_REF} \ USER root -# Create working directory -RUN mkdir /opt/app-root && chmod 755 /opt/app-root -WORKDIR /opt/app-root +LABEL maintainer="travissemple" +LABEL vendor="BCROS" -# Install the requirements -COPY ./requirements.txt . +ARG APP_ENV \ + # Needed for fixing permissions of files created by Docker: + UID=1000 \ + GID=1000 -#RUN pip install --upgrade pip -RUN pip install pip==24.0.0 -RUN pip install --no-cache-dir -r requirements.txt +ENV APP_ENV=${APP_ENV} \ + # python: + PYTHONFAULTHANDLER=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONHASHSEED=random \ + PYTHONDONTWRITEBYTECODE=1 \ + # pip: + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_DEFAULT_TIMEOUT=100 \ + PIP_ROOT_USER_ACTION=ignore \ + # poetry: + POETRY_VERSION=1.3.2 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false \ + POETRY_CACHE_DIR='/var/cache/pypoetry' \ + POETRY_HOME='/usr/local' -COPY . . +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] -RUN pip install . +RUN apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + gettext \ + git \ + libpq-dev \ + wait-for-it \ + && curl -sSL 'https://install.python-poetry.org' | python - \ + && poetry --version \ + # Cleaning cache: + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* -USER 1001 +WORKDIR /code -# Set Python path -ENV PYTHONPATH=/opt/app-root/src +RUN groupadd -g "${GID}" -r web \ + && useradd -d '/code' -g web -l -r -u "${UID}" web \ + && chown web:web -R '/code' -#EXPOSE 8080 +# Copy only requirements, to cache them in docker layer +COPY --chown=web:web ./poetry.lock ./pyproject.toml /code/ -CMD [ "python", "/opt/app-root/app.py" ] +COPY --chown=web:web ./src /code/src +COPY --chown=web:web ./README.md /code + +# Project initialization: +RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \ + echo "$APP_ENV" \ + && poetry version \ + # Install deps: + && poetry run pip install -U pip \ + && poetry install \ + $(if [ -z ${APP_ENV+x} ] | [ "$APP_ENV" = 'production' ]; then echo '--only main'; fi) \ + --no-interaction --no-ansi + +# Running as non-root user: +USER web + +# The following stage is only for production: +FROM development_build AS production_build +COPY --chown=web:web . /code + +CMD [ "python", "/code/app.py" ] diff --git a/pay-queue/Makefile b/pay-queue/Makefile index 307e09ca3..101a2de04 100644 --- a/pay-queue/Makefile +++ b/pay-queue/Makefile @@ -12,7 +12,7 @@ DOCKER_NAME:=pay-queue ################################################################################# # COMMANDS -- Setup # ################################################################################# -setup: install install-dev ## Setup the project +setup: install ## Setup the project clean: clean-build clean-pyc clean-test ## Clean the project rm -rf venv/ @@ -36,24 +36,9 @@ clean-test: ## clean test files rm -f .coverage rm -fr htmlcov/ -build-req: clean ## Upgrade requirements - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements/prod.txt ;\ - pip install -Ur requirements/bcregistry-libraries.txt ;\ - pip freeze | sort > requirements.txt ;\ - -install: clean ## Install python virtrual environment - test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ - . venv/bin/activate ;\ - pip install --upgrade pip ;\ - pip install -Ur requirements.txt - -install-dev: ## Install local application - . venv/bin/activate ; \ - pip install -Ur requirements/dev.txt; \ - pip install -e . +install: clean + pip install poetry ;\ + poetry install ################################################################################# # COMMANDS - CI # @@ -61,15 +46,15 @@ install-dev: ## Install local application ci: lint flake8 test ## CI flow pylint: ## Linting with pylint - . venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME) + poetry run pylint --rcfile=setup.cfg src/$(PROJECT_NAME) flake8: ## Linting with flake8 - . venv/bin/activate && flake8 src/$(PROJECT_NAME) tests + poetry run flake8 src/$(PROJECT_NAME) tests lint: pylint flake8 ## run all lint type scripts test: ## Unit testing - . venv/bin/activate && pytest + poetry run pytest mac-cov: test ## Run the coverage report and display in a browser window (mac) @open -a "Google Chrome" htmlcov/index.html diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock new file mode 100644 index 000000000..ce6f002bd --- /dev/null +++ b/pay-queue/poetry.lock @@ -0,0 +1,2566 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + +[[package]] +name = "argon2-cffi" +version = "23.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, + {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["argon2-cffi[tests,typing]", "tox (>4)"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] +tests = ["hypothesis", "pytest"] +typing = ["mypy"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" + +[[package]] +name = "blinker" +version = "1.7.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "cachecontrol" +version = "0.14.0" +description = "httplib2 caching for requests" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachecontrol-0.14.0-py3-none-any.whl", hash = "sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0"}, + {file = "cachecontrol-0.14.0.tar.gz", hash = "sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938"}, +] + +[package.dependencies] +msgpack = ">=0.5.2,<2.0.0" +requests = ">=2.16.0" + +[package.extras] +dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "furo", "mypy", "pytest", "pytest-cov", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"] +filecache = ["filelock (>=3.8.0)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "cachelib" +version = "0.9.0" +description = "A collection of cache libraries in the same API interface." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.3" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "croniter" +version = "2.0.2" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "croniter-2.0.2-py2.py3-none-any.whl", hash = "sha256:78bf110a2c7dbbfdd98b926318ae6c64a731a4c637c7befe3685755110834746"}, + {file = "croniter-2.0.2.tar.gz", hash = "sha256:8bff16c9af4ef1fb6f05416973b8f7cb54997c02f2f8365251f9bf1dded91866"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dpath" +version = "2.1.6" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dpath-2.1.6-py3-none-any.whl", hash = "sha256:31407395b177ab63ef72e2f6ae268c15e938f2990a8ecf6510f5686c02b6db73"}, + {file = "dpath-2.1.6.tar.gz", hash = "sha256:f1e07c72e8605c6a9e80b64bc8f42714de08a789c7de417e49c3f87a19692e47"}, +] + +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + +[[package]] +name = "flake8" +version = "7.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-isort" +version = "6.1.1" +description = "flake8 plugin that integrates isort" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, + {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, +] + +[package.dependencies] +flake8 = "*" +isort = ">=5.0.0,<6" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-caching" +version = "2.1.0" +description = "Adds caching support to Flask applications." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, +] + +[package.dependencies] +cachelib = ">=0.9.0,<0.10.0" +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" + +[package.source] +type = "git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +reference = "HEAD" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" + +[[package]] +name = "flask-marshmallow" +version = "1.2.0" +description = "Flask + marshmallow for beautiful APIs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_marshmallow-1.2.0-py3-none-any.whl", hash = "sha256:ddd2a7c8db5e00a8d56c8ca5f651efae1de7d76b7d821b56ccc2caf09135ad12"}, + {file = "flask_marshmallow-1.2.0.tar.gz", hash = "sha256:d0f79eb9743f0c530a3d9e848503e1f2228e6b35a819c91e913af02e68421805"}, +] + +[package.dependencies] +Flask = ">=2.2" +marshmallow = ">=3.0.0" + +[package.extras] +dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"] +sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"] +tests = ["flask-marshmallow[sqlalchemy]", "pytest"] + +[[package]] +name = "flask-migrate" +version = "4.0.7" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"}, + {file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-moment" +version = "1.0.5" +description = "Formatting of dates and times in Flask templates using moment.js." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Moment-1.0.5.tar.gz", hash = "sha256:33307ecd4af8290b6df6a9828ff55ac0977d0567817f9bc0f69803d22ed2b55c"}, + {file = "Flask_Moment-1.0.5-py3-none-any.whl", hash = "sha256:6e7b3eef89e2137bbbee975405f241a68a44edfa34bf052c92d84364992adca6"}, +] + +[package.dependencies] +Flask = "*" +packaging = ">=14.1" + +[[package]] +name = "flask-opentracing" +version = "1.1.0" +description = "OpenTracing support for Flask applications" +optional = false +python-versions = "*" +files = [ + {file = "Flask-OpenTracing-1.1.0.tar.gz", hash = "sha256:a9a39d367fbe7e9ed9c77b90ac48159c1a3e82982a5abf84d3f4d710d24580ac"}, +] + +[package.dependencies] +Flask = "*" +opentracing = ">=2.0,<3" + +[package.extras] +tests = ["flake8", "flake8-quotes", "mock", "pytest", "pytest-cov"] + +[[package]] +name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Script-2.0.6.tar.gz", hash = "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "freezegun" +version = "1.4.0" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, + {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "google-api-core" +version = "2.17.1" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.28.1" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-pubsub" +version = "2.20.0" +description = "Google Cloud Pub/Sub API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpcio = ">=1.51.3,<2.0dev" +grpcio-status = ">=1.33.2" +proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +libcst = ["libcst (>=0.3.10)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.0" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, + {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.62.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.62.1)"] + +[[package]] +name = "grpcio-status" +version = "1.62.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.1" +protobuf = ">=4.21.6" + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "holidays" +version = "0.37" +description = "Generate and work with holidays in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "holidays-0.37-py3-none-any.whl", hash = "sha256:5b8ff8c94c06e3b225762d495e51b8e51205d332f8ad092aab809c4bffa8d123"}, + {file = "holidays-0.37.tar.gz", hash = "sha256:712df71a8d97b04554fa1c9208d219fbf174bad2864263bef24c6dcfa1ded6ff"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jaeger-client" +version = "4.8.0" +description = "Jaeger Python OpenTracing Tracer implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaeger-client-4.8.0.tar.gz", hash = "sha256:3157836edab8e2c209bd2d6ae61113db36f7ee399e66b1dcbb715d87ab49bfe0"}, +] + +[package.dependencies] +opentracing = ">=2.1,<3.0" +threadloop = ">=1,<2" +thrift = "*" +tornado = ">=4.3" + +[package.extras] +tests = ["codecov", "coverage", "flake8", "flake8-quotes", "flake8-typing-imports", "mock", "mypy", "opentracing_instrumentation (>=3,<4)", "prometheus_client (==0.11.0)", "pycurl", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "pytest-localserver", "pytest-timeout", "pytest-tornado", "tchannel (==2.1.0)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "launchdarkly-eventsource" +version = "1.1.1" +description = "LaunchDarkly SSE Client" +optional = false +python-versions = ">=3.7" +files = [ + {file = "launchdarkly_eventsource-1.1.1-py3-none-any.whl", hash = "sha256:3d7e5301bc4b4a744ecdaa10de8bce52c2f3c66a97e9aa10ab11ca81b67fb31b"}, + {file = "launchdarkly_eventsource-1.1.1.tar.gz", hash = "sha256:211791f1267f9b7b0a62a0bb5fc9c5ed1fb4a834440f16be551968dbe772557a"}, +] + +[package.dependencies] +urllib3 = ">=1.26.0,<3" + +[[package]] +name = "launchdarkly-server-sdk" +version = "9.2.2" +description = "LaunchDarkly SDK for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, +] + +[package.dependencies] +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" +pyRFC3339 = ">=1.0" +semver = ">=2.10.2" +urllib3 = ">=1.26.0,<3" + +[package.extras] +consul = ["python-consul (>=1.0.1)"] +dynamodb = ["boto3 (>=1.9.71)"] +redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] + +[[package]] +name = "lovely-pytest-docker" +version = "0.3.1" +description = "Pytest testing utilities with docker containers." +optional = false +python-versions = "*" +files = [ + {file = "lovely-pytest-docker-0.3.1.tar.gz", hash = "sha256:4326a180bfd4dd4ad69c2ef3e3643c41075d965f40068488b40204602e6df85e"}, +] + +[package.dependencies] +pytest = "*" +six = "*" + +[[package]] +name = "mako" +version = "1.3.2" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-sqlalchemy" +version = "1.0.0" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df"}, + {file = "marshmallow_sqlalchemy-1.0.0.tar.gz", hash = "sha256:20a0f2fcdd5bddc86444fa01461f17f9b6a12a8ddd4ca8c9b34fe2f2e35d00a2"}, +] + +[package.dependencies] +marshmallow = ">=3.10.0" +SQLAlchemy = ">=1.4.40,<3.0" + +[package.extras] +dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)"] +tests = ["pytest (<8)", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "minio" +version = "7.2.5" +description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" +optional = false +python-versions = "*" +files = [ + {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, + {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, +] + +[package.dependencies] +argon2-cffi = "*" +certifi = "*" +pycryptodome = "*" +typing-extensions = "*" +urllib3 = "*" + +[[package]] +name = "msgpack" +version = "1.0.8" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +files = [ + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, + {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, + {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, + {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, + {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, + {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, + {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, + {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, + {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, + {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, + {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, +] + +[[package]] +name = "opentracing" +version = "2.4.0" +description = "OpenTracing API for Python. See documentation at http://opentracing.io" +optional = false +python-versions = "*" +files = [ + {file = "opentracing-2.4.0.tar.gz", hash = "sha256:a173117e6ef580d55874734d1fa7ecb6f3655160b8b8974a2a1e98e5ec9c840d"}, +] + +[package.extras] +tests = ["Sphinx", "doubles", "flake8", "flake8-quotes", "gevent", "mock", "pytest", "pytest-cov", "pytest-mock", "six (>=1.10.0,<2.0)", "sphinx_rtd_theme", "tornado"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pay_api" +version = "0.0.0" +description = "A short description of the project" +optional = false +python-versions = ">=3.12" +files = [] +develop = false + +[package.dependencies] +cattrs = "*" +croniter = "*" +cryptography = "*" +dpath = "*" +Flask = "*" +Flask-Caching = "*" +Flask-Cors = "*" +flask-jwt-oidc = "*" +flask-marshmallow = "*" +Flask-Migrate = "*" +Flask-Moment = "*" +Flask-Script = "*" +Flask-SQLAlchemy = "*" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +gunicorn = "*" +holidays = "0.37" +itsdangerous = "*" +jaeger-client = "*" +Jinja2 = "*" +jsonschema = "4.17.3" +launchdarkly-server-sdk = "*" +marshmallow-sqlalchemy = "*" +psycopg2-binary = "*" +pyhumps = "*" +python-dotenv = "*" +requests = "*" +sentry-sdk = {version = "*", extras = ["flask"]} +sqlalchemy = "*" +sqlalchemy_utils = "*" +Werkzeug = "*" + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "18263" +resolved_reference = "fe42bf81c86c77946a1df238f69f12ad6b83d804" +subdirectory = "pay-api" + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "proto-plus" +version = "1.23.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, + {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pycountry" +version = "23.12.11" +description = "ISO country, subdivision, language, currency and script definitions and their translations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycountry-23.12.11-py3-none-any.whl", hash = "sha256:2ff91cff4f40ff61086e773d61e72005fe95de4a57bfc765509db05695dc50ab"}, + {file = "pycountry-23.12.11.tar.gz", hash = "sha256:00569d82eaefbc6a490a311bfa84a9c571cff9ddbf8b0a4f4e7b4f868b4ad925"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pycryptodome" +version = "3.20.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, + {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pyhamcrest" +version = "2.1.0" +description = "Hamcrest framework for matcher objects" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyhamcrest-2.1.0-py3-none-any.whl", hash = "sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587"}, + {file = "pyhamcrest-2.1.0.tar.gz", hash = "sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c"}, +] + +[package.extras] +dev = ["black", "doc2dash", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "pyyaml", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-flask" +version = "0.6" +description = "pylint-flask is a Pylint plugin to aid Pylint in recognizing and understanding errors caused when using Flask" +optional = false +python-versions = "*" +files = [ + {file = "pylint-flask-0.6.tar.gz", hash = "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517"}, +] + +[package.dependencies] +pylint-plugin-utils = ">=0.2.1" + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pyrfc3339" +version = "1.1" +description = "Generate and parse RFC 3339 timestamps" +optional = false +python-versions = "*" +files = [ + {file = "pyRFC3339-1.1-py2.py3-none-any.whl", hash = "sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4"}, + {file = "pyRFC3339-1.1.tar.gz", hash = "sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a"}, +] + +[package.dependencies] +pytz = "*" + +[[package]] +name = "pyrsistent" +version = "0.20.0" +description = "Persistent/Functional/Immutable data structures" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, + {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, + {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, + {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, + {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, + {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, + {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, + {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, + {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, + {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, + {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, + {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, + {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, + {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, + {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, + {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, + {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, +] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.18.3" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-asyncio-0.18.3.tar.gz", hash = "sha256:7659bdb0a9eb9c6e3ef992eef11a2b3e69697800ad02fb06374a210d85b29f91"}, + {file = "pytest_asyncio-0.18.3-1-py3-none-any.whl", hash = "sha256:16cf40bdf2b4fb7fc8e4b82bd05ce3fbcd454cbf7b92afc445fe299dabb88213"}, + {file = "pytest_asyncio-0.18.3-py3-none-any.whl", hash = "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84"}, +] + +[package.dependencies] +pytest = ">=6.1.0" + +[package.extras] +testing = ["coverage (==6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (==0.931)", "pytest-trio (>=0.7.0)"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.12.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "sbc_common_components" +version = "0.0.0" +description = "" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +flask = "*" +flask-jwt-oidc = ">=0.1.5" +Flask-OpenTracing = "1.1.0" +Flask-SQLAlchemy = "*" +jaeger-client = "*" + +[package.source] +type = "git" +url = "https://github.com/bcgov/sbc-common-components.git" +reference = "HEAD" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +subdirectory = "python" + +[[package]] +name = "semver" +version = "3.0.2" +description = "Python helper for Semantic Versioning (https://semver.org)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"}, + {file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"}, +] + +[[package]] +name = "sentry-sdk" +version = "1.42.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, + {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, +] + +[package.dependencies] +blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} +certifi = "*" +flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} +markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simple-cloudevent" +version = "0.0.2" +description = "A short description of the project" +optional = false +python-versions = ">=3.8" +files = [] +develop = false + +[package.dependencies] +strict-rfc3339 = "*" + +[package.source] +type = "git" +url = "https://github.com/daxiom/simple-cloudevent.py.git" +reference = "HEAD" +resolved_reference = "447cabb988202206ac69e71177d7cd11b6c0b002" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.28" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-utils" +version = "0.41.1" +description = "Various utility functions for SQLAlchemy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, +] + +[package.dependencies] +SQLAlchemy = ">=1.3" + +[package.extras] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +name = "strict-rfc3339" +version = "0.7" +description = "Strict, simple, lightweight RFC3339 functions" +optional = false +python-versions = "*" +files = [ + {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"}, +] + +[[package]] +name = "threadloop" +version = "1.0.2" +description = "Tornado IOLoop Backed Concurrent Futures" +optional = false +python-versions = "*" +files = [ + {file = "threadloop-1.0.2-py2-none-any.whl", hash = "sha256:5c90dbefab6ffbdba26afb4829d2a9df8275d13ac7dc58dccb0e279992679599"}, + {file = "threadloop-1.0.2.tar.gz", hash = "sha256:8b180aac31013de13c2ad5c834819771992d350267bddb854613ae77ef571944"}, +] + +[package.dependencies] +tornado = "*" + +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "werkzeug" +version = "3.0.1" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "db250a37ef975521eb95b2da881e7383e738c912c208062be75a84f189828be5" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml new file mode 100644 index 000000000..269d894ce --- /dev/null +++ b/pay-queue/pyproject.toml @@ -0,0 +1,56 @@ +[tool.poetry] +name = "pay-queue" +version = "0.1.0" +description = "" +authors = ["Travis Semple "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +flask = "^3.0.2" +jsonschema = "4.17.3" +python-dotenv = "^1.0.1" +sentry-sdk = {extras = ["flask"], version = "^1.42.0"} +pycountry = "^23.12.11" +werkzeug = "^3.0.1" +minio = "^7.2.5" +jaeger-client = "^4.8.0" +attrs = "^23.2.0" +sqlalchemy = "^2.0.28" +itsdangerous = "^2.1.2" +jinja2 = "^3.1.3" +protobuf = "4.25.3" +launchdarkly-server-sdk = "^9.2.2" +cachecontrol = "^0.14.0" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "18263", subdirectory = "pay-api"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-mock = "^3.12.0" +requests = "^2.31.0" +pyhamcrest = "^2.1.0" +pytest-cov = "^4.1.0" +freezegun = "^1.4.0" +flake8 = "^7.0.0" +flake8-blind-except = "^0.2.1" +flake8-debugger = "^4.1.2" +flake8-docstrings = "^1.7.0" +flake8-isort = "^6.1.1" +flake8-quotes = "^3.4.0" +pep8-naming = "^0.13.3" +autopep8 = "^2.0.4" +coverage = "^7.4.3" +pylint = "^3.1.0" +pylint-flask = "^0.6" +pydocstyle = "^6.3.0" +isort = "^5.13.2" +lovely-pytest-docker = "^0.3.1" +pytest-asyncio = "0.18.3" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pay-queue/requirements.txt b/pay-queue/requirements.txt deleted file mode 100644 index 2d3d82364..000000000 --- a/pay-queue/requirements.txt +++ /dev/null @@ -1,85 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git@5f99e135214ae949c9af951d4aa0b88b1067d853#egg=sbc_common_components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@ca0a69dce17ec602f2c3ceee164f15d0c6e7e804#egg=pay_api&subdirectory=pay-api --e git+https://github.com/thorwolpert/flask-jwt-oidc.git@40cc811ccf70e838c5f7522fe8d83b7e58853539#egg=flask_jwt_oidc -CacheControl==0.14.0 -Flask-Caching==2.1.0 -Flask-Cors==4.0.0 -Flask-Migrate==4.0.7 -Flask-Moment==1.0.5 -Flask-OpenTracing==1.1.0 -Flask-SQLAlchemy==3.1.1 -Flask-Script==2.0.6 -Flask==3.0.2 -Jinja2==3.1.3 -Mako==1.3.2 -MarkupSafe==2.1.5 -SQLAlchemy-Utils==0.41.1 -SQLAlchemy==2.0.28 -Werkzeug==3.0.1 -alembic==1.13.1 -argon2-cffi-bindings==21.2.0 -argon2-cffi==23.1.0 -attrs==23.2.0 -blinker==1.7.0 -cachelib==0.9.0 -cachetools==5.3.3 -cattrs==23.2.3 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -croniter==2.0.2 -cryptography==42.0.5 -dpath==2.1.6 -ecdsa==0.18.0 -expiringdict==1.2.2 -flask-marshmallow==1.2.0 -google-api-core==2.17.1 -google-auth==2.28.1 -google-cloud-pubsub==2.20.0 -googleapis-common-protos==1.63.0 -greenlet==3.0.3 -grpc-google-iam-v1==0.13.0 -grpcio-status==1.62.1 -grpcio==1.62.1 -gunicorn==21.2.0 -holidays==0.37 -idna==3.6 -itsdangerous==2.1.2 -jaeger-client==4.8.0 -jsonschema==4.17.3 -launchdarkly-eventsource==1.1.1 -launchdarkly-server-sdk==9.2.2 -marshmallow-sqlalchemy==1.0.0 -marshmallow==3.21.1 -minio==7.2.5 -msgpack==1.0.8 -opentracing==2.4.0 -packaging==24.0 -proto-plus==1.23.0 -protobuf==4.25.3 -psycopg2-binary==2.9.9 -pyRFC3339==1.1 -pyasn1-modules==0.3.0 -pyasn1==0.5.1 -pycountry==23.12.11 -pycparser==2.21 -pycryptodome==3.20.0 -pyhumps==3.8.0 -pyrsistent==0.20.0 -python-dateutil==2.9.0.post0 -python-dotenv==1.0.1 -python-jose==3.3.0 -pytz==2024.1 -requests==2.31.0 -rsa==4.9 -semver==3.0.2 -sentry-sdk==1.42.0 -simple-cloudevent @ git+https://github.com/daxiom/simple-cloudevent.py.git@447cabb988202206ac69e71177d7cd11b6c0b002 -six==1.16.0 -strict-rfc3339==0.7 -threadloop==1.0.2 -thrift==0.16.0 -tornado==6.4 -typing_extensions==4.10.0 -urllib3==2.2.1 diff --git a/pay-queue/requirements/bcregistry-libraries.txt b/pay-queue/requirements/bcregistry-libraries.txt deleted file mode 100644 index d1067265b..000000000 --- a/pay-queue/requirements/bcregistry-libraries.txt +++ /dev/null @@ -1,5 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python --e git+https://github.com/seeker25/sbc-pay.git@18263#egg=pay-api&subdirectory=pay-api --e git+https://github.com/thorwolpert/flask-jwt-oidc.git#egg=flask-jwt-oidc -# Note move out queue stuff into here. -git+https://github.com/daxiom/simple-cloudevent.py.git diff --git a/pay-queue/requirements/dev.txt b/pay-queue/requirements/dev.txt deleted file mode 100755 index 6c304627f..000000000 --- a/pay-queue/requirements/dev.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov -FreezeGun - -# Lint and code style -flake8 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -pydocstyle -isort - - -# docker -lovely-pytest-docker -pytest-asyncio==0.18.3 - diff --git a/pay-queue/requirements/prod.txt b/pay-queue/requirements/prod.txt deleted file mode 100644 index 2266a7ec5..000000000 --- a/pay-queue/requirements/prod.txt +++ /dev/null @@ -1,15 +0,0 @@ -Flask -jsonschema==4.17.3 -python-dotenv -sentry-sdk[flask] -pycountry -Werkzeug -minio -jaeger-client -attrs -sqlalchemy -itsdangerous -Jinja2 -protobuf -launchdarkly-server-sdk -CacheControl diff --git a/pay-queue/tests/conftest.py b/pay-queue/tests/conftest.py index 56771964a..3707d540c 100644 --- a/pay-queue/tests/conftest.py +++ b/pay-queue/tests/conftest.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Common setup and fixtures for the pytest suite used by this service.""" -import sys +import os import pytest from flask_migrate import Migrate, upgrade @@ -38,10 +38,9 @@ def db(app): # pylint: disable=redefined-outer-name, invalid-name drop_database(_db.engine.url) create_database(_db.engine.url) _db.session().execute(text('SET TIME ZONE "UTC";')) - migrations_path = [folder for folder in sys.path if 'pay-api/pay-api' in folder] - if len(migrations_path) > 0: - migrations_path = migrations_path[0].replace('/pay-api/src', '/pay-api/migrations') - Migrate(app, _db, directory=migrations_path) + pay_api_dir = os.path.abspath('.').replace('pay-queue', 'pay-api') + pay_api_dir = os.path.join(pay_api_dir, 'migrations') + Migrate(app, _db, directory=pay_api_dir) upgrade() return _db diff --git a/report-api/src/api/resources/__init__.py b/report-api/src/api/resources/__init__.py index 9cfc8d130..4fe7e51df 100755 --- a/report-api/src/api/resources/__init__.py +++ b/report-api/src/api/resources/__init__.py @@ -34,7 +34,6 @@ __all__ = ('API_BLUEPRINT', 'OPS_BLUEPRINT') # This will add the Authorize button to the swagger docs -# TODO oauth2 & openid may not yet be supported by restplus <- check on this AUTHORIZATIONS = { 'apikey': { 'type': 'apiKey', From 4b98de570d279180f10828ab03162344b94bb6b0 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 14 Mar 2024 10:56:57 -0700 Subject: [PATCH 21/87] Add pay api gcp CD flow. (#1446) (#1447) Co-authored-by: Patrick Wei <44277752+pwei1018@users.noreply.github.com> --- .github/workflows/pay-api-cd-gcp.yml | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/pay-api-cd-gcp.yml diff --git a/.github/workflows/pay-api-cd-gcp.yml b/.github/workflows/pay-api-cd-gcp.yml new file mode 100644 index 000000000..a5fe4c8dc --- /dev/null +++ b/.github/workflows/pay-api-cd-gcp.yml @@ -0,0 +1,30 @@ +name: Pay API CD + +on: + push: + branches: + - feature-queue-python-upgrade + paths: + - "pay-api/**" + workflow_dispatch: + inputs: + target: + description: "Deploy To" + required: true + type: choice + options: + - dev + - test + - sandbox + - prod + +jobs: + pay-api-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-api" + working_directory: "./pay-api" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file From 17c5c37100935b3ad39e0f25012496f4b0d1b73b Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 15 Mar 2024 12:41:17 -0700 Subject: [PATCH 22/87] First run at pubsub emulation for integration tests + pg8000 upgrade (#1448) * First run at pubsub emulation * Use pg8000 instead of psycopg2 * Remove requirement of keys allow for unauthenticated client * Change gunicorn to match lear * Remove prehooks, no concepts of prehooks anymore * Remove jaeger-client * Fix unit tests * Update dependencies * Use test environment variables * Update secret for linux * Checkout v4 * Add in extra hosts * bind to 0.0.0.0 --- .github/workflows/bcol-api-cd.yml | 4 +- .github/workflows/bcol-api-ci.yml | 8 +- .github/workflows/ftp-poller-cd.yml | 4 +- .github/workflows/ftp-poller-ci.yml | 8 +- .github/workflows/notebook-report-cd.yml | 4 +- .github/workflows/notebook-report-ci.yml | 8 +- .github/workflows/pay-admin-cd.yml | 4 +- .github/workflows/pay-admin-ci.yml | 8 +- .github/workflows/pay-api-ci.yml | 8 +- .github/workflows/pay-queue-cd.yml | 4 +- .github/workflows/pay-queue-ci.yml | 10 +- .github/workflows/payment-jobs-cd.yml | 4 +- .github/workflows/payment-jobs-ci.yml | 8 +- .github/workflows/ppr-report-api-cd.yml | 4 +- .github/workflows/report-api-cd.yml | 4 +- .github/workflows/report-api-ci.yml | 8 +- jobs/payment-jobs/config.py | 16 +- jobs/payment-jobs/poetry.lock | 42 ++- jobs/payment-jobs/pyproject.toml | 1 + pay-admin/admin/config.py | 12 +- pay-admin/poetry.lock | 42 ++- pay-admin/pyproject.toml | 1 + pay-api/gunicorn_config.py | 6 +- pay-api/poetry.lock | 42 ++- pay-api/pre-hook-update-db.sh | 4 - pay-api/pre_hook_create_database.py | 40 --- pay-api/pyproject.toml | 1 + pay-api/src/pay_api/config.py | 4 +- .../pay_api/services/gcp_queue_publisher.py | 23 +- pay-queue/poetry.lock | 266 ++++++++++++------ pay-queue/pyproject.toml | 6 +- pay-queue/src/pay_queue/config.py | 16 +- pay-queue/src/pay_queue/resources/__init__.py | 2 +- .../services/eft/eft_reconciliation.py | 1 - pay-queue/tests/conftest.py | 34 +++ pay-queue/tests/docker-compose.yml | 7 + .../integration/test_cgi_reconciliations.py | 74 +++-- .../integration/test_eft_reconciliation.py | 28 +- .../test_payment_reconciliations.py | 34 +-- pay-queue/tests/integration/utils.py | 52 +++- 40 files changed, 553 insertions(+), 299 deletions(-) delete mode 100755 pay-api/pre-hook-update-db.sh delete mode 100644 pay-api/pre_hook_create_database.py diff --git a/.github/workflows/bcol-api-cd.yml b/.github/workflows/bcol-api-cd.yml index 4c99a3c15..ca8bc50c4 100644 --- a/.github/workflows/bcol-api-cd.yml +++ b/.github/workflows/bcol-api-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -74,7 +74,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/bcol-api-ci.yml b/.github/workflows/bcol-api-ci.yml index ad004ae26..54f82833c 100644 --- a/.github/workflows/bcol-api-ci.yml +++ b/.github/workflows/bcol-api-ci.yml @@ -20,7 +20,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -32,7 +32,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -97,7 +97,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -128,7 +128,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/ftp-poller-cd.yml b/.github/workflows/ftp-poller-cd.yml index 57ed7e787..f358a3732 100644 --- a/.github/workflows/ftp-poller-cd.yml +++ b/.github/workflows/ftp-poller-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -74,7 +74,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/ftp-poller-ci.yml b/.github/workflows/ftp-poller-ci.yml index 53e90abb1..d9a998238 100644 --- a/.github/workflows/ftp-poller-ci.yml +++ b/.github/workflows/ftp-poller-ci.yml @@ -20,7 +20,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -32,7 +32,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -77,7 +77,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -106,7 +106,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/notebook-report-cd.yml b/.github/workflows/notebook-report-cd.yml index 020a5d8d2..7eea8db18 100644 --- a/.github/workflows/notebook-report-cd.yml +++ b/.github/workflows/notebook-report-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -69,7 +69,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/notebook-report-ci.yml b/.github/workflows/notebook-report-ci.yml index 8a7a8fffa..b4365bd1b 100644 --- a/.github/workflows/notebook-report-ci.yml +++ b/.github/workflows/notebook-report-ci.yml @@ -19,7 +19,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -31,7 +31,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -72,7 +72,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -101,7 +101,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/pay-admin-cd.yml b/.github/workflows/pay-admin-cd.yml index 17059c411..326adb54c 100644 --- a/.github/workflows/pay-admin-cd.yml +++ b/.github/workflows/pay-admin-cd.yml @@ -32,7 +32,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -75,7 +75,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/pay-admin-ci.yml b/.github/workflows/pay-admin-ci.yml index a7dac379c..cccbe5a3e 100644 --- a/.github/workflows/pay-admin-ci.yml +++ b/.github/workflows/pay-admin-ci.yml @@ -21,7 +21,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -33,7 +33,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -78,7 +78,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -107,7 +107,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/pay-api-ci.yml b/.github/workflows/pay-api-ci.yml index b21549e25..00c080090 100644 --- a/.github/workflows/pay-api-ci.yml +++ b/.github/workflows/pay-api-ci.yml @@ -20,7 +20,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -32,7 +32,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -88,7 +88,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -117,7 +117,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/pay-queue-cd.yml b/.github/workflows/pay-queue-cd.yml index 8461d258e..48d125831 100644 --- a/.github/workflows/pay-queue-cd.yml +++ b/.github/workflows/pay-queue-cd.yml @@ -33,7 +33,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -76,7 +76,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/pay-queue-ci.yml b/.github/workflows/pay-queue-ci.yml index c399fae26..536f6a92c 100644 --- a/.github/workflows/pay-queue-ci.yml +++ b/.github/workflows/pay-queue-ci.yml @@ -22,7 +22,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -34,7 +34,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -75,7 +75,7 @@ jobs: services: postgres: - image: postgres:12 + image: postgres:15 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -86,7 +86,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -115,7 +115,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/payment-jobs-cd.yml b/.github/workflows/payment-jobs-cd.yml index 33d7a921f..93b190a5c 100644 --- a/.github/workflows/payment-jobs-cd.yml +++ b/.github/workflows/payment-jobs-cd.yml @@ -33,7 +33,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -76,7 +76,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/payment-jobs-ci.yml b/.github/workflows/payment-jobs-ci.yml index 2f3e58c13..38b72035e 100644 --- a/.github/workflows/payment-jobs-ci.yml +++ b/.github/workflows/payment-jobs-ci.yml @@ -22,7 +22,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -34,7 +34,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -79,7 +79,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -108,7 +108,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/.github/workflows/ppr-report-api-cd.yml b/.github/workflows/ppr-report-api-cd.yml index 054547381..b37d3d0a5 100644 --- a/.github/workflows/ppr-report-api-cd.yml +++ b/.github/workflows/ppr-report-api-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -77,7 +77,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/report-api-cd.yml b/.github/workflows/report-api-cd.yml index 2611a7346..e9a05b7d8 100644 --- a/.github/workflows/report-api-cd.yml +++ b/.github/workflows/report-api-cd.yml @@ -31,7 +31,7 @@ jobs: name: "dev" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login Openshift shell: bash @@ -77,7 +77,7 @@ jobs: name: "${{ github.event.inputs.environment }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env by input run: | echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV diff --git a/.github/workflows/report-api-ci.yml b/.github/workflows/report-api-ci.yml index 57359f7c4..08fbb4a22 100644 --- a/.github/workflows/report-api-ci.yml +++ b/.github/workflows/report-api-ci.yml @@ -19,7 +19,7 @@ jobs: if: github.repository == 'bcgov/sbc-pay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: "true" linting: @@ -31,7 +31,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -78,7 +78,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -107,7 +107,7 @@ jobs: python-version: [3.12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build to check strictness id: build run: | diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index 842feb1ba..426f77f01 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -64,14 +64,12 @@ class _Config(object): # pylint: disable=too-few-public-methods DB_NAME = os.getenv('DATABASE_NAME', '') DB_HOST = os.getenv('DATABASE_HOST', '') DB_PORT = os.getenv('DATABASE_PORT', '5432') - - SQLALCHEMY_DATABASE_URI = 'postgresql://{user}:{password}@{host}:{port}/{name}'.format( - user=DB_USER, - password=DB_PASSWORD, - host=DB_HOST, - port=int(DB_PORT), - name=DB_NAME, - ) + if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): + SQLALCHEMY_DATABASE_URI = ( + f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + ) + else: + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' SQLALCHEMY_ECHO = False ORACLE_USER = os.getenv('ORACLE_USER', '') @@ -213,7 +211,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DB_HOST = os.getenv('DATABASE_TEST_HOST', '') DB_PORT = os.getenv('DATABASE_TEST_PORT', '5432') SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_TEST_URL', - 'postgresql://{user}:{password}@{host}:{port}/{name}'.format( + 'postgresql+pg8000://{user}:{password}@{host}:{port}/{name}'.format( user=DB_USER, password=DB_PASSWORD, host=DB_HOST, diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock index fbc3c7b50..b71776aa8 100644 --- a/jobs/payment-jobs/poetry.lock +++ b/jobs/payment-jobs/poetry.lock @@ -76,6 +76,17 @@ cffi = ">=1.0.1" dev = ["cogapp", "pre-commit", "pytest", "wheel"] tests = ["pytest"] +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + [[package]] name = "astroid" version = "3.1.0" @@ -1653,6 +1664,21 @@ files = [ [package.dependencies] flake8 = ">=5.0.0" +[[package]] +name = "pg8000" +version = "1.30.5" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, + {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.4" + [[package]] name = "platformdirs" version = "4.2.0" @@ -2281,6 +2307,20 @@ reference = "HEAD" resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" subdirectory = "python" +[[package]] +name = "scramp" +version = "1.4.4" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.7" +files = [ + {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, + {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + [[package]] name = "semver" version = "3.0.2" @@ -2633,4 +2673,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "f9361d9a2eb005b26cfdd2bf2a3dd01ef810f515f50061722bf54b3d436978c2" +content-hash = "350c3fdfde2b9ef2aa795334a051e997a91eddd0e155897b958fd7f60492f4a5" diff --git a/jobs/payment-jobs/pyproject.toml b/jobs/payment-jobs/pyproject.toml index 23ab857ba..a7f42bb90 100644 --- a/jobs/payment-jobs/pyproject.toml +++ b/jobs/payment-jobs/pyproject.toml @@ -31,6 +31,7 @@ dataclass-wizard = "^0.22.3" launchdarkly-server-sdk = "^9.2.2" cx-oracle = "^8.3.0" more-itertools = "^10.2.0" +pg8000 = "^1.30.5" [tool.poetry.group.dev.dependencies] diff --git a/pay-admin/admin/config.py b/pay-admin/admin/config.py index 679e8019c..ea9fabc0f 100755 --- a/pay-admin/admin/config.py +++ b/pay-admin/admin/config.py @@ -76,7 +76,12 @@ class _Config(): # pylint: disable=too-few-public-methods DB_NAME = _get_config('DATABASE_NAME') DB_HOST = _get_config('DATABASE_HOST') DB_PORT = _get_config('DATABASE_PORT', default='5432') - SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): + SQLALCHEMY_DATABASE_URI = ( + f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + ) + else: + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' SQLALCHEMY_ECHO = _get_config('SQLALCHEMY_ECHO', default='False').lower() == 'true' # Normal Keycloak parameters. @@ -111,10 +116,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DB_NAME = _get_config('DATABASE_TEST_NAME', default='paytestdb') DB_HOST = _get_config('DATABASE_TEST_HOST', default='localhost') DB_PORT = _get_config('DATABASE_TEST_PORT', default='5432') - SQLALCHEMY_DATABASE_URI = _get_config( - 'DATABASE_TEST_URL', - default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' - ) + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' class ProdConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/pay-admin/poetry.lock b/pay-admin/poetry.lock index 38966f8e4..67fd517ef 100644 --- a/pay-admin/poetry.lock +++ b/pay-admin/poetry.lock @@ -19,6 +19,17 @@ typing-extensions = ">=4" [package.extras] tz = ["backports.zoneinfo"] +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + [[package]] name = "astroid" version = "3.1.0" @@ -1500,6 +1511,21 @@ files = [ [package.dependencies] flake8 = ">=5.0.0" +[[package]] +name = "pg8000" +version = "1.30.5" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, + {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.4" + [[package]] name = "platformdirs" version = "4.2.0" @@ -2061,6 +2087,20 @@ reference = "HEAD" resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" subdirectory = "python" +[[package]] +name = "scramp" +version = "1.4.4" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.7" +files = [ + {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, + {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + [[package]] name = "semver" version = "3.0.2" @@ -2430,4 +2470,4 @@ email = ["email-validator"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "89b1ff4a2b3b813184e1f2bd3dbe358134e0dc202139e7521f843d223503b850" +content-hash = "feabc25da8baaa7b9cefbf4555e8293a347a3d8a44c4c02f6ceb3c0839655256" diff --git a/pay-admin/pyproject.toml b/pay-admin/pyproject.toml index d0aac6299..24e846151 100644 --- a/pay-admin/pyproject.toml +++ b/pay-admin/pyproject.toml @@ -26,6 +26,7 @@ pay-api = {git = "https://github.com/bcgov/sbc-pay.git", rev = "feature-queue-py sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +pg8000 = "^1.30.5" [tool.poetry.group.dev.dependencies] diff --git a/pay-api/gunicorn_config.py b/pay-api/gunicorn_config.py index c27530fcc..a3ab9633f 100755 --- a/pay-api/gunicorn_config.py +++ b/pay-api/gunicorn_config.py @@ -18,11 +18,11 @@ import os # https://docs.gunicorn.org/en/stable/settings.html#workers -workers = int(os.environ.get('GUNICORN_PROCESSES', '3')) # gunicorn default - 1 +workers = int(os.environ.get('GUNICORN_PROCESSES', '1')) # gunicorn default - 1 worker_class = os.environ.get('GUNICORN_WORKER_CLASS', 'sync') # gunicorn default - sync worker_connections = int(os.environ.get('GUNICORN_WORKER_CONNECIONS', '1000')) # gunicorn default - 1000 -threads = int(os.environ.get('GUNICORN_THREADS', '1')) # gunicorn default - 1 -timeout = int(os.environ.get('GUNICORN_TIMEOUT', '100')) # gunicorn default - 30 +threads = int(os.environ.get('GUNICORN_THREADS', '8')) # gunicorn default - 1 +timeout = int(os.environ.get('GUNICORN_TIMEOUT', '0')) # gunicorn default - 30 keepalive = int(os.environ.get('GUNICORN_KEEPALIVE', '2')) # gunicorn default - 2 diff --git a/pay-api/poetry.lock b/pay-api/poetry.lock index abeb53a08..0a825ea22 100644 --- a/pay-api/poetry.lock +++ b/pay-api/poetry.lock @@ -19,6 +19,17 @@ typing-extensions = ">=4" [package.extras] tz = ["backports.zoneinfo"] +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + [[package]] name = "astroid" version = "3.1.0" @@ -1416,6 +1427,21 @@ files = [ [package.dependencies] flake8 = ">=5.0.0" +[[package]] +name = "pg8000" +version = "1.30.5" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, + {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.4" + [[package]] name = "platformdirs" version = "4.2.0" @@ -1964,6 +1990,20 @@ reference = "HEAD" resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" subdirectory = "python" +[[package]] +name = "scramp" +version = "1.4.4" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.7" +files = [ + {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, + {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + [[package]] name = "semver" version = "3.0.2" @@ -2312,4 +2352,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "0c5418d826d95e899b3996724ab8887f08935df2e03e11a289d5f8d9e022b3e2" +content-hash = "d61ec06879fc556df5d136cdee89a237a5148971b5b4e34f334b397cc64c7e97" diff --git a/pay-api/pre-hook-update-db.sh b/pay-api/pre-hook-update-db.sh deleted file mode 100755 index 5624bf62f..000000000 --- a/pay-api/pre-hook-update-db.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh -cd /opt/app-root -echo 'starting upgrade' -python3 manage.py db upgrade diff --git a/pay-api/pre_hook_create_database.py b/pay-api/pre_hook_create_database.py deleted file mode 100644 index 6910f3aa3..000000000 --- a/pay-api/pre_hook_create_database.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Function to create or upgrade database before application deployment.""" -import contextlib -import os -import sys - -import sqlalchemy -import sqlalchemy.exc -from config import ProdConfig - - -DB_ADMIN_PASSWORD = os.getenv('DB_ADMIN_PASSWORD', None) - -if not hasattr(ProdConfig, 'DB_NAME') or not DB_ADMIN_PASSWORD: - print('Unable to create database.', sys.stdout) - sys.exit(-1) - -DATABASE_URI = 'postgresql://postgres:{password}@{host}:{port}/{name}'.format( - password=DB_ADMIN_PASSWORD, - host=ProdConfig.DB_HOST, - port=int(ProdConfig.DB_PORT), - name='postgres', -) - -with contextlib.suppress(sqlalchemy.exc.ProgrammingError): - with sqlalchemy.create_engine(DATABASE_URI, isolation_level='AUTOCOMMIT').connect() as connection: - DATABASE_NAME = ProdConfig.DB_NAME - connection.execute(f'CREATE DATABASE "{DATABASE_NAME}"') diff --git a/pay-api/pyproject.toml b/pay-api/pyproject.toml index 3abc57aad..58938ac0c 100644 --- a/pay-api/pyproject.toml +++ b/pay-api/pyproject.toml @@ -82,6 +82,7 @@ typing-extensions = "4.10.0" urllib3 = "2.2.1" simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +pg8000 = "^1.30.5" [tool.poetry.group.dev.dependencies] diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 9e3ad40af..3fd18204c 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -88,10 +88,10 @@ class _Config: # pylint: disable=too-few-public-methods # POSTGRESQL if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): SQLALCHEMY_DATABASE_URI = ( - f'postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' ) else: - SQLALCHEMY_DATABASE_URI = f'postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}' + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}' # JWT_OIDC Settings JWT_OIDC_WELL_KNOWN_CONFIG = _get_config('JWT_OIDC_WELL_KNOWN_CONFIG') diff --git a/pay-api/src/pay_api/services/gcp_queue_publisher.py b/pay-api/src/pay_api/services/gcp_queue_publisher.py index 2e390d3ae..1d894ea24 100644 --- a/pay-api/src/pay_api/services/gcp_queue_publisher.py +++ b/pay-api/src/pay_api/services/gcp_queue_publisher.py @@ -44,18 +44,19 @@ def publish_to_queue(queue_message: QueueMessage): def _send_to_queue(topic_name: str, payload: bytes): """Send payload to the queue.""" - if not ((gcp_auth_key := current_app.config.get('GCP_AUTH_KEY')) and - (audience := current_app.config.get('AUDIENCE')) and - (publisher_audience := current_app.config.get('PUBLISHER_AUDIENCE'))): - raise Exception('Missing setup arguments') # pylint: disable=W0719 - + gcp_auth_key = current_app.config.get('GCP_AUTH_KEY') + audience = current_app.config.get('AUDIENCE') + publisher_audience = current_app.config.get('PUBLISHER_AUDIENCE') try: - service_account_info = json.loads(base64.b64decode(gcp_auth_key).decode('utf-8')) - credentials = jwt.Credentials.from_service_account_info( - service_account_info, audience=audience - ) - credentials_pub = credentials.with_claims(audience=publisher_audience) - publisher = pubsub_v1.PublisherClient(credentials=credentials_pub) + if gcp_auth_key: + service_account_info = json.loads(base64.b64decode(gcp_auth_key).decode('utf-8')) + credentials = jwt.Credentials.from_service_account_info( + service_account_info, audience=audience + ) + credentials_pub = credentials.with_claims(audience=publisher_audience) + publisher = pubsub_v1.PublisherClient(credentials=credentials_pub) + else: + publisher = pubsub_v1.PublisherClient() except Exception as error: # noqa: B902 raise Exception('Unable to create a connection', error) from error # pylint: disable=W0719 diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index ce6f002bd..d3d57005b 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -76,6 +76,17 @@ cffi = ">=1.0.1" dev = ["cogapp", "pre-commit", "pytest", "wheel"] tests = ["pytest"] +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + [[package]] name = "astroid" version = "3.1.0" @@ -397,63 +408,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.3" +version = "7.4.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, - {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, - {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, - {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, - {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, - {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, - {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, - {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, - {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, - {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, - {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, - {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, - {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, - {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, ] [package.extras] @@ -1549,52 +1560,97 @@ files = [ ] [[package]] -name = "pay_api" -version = "0.0.0" -description = "A short description of the project" +name = "pay-api" +version = "0.1.0" +description = "" optional = false -python-versions = ">=3.12" +python-versions = "^3.12" files = [] develop = false [package.dependencies] -cattrs = "*" -croniter = "*" -cryptography = "*" -dpath = "*" -Flask = "*" -Flask-Caching = "*" -Flask-Cors = "*" -flask-jwt-oidc = "*" -flask-marshmallow = "*" -Flask-Migrate = "*" -Flask-Moment = "*" -Flask-Script = "*" -Flask-SQLAlchemy = "*" +alembic = "1.13.1" +attrs = "23.2.0" +blinker = "1.7.0" +cachelib = "0.9.0" +cachetools = "5.3.3" +cattrs = "23.2.3" +certifi = "2024.2.2" +cffi = "1.16.0" +charset-normalizer = "3.3.2" +click = "8.1.7" +croniter = "2.0.2" +cryptography = "42.0.5" +dpath = "2.1.6" +ecdsa = "0.18.0" +expiringdict = "1.2.2" +flask = "3.0.2" +flask-caching = "2.1.0" +flask-cors = "4.0.0" +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +flask-marshmallow = "1.2.0" +flask-migrate = "4.0.7" +flask-moment = "1.0.5" +flask-script = "2.0.6" +flask-sqlalchemy = "3.1.1" +google-api-core = "2.17.1" google-auth = "2.28.1" google-cloud-pubsub = "2.20.0" -gunicorn = "*" +googleapis-common-protos = "1.63.0" +greenlet = "3.0.3" +grpc-google-iam-v1 = "0.13.0" +grpcio = "1.62.1" +grpcio-status = "1.62.1" +gunicorn = "21.2.0" holidays = "0.37" -itsdangerous = "*" -jaeger-client = "*" -Jinja2 = "*" +idna = "3.6" +itsdangerous = "2.1.2" +jaeger-client = "4.8.0" +jinja2 = "3.1.3" jsonschema = "4.17.3" -launchdarkly-server-sdk = "*" -marshmallow-sqlalchemy = "*" -psycopg2-binary = "*" -pyhumps = "*" -python-dotenv = "*" -requests = "*" -sentry-sdk = {version = "*", extras = ["flask"]} -sqlalchemy = "*" -sqlalchemy_utils = "*" -Werkzeug = "*" +launchdarkly-eventsource = "1.1.1" +launchdarkly-server-sdk = "9.2.2" +mako = "1.3.2" +markupsafe = "2.1.5" +marshmallow = "3.21.1" +marshmallow-sqlalchemy = "1.0.0" +opentracing = "2.4.0" +packaging = "24.0" +pg8000 = "^1.30.5" +proto-plus = "1.23.0" +protobuf = "4.25.3" +psycopg2-binary = "2.9.9" +pyasn1 = "0.5.1" +pyasn1-modules = "0.3.0" +pycparser = "2.21" +pyhumps = "3.8.0" +pyrfc3339 = "1.1" +pyrsistent = "0.20.0" +python-dateutil = "2.9.0.post0" +python-dotenv = "1.0.1" +python-jose = "3.3.0" +pytz = "2024.1" +requests = "2.31.0" +rsa = "4.9" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +semver = "3.0.2" +sentry-sdk = "1.41.0" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +six = "1.16.0" +sqlalchemy = "2.0.28" +sqlalchemy-utils = "0.41.1" +threadloop = "1.0.2" +thrift = "0.16.0" +tornado = "6.4" +typing-extensions = "4.10.0" +urllib3 = "2.2.1" +werkzeug = "3.0.1" [package.source] type = "git" url = "https://github.com/seeker25/sbc-pay.git" -reference = "18263" -resolved_reference = "fe42bf81c86c77946a1df238f69f12ad6b83d804" +reference = "pubsub_emulation" +resolved_reference = "e2c9d5fe0e51ce2be585b45ed26d40ba6e03ecbd" subdirectory = "pay-api" [[package]] @@ -1611,6 +1667,21 @@ files = [ [package.dependencies] flake8 = ">=5.0.0" +[[package]] +name = "pg8000" +version = "1.30.5" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, + {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.4" + [[package]] name = "platformdirs" version = "4.2.0" @@ -2211,6 +2282,20 @@ reference = "HEAD" resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" subdirectory = "python" +[[package]] +name = "scramp" +version = "1.4.4" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.7" +files = [ + {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, + {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + [[package]] name = "semver" version = "3.0.2" @@ -2224,13 +2309,13 @@ files = [ [[package]] name = "sentry-sdk" -version = "1.42.0" +version = "1.41.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, - {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, + {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, + {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, ] [package.dependencies] @@ -2257,7 +2342,6 @@ grpcio = ["grpcio (>=1.21.1)"] httpx = ["httpx (>=0.16.0)"] huey = ["huey (>=2)"] loguru = ["loguru (>=0.5)"] -openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] opentelemetry = ["opentelemetry-distro (>=0.35b0)"] opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] pure-eval = ["asttokens", "executing", "pure-eval"] @@ -2563,4 +2647,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "db250a37ef975521eb95b2da881e7383e738c912c208062be75a84f189828be5" +content-hash = "bc1d52223cc3c9ae97947aeb1a795405091f9c1d771d0a088090cdfde97d9093" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml index 269d894ce..f2c245e7f 100644 --- a/pay-queue/pyproject.toml +++ b/pay-queue/pyproject.toml @@ -10,11 +10,10 @@ python = "^3.12" flask = "^3.0.2" jsonschema = "4.17.3" python-dotenv = "^1.0.1" -sentry-sdk = {extras = ["flask"], version = "^1.42.0"} +sentry-sdk = {extras = ["flask"], version = "^1.41.0"} pycountry = "^23.12.11" werkzeug = "^3.0.1" minio = "^7.2.5" -jaeger-client = "^4.8.0" attrs = "^23.2.0" sqlalchemy = "^2.0.28" itsdangerous = "^2.1.2" @@ -23,9 +22,10 @@ protobuf = "4.25.3" launchdarkly-server-sdk = "^9.2.2" cachecontrol = "^0.14.0" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "18263", subdirectory = "pay-api"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "pubsub_emulation", subdirectory = "pay-api"} flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +pg8000 = "^1.30.5" [tool.poetry.group.dev.dependencies] diff --git a/pay-queue/src/pay_queue/config.py b/pay-queue/src/pay_queue/config.py index 9e9f21106..9acc00a49 100644 --- a/pay-queue/src/pay_queue/config.py +++ b/pay-queue/src/pay_queue/config.py @@ -72,7 +72,12 @@ class _Config(): # pylint: disable=too-few-public-methods DB_NAME = os.getenv('DATABASE_NAME', '') DB_HOST = os.getenv('DATABASE_HOST', '') DB_PORT = os.getenv('DATABASE_PORT', '5432') - SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): + SQLALCHEMY_DATABASE_URI = ( + f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?unix_sock={DB_UNIX_SOCKET}/.s.PGSQL.5432' + ) + else: + SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' # Minio configuration values MINIO_ENDPOINT = os.getenv('MINIO_ENDPOINT') @@ -127,7 +132,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DB_PORT = os.getenv('DATABASE_TEST_PORT', '5432') SQLALCHEMY_DATABASE_URI = os.getenv( 'DATABASE_TEST_URL', - default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' + default=f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' ) USE_DOCKER_MOCK = os.getenv('USE_DOCKER_MOCK', None) @@ -146,6 +151,13 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods # Secret key for encrypting bank account ACCOUNT_SECRET_KEY = os.getenv('ACCOUNT_SECRET_KEY', 'test') + # Secrets for integration tests + TEST_GCP_PROJECT_NAME = 'abdefg-dev' + # Needs to have ftp-poller-dev in it. + TEST_GCP_TOPICS = ['account-mailer-dev', 'ftp-poller-dev', 'business-identifier-update-pay-dev'] + TEST_PUSH_ENDPOINT_PORT = 5020 + TEST_PUSH_ENDPOINT = os.getenv('TEST_PUSH_ENDPOINT', f'http://host.docker.internal:{str(TEST_PUSH_ENDPOINT_PORT)}/') + class ProdConfig(_Config): # pylint: disable=too-few-public-methods """Production environment configuration.""" diff --git a/pay-queue/src/pay_queue/resources/__init__.py b/pay-queue/src/pay_queue/resources/__init__.py index cca349370..5fa222686 100644 --- a/pay-queue/src/pay_queue/resources/__init__.py +++ b/pay-queue/src/pay_queue/resources/__init__.py @@ -15,7 +15,7 @@ from flask import Flask from pay_api.resources.ops import bp as ops_bp -from .worker import bp as worker_endpoint +from pay_queue.resources.worker import bp as worker_endpoint def register_endpoints(app: Flask): diff --git a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py index 6cdc36122..d63e23e29 100644 --- a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py +++ b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py @@ -419,7 +419,6 @@ def _shortname_balance_as_dict(eft_transactions: List[EFTRecord]) -> Dict: return shortname_balance -# TODO: THIS NEEDS TO CHANGE TO WORK ON THE CFS JOB instead. def _pay_invoice(invoice: InvoiceModel, shortname_balance: Dict): """Pay for an invoice and update invoice state.""" payment_date = shortname_balance.get('transaction_date') or datetime.now() diff --git a/pay-queue/tests/conftest.py b/pay-queue/tests/conftest.py index 3707d540c..66508ee24 100644 --- a/pay-queue/tests/conftest.py +++ b/pay-queue/tests/conftest.py @@ -16,6 +16,8 @@ import pytest from flask_migrate import Migrate, upgrade +from google.api_core.exceptions import NotFound +from google.cloud import pubsub from pay_api import db as _db from sqlalchemy import event, text from sqlalchemy_utils import create_database, database_exists, drop_database @@ -100,6 +102,7 @@ def auto(docker_services, app): docker_services.start('minio') docker_services.start('proxy') docker_services.start('paybc') + docker_services.start('pubsub-emulator') @pytest.fixture() @@ -112,3 +115,34 @@ def mock_publish(monkeypatch): def mock_queue_auth(mocker): """Mock queue authorization.""" mocker.patch('pay_queue.external.gcp_auth.verify_jwt', return_value='') + + +@pytest.fixture(scope='session', autouse=True) +def initialize_pubsub(app): + """Initialize pubsub emulator and respective publisher and subscribers.""" + os.environ['PUBSUB_EMULATOR_HOST'] = 'localhost:8085' + project = app.config.get('TEST_GCP_PROJECT_NAME') + topics = app.config.get('TEST_GCP_TOPICS') + push_config = pubsub.types.PushConfig(push_endpoint=app.config.get('TEST_PUSH_ENDPOINT')) + publisher = pubsub.PublisherClient() + subscriber = pubsub.SubscriberClient() + with publisher, subscriber: + for topic in topics: + topic_path = publisher.topic_path(project, topic) + try: + publisher.delete_topic(topic=topic_path) + except NotFound: + pass + publisher.create_topic(name=topic_path) + subscription_path = subscriber.subscription_path(project, f'{topic}_subscription') + try: + subscriber.delete_subscription(subscription=subscription_path) + except NotFound: + pass + subscriber.create_subscription( + request={ + 'name': subscription_path, + 'topic': topic_path, + 'push_config': push_config, + } + ) diff --git a/pay-queue/tests/docker-compose.yml b/pay-queue/tests/docker-compose.yml index 5d5bca67a..077e77a90 100644 --- a/pay-queue/tests/docker-compose.yml +++ b/pay-queue/tests/docker-compose.yml @@ -22,3 +22,10 @@ services: command: > mock -p 4010 --host 0.0.0.0 https://raw.githubusercontent.com/bcgov/sbc-pay/main/docs/docs/PayBC%20Mocking/paybc-1.0.0.yaml + pubsub-emulator: + image: gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators + command: gcloud beta emulators pubsub start --host-port=0.0.0.0:8085 + ports: + - "8085:8085" + extra_hosts: + - "host.docker.internal:host-gateway" diff --git a/pay-queue/tests/integration/test_cgi_reconciliations.py b/pay-queue/tests/integration/test_cgi_reconciliations.py index 1e7ccda87..8b3adaac9 100644 --- a/pay-queue/tests/integration/test_cgi_reconciliations.py +++ b/pay-queue/tests/integration/test_cgi_reconciliations.py @@ -16,7 +16,6 @@ Test-Suite to ensure that the Payment Reconciliation queue service is working as expected. """ - from datetime import datetime from pay_api.models import DistributionCode as DistributionCodeModel @@ -37,7 +36,7 @@ CfsAccountStatus, DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, RoutingSlipStatus) -from tests.integration.utils import helper_add_file_event_to_queue +from tests.integration.utils import add_file_event_to_queue_and_process from .factory import ( factory_create_ejv_account, factory_create_pad_account, factory_distribution, factory_invoice, @@ -105,9 +104,9 @@ def test_successful_partner_ejv_reconciliations(client): jv_file.close() # Now upload the ACK file to minio and publish message. - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -149,7 +148,7 @@ def test_successful_partner_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -158,7 +157,7 @@ def test_successful_partner_ejv_reconciliations(client): assert invoice.disbursement_status_code == DisbursementStatus.COMPLETED.value -def test_failed_partner_ejv_reconciliations(client, mock_publish): +def test_failed_partner_ejv_reconciliations(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -219,9 +218,9 @@ def test_failed_partner_ejv_reconciliations(client, mock_publish): jv_file.close() # Now upload the ACK file to minio and publish message. - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -263,7 +262,7 @@ def test_failed_partner_ejv_reconciliations(client, mock_publish): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -337,9 +336,9 @@ def test_successful_partner_reversal_ejv_reconciliations(client): jv_file.close() # Now upload the ACK file to minio and publish message. - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -381,7 +380,7 @@ def test_successful_partner_reversal_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -505,9 +504,9 @@ def test_succesful_payment_ejv_reconciliations(client): jv_file.close() # Now upload the ACK file to minio and publish message. - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -523,7 +522,7 @@ def test_succesful_payment_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -662,9 +661,9 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): jv_file.close() # Now upload the ACK file to minio and publish message. - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -680,7 +679,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -706,7 +705,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): assert payment[0][0].paid_amount == inv_total_amount -def test_successful_refund_reconciliations(client, mock_publish): +def test_successful_refund_reconciliations(client): """Test Reconciliations worker.""" # 1. Create a routing slip. # 2. Mark the routing slip for refund. @@ -752,9 +751,9 @@ def test_successful_refund_reconciliations(client, mock_publish): jv_file.close() # Now upload the ACK file to minio and publish message. - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -829,7 +828,7 @@ def test_successful_refund_reconciliations(client, mock_publish): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -839,7 +838,7 @@ def test_successful_refund_reconciliations(client, mock_publish): assert routing_slip.status == RoutingSlipStatus.REFUND_COMPLETED.value -def test_failed_refund_reconciliations(client, mock_publish): +def test_failed_refund_reconciliations(client): """Test Reconciliations worker.""" # 1. Create a routing slip. # 2. Mark the routing slip for refund. @@ -885,9 +884,9 @@ def test_failed_refund_reconciliations(client, mock_publish): jv_file.close() # Now upload the ACK file to minio and publish message. - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -963,7 +962,7 @@ def test_failed_refund_reconciliations(client, mock_publish): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -975,7 +974,7 @@ def test_failed_refund_reconciliations(client, mock_publish): assert routing_slip_2.status == RoutingSlipStatus.REFUND_REJECTED.value -def test_prevent_duplicate_ack(client, mock_publish): +def test_prevent_duplicate_ack(client): """Assert processing completes when existing ack.""" file_ref = f'INBOX.{datetime.now()}' # Upload an acknowledgement file @@ -989,18 +988,18 @@ def test_prevent_duplicate_ack(client, mock_publish): jv_file.write('') jv_file.close() - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value # Nothing should change, because it's already processed this ACK. ejv.disbursement_status_code = DisbursementStatus.UPLOADED.value - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.UPLOADED.value -def test_successful_ap_disbursement(client, mock_publish): +def test_successful_ap_disbursement(client): """Test Reconciliations worker for ap disbursement.""" # 1. Create invoice. # 2. Create a AP reconciliation file. @@ -1057,9 +1056,9 @@ def test_successful_ap_disbursement(client, mock_publish): jv_file.write('') jv_file.close() - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1132,8 +1131,7 @@ def test_successful_ap_disbursement(client, mock_publish): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - helper_add_file_event_to_queue(client, file_name=feedback_file_name, - message_type=MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value @@ -1149,7 +1147,7 @@ def test_successful_ap_disbursement(client, mock_publish): assert refund.gl_posted is not None -def test_failure_ap_disbursement(client, mock_publish): +def test_failure_ap_disbursement(client): """Test Reconciliations worker for ap disbursement.""" # 1. Create invoice. # 2. Create a AP reconciliation file. @@ -1204,9 +1202,9 @@ def test_failure_ap_disbursement(client, mock_publish): jv_file.write('') jv_file.close() - upload_to_minio(file_name=ack_file_name, value_as_bytes=str.encode('')) + upload_to_minio(str.encode(''), ack_file_name) - helper_add_file_event_to_queue(client, file_name=ack_file_name, message_type=MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1282,7 +1280,7 @@ def test_failure_ap_disbursement(client, mock_publish): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - helper_add_file_event_to_queue(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value diff --git a/pay-queue/tests/integration/test_eft_reconciliation.py b/pay-queue/tests/integration/test_eft_reconciliation.py index 3c44cf62d..5c7e9d081 100644 --- a/pay-queue/tests/integration/test_eft_reconciliation.py +++ b/pay-queue/tests/integration/test_eft_reconciliation.py @@ -31,11 +31,11 @@ from pay_queue.services.eft.eft_enums import EFTConstants from tests.integration.factory import factory_create_eft_account, factory_invoice -from tests.integration.utils import create_and_upload_eft_file, helper_add_file_event_to_queue +from tests.integration.utils import add_file_event_to_queue_and_process, create_and_upload_eft_file from tests.utilities.factory_utils import factory_eft_header, factory_eft_record, factory_eft_trailer -def test_eft_tdi17_fail_header(client, mock_publish): +def test_eft_tdi17_fail_header(client): """Test EFT Reconciliations properly fails for a bad EFT header.""" # Generate file with invalid header file_name: str = 'test_eft_tdi17.txt' @@ -44,7 +44,7 @@ def test_eft_tdi17_fail_header(client, mock_publish): create_and_upload_eft_file(file_name, [header]) - helper_add_file_event_to_queue(client, file_name, MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name, MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -87,7 +87,7 @@ def test_eft_tdi17_fail_header(client, mock_publish): assert not bool(eft_transactions) -def test_eft_tdi17_fail_trailer(client, mock_publish): +def test_eft_tdi17_fail_trailer(client): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' @@ -98,7 +98,7 @@ def test_eft_tdi17_fail_trailer(client, mock_publish): create_and_upload_eft_file(file_name, [header, trailer]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -141,7 +141,7 @@ def test_eft_tdi17_fail_trailer(client, mock_publish): assert not bool(eft_transactions) -def test_eft_tdi17_fail_transactions(client, mock_publish): +def test_eft_tdi17_fail_transactions(client): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' @@ -160,7 +160,7 @@ def test_eft_tdi17_fail_transactions(client, mock_publish): create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -198,13 +198,13 @@ def test_eft_tdi17_fail_transactions(client, mock_publish): assert eft_transactions[0].error_messages[0] == 'Invalid transaction deposit amount CAD.' -def test_eft_tdi17_basic_process(client, mock_publish): +def test_eft_tdi17_basic_process(client): """Test EFT Reconciliations worker is able to create basic EFT processing records.""" # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_basic_tdi17_file(file_name) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -276,14 +276,14 @@ def test_eft_tdi17_basic_process(client, mock_publish): assert not eft_credit_invoice_links -def test_eft_tdi17_process(client, mock_publish): +def test_eft_tdi17_process(client): """Test EFT Reconciliations worker.""" payment_account, eft_shortname, invoice = create_test_data() # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_tdi17_file(file_name) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -402,7 +402,7 @@ def test_eft_tdi17_process(client, mock_publish): # assert eft_credit_invoice_links[0].invoice_id == invoice.id -def test_eft_tdi17_rerun(client, mock_publish): +def test_eft_tdi17_rerun(client): """Test EFT Reconciliations can be re-executed with a corrected file.""" payment_account, eft_shortname, invoice = create_test_data() @@ -423,7 +423,7 @@ def test_eft_tdi17_rerun(client, mock_publish): create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -462,7 +462,7 @@ def test_eft_tdi17_rerun(client, mock_publish): jv_number='002425669', transaction_date='') create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Check file is completed after correction eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( diff --git a/pay-queue/tests/integration/test_payment_reconciliations.py b/pay-queue/tests/integration/test_payment_reconciliations.py index fe3eb034a..8e6850413 100644 --- a/pay-queue/tests/integration/test_payment_reconciliations.py +++ b/pay-queue/tests/integration/test_payment_reconciliations.py @@ -34,7 +34,7 @@ from .factory import ( factory_create_online_banking_account, factory_create_pad_account, factory_invoice, factory_invoice_reference, factory_payment, factory_payment_line_item, factory_receipt) -from .utils import create_and_upload_settlement_file, helper_add_file_event_to_queue +from .utils import add_file_event_to_queue_and_process, create_and_upload_settlement_file def test_online_banking_reconciliations(client): @@ -67,7 +67,7 @@ def test_online_banking_reconciliations(client): TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -113,7 +113,7 @@ def test_online_banking_reconciliations_over_payment(client): over_payment_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, over_payment_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -159,7 +159,7 @@ def test_online_banking_reconciliations_with_credit(client): credit_row = [RecordType.ONAC.value, SourceTransaction.EFT_WIRE.value, '555566677', 100001, date, credit_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -173,7 +173,7 @@ def test_online_banking_reconciliations_with_credit(client): assert payment.invoice_number == invoice_number -def test_online_banking_reconciliations_overflows_credit(client, mock_publish): +def test_online_banking_reconciliations_overflows_credit(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -210,7 +210,7 @@ def test_online_banking_reconciliations_overflows_credit(client, mock_publish): Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row, onac_row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -256,7 +256,7 @@ def test_online_banking_under_payment(client): TargetTransaction.INV.value, invoice_number, total, total - paid_amount, Status.PARTIAL.value] create_and_upload_settlement_file(file_name, [row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id) @@ -271,7 +271,7 @@ def test_online_banking_under_payment(client): assert payment.invoice_number == invoice_number -def test_pad_reconciliations(client, mock_publish): +def test_pad_reconciliations(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoices and related records @@ -313,7 +313,7 @@ def test_pad_reconciliations(client, mock_publish): 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -381,7 +381,7 @@ def test_pad_reconciliations_with_credit_memo(client): pad_row = [RecordType.PAD.value, SourceTransaction.PAD.value, receipt_number, 100001, date, total - credit_amount, cfs_account_number, 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [credit_row, pad_row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -403,7 +403,7 @@ def test_pad_reconciliations_with_credit_memo(client): assert rcpt1.receipt_date == rcpt2.receipt_date -def test_pad_nsf_reconciliations(client, mock_publish): +def test_pad_nsf_reconciliations(client): """Test Reconciliations worker for NSF.""" # 1. Create payment account # 2. Create invoices and related records @@ -446,7 +446,7 @@ def test_pad_nsf_reconciliations(client, mock_publish): 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -465,7 +465,7 @@ def test_pad_nsf_reconciliations(client, mock_publish): assert cfs_account.status == CfsAccountStatus.FREEZE.value -def test_pad_reversal_reconciliations(client, mock_publish): +def test_pad_reversal_reconciliations(client): """Test Reconciliations worker for NSF.""" # 1. Create payment account # 2. Create invoices and related records for a completed payment @@ -517,7 +517,7 @@ def test_pad_reversal_reconciliations(client, mock_publish): 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -541,7 +541,7 @@ def test_pad_reversal_reconciliations(client, mock_publish): @pytest.mark.asyncio -async def test_eft_wire_reconciliations(client, mock_publish): +async def test_eft_wire_reconciliations(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -582,7 +582,7 @@ async def test_eft_wire_reconciliations(client, mock_publish): row = [RecordType.EFTP.value, SourceTransaction.EFT_WIRE.value, eft_wire_receipt, 100001, date, total, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -657,7 +657,7 @@ def mock_cms(cfs_account: CfsAccountModel, cfs_account_number, TargetTransaction.RECEIPT.value, eft_wire_receipt, onac_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [row]) - helper_add_file_event_to_queue(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # Look up credit file and make sure the credits are recorded. pay_account = PaymentAccountModel.find_by_id(pay_account_id) diff --git a/pay-queue/tests/integration/utils.py b/pay-queue/tests/integration/utils.py index 65c54f8b8..58d190d4b 100644 --- a/pay-queue/tests/integration/utils.py +++ b/pay-queue/tests/integration/utils.py @@ -19,11 +19,15 @@ import os import uuid from datetime import datetime, timezone +from socket import SO_REUSEADDR, SOL_SOCKET, socket +from time import sleep from typing import List from flask import current_app from minio import Minio -from pay_api.utils.enums import MessageType +from pay_api.services import gcp_queue_publisher +from pay_api.services.gcp_queue_publisher import QueueMessage +from pay_api.utils.enums import MessageType, QueueSources from simple_cloudevent import SimpleCloudEvent, to_queue_message @@ -47,8 +51,9 @@ def build_request_for_queue_push(message_type, payload): def post_to_queue(client, request_payload): - """Post request to queue.""" - response = client.post('/', data=json.dumps(request_payload), headers={'Content-Type': 'application/json'}) + """Post request to worker using an http request on our wrapped flask instance.""" + response = client.post('/', data=json.dumps(request_payload), + headers={'Content-Type': 'application/json'}) assert response.status_code == 200 @@ -93,14 +98,49 @@ def upload_to_minio(value_as_bytes, file_name: str): os.stat(file_name).st_size) -def helper_add_file_event_to_queue(client, file_name: str, message_type: str): +def forward_incoming_message_to_test_instance(client): + """Forward incoming http message to test instance.""" + # Note this is a bit different than how the queue could behave, it could send multiples. + # This just receives one HTTP request and forwards it to the test instance. + # This is simpler than running another flask instance and tieing it to all the same as our unit tests. + with socket() as server_socket: + server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) + server_socket.settimeout(2) + server_socket.bind(('0.0.0.0', current_app.config.get('TEST_PUSH_ENDPOINT_PORT'))) + server_socket.listen(10) + tries = 100 + while tries > 0: + client_socket, _ = server_socket.accept() + if socket_data := client_socket.recv(4096): + body = socket_data.decode('utf8').split('\r\n')[-1] + payload = json.loads(body) + post_to_queue(client, payload) + client_socket.send('HTTP/1.1 200 OK\n\n'.encode('utf8')) + break + sleep(0.01) + tries -= 1 + assert tries > 0 + + +def add_file_event_to_queue_and_process(client, file_name: str, message_type: str, use_pubsub_emulator=True): """Add event to the Queue.""" queue_payload = { 'fileName': file_name, 'location': current_app.config['MINIO_BUCKET_NAME'] } - request_payload = build_request_for_queue_push(message_type, queue_payload) - post_to_queue(client, request_payload) + if use_pubsub_emulator: + gcp_queue_publisher.publish_to_queue( + QueueMessage( + source=QueueSources.FTP_POLLER.value, + message_type=message_type, + payload=queue_payload, + topic=f'projects/{current_app.config["TEST_GCP_PROJECT_NAME"]}/topics/ftp-poller-dev' + ) + ) + forward_incoming_message_to_test_instance(client) + else: + payload = build_request_for_queue_push(message_type, queue_payload) + post_to_queue(client, payload) def helper_add_identifier_event_to_queue(client, old_identifier: str = 'T1234567890', From 99ed7e0322bf8d3b6fc11cb11d29b119d065db28 Mon Sep 17 00:00:00 2001 From: Patrick Wei <44277752+pwei1018@users.noreply.github.com> Date: Sun, 17 Mar 2024 15:03:20 -0700 Subject: [PATCH 23/87] Upgrade CD flows for Payment projects. (#1449) * Upgrade CD flows for Payment projects. * fixed linting. --- .github/workflows/pay-admin-cd.yml | 119 +---- .github/workflows/pay-api-cd-gcp.yml | 30 -- .github/workflows/pay-api-cd.yml | 3 +- .github/workflows/pay-queue-cd.yml | 118 +---- .github/workflows/payment-jobs-cd.yml | 118 +---- jobs/payment-jobs/config.py | 15 +- .../devops/gcp/clouddeploy-targets.yaml | 86 ++++ jobs/payment-jobs/devops/vaults.gcp.env | 88 ++++ jobs/payment-jobs/devops/vaults.json | 63 --- .../openshift/payment-job-build.json | 139 ------ .../openshift/payment-job-deploy.json | 417 ------------------ .../tasks/statement_notification_task.py | 2 +- pay-admin/devops/gcp/clouddeploy-targets.yaml | 100 +++++ pay-admin/devops/vaults.gcp.env | 8 + pay-admin/devops/vaults.json | 15 - .../openshift/templates/pay-admin-build.json | 111 ----- .../openshift/templates/pay-admin-deploy.json | 316 ------------- pay-api/openshift/templates/dc.json | 368 ---------------- .../openshift/templates/pay-api-build.json | 111 ----- .../openshift/templates/pay-api-deploy.json | 360 --------------- pay-queue/devops/gcp/clouddeploy-targets.yaml | 100 +++++ pay-queue/devops/vaults.gcp.env | 25 ++ pay-queue/devops/vaults.json | 39 -- .../payment-reconciliations-build.json | 111 ----- .../payment-reconciliations-deploy.json | 200 --------- 25 files changed, 473 insertions(+), 2589 deletions(-) delete mode 100644 .github/workflows/pay-api-cd-gcp.yml create mode 100644 jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml create mode 100644 jobs/payment-jobs/devops/vaults.gcp.env delete mode 100644 jobs/payment-jobs/devops/vaults.json delete mode 100644 jobs/payment-jobs/openshift/payment-job-build.json delete mode 100644 jobs/payment-jobs/openshift/payment-job-deploy.json create mode 100644 pay-admin/devops/gcp/clouddeploy-targets.yaml create mode 100644 pay-admin/devops/vaults.gcp.env delete mode 100644 pay-admin/devops/vaults.json delete mode 100644 pay-admin/openshift/templates/pay-admin-build.json delete mode 100644 pay-admin/openshift/templates/pay-admin-deploy.json delete mode 100644 pay-api/openshift/templates/dc.json delete mode 100644 pay-api/openshift/templates/pay-api-build.json delete mode 100644 pay-api/openshift/templates/pay-api-deploy.json create mode 100644 pay-queue/devops/gcp/clouddeploy-targets.yaml create mode 100644 pay-queue/devops/vaults.gcp.env delete mode 100644 pay-queue/devops/vaults.json delete mode 100755 pay-queue/openshift/templates/payment-reconciliations-build.json delete mode 100755 pay-queue/openshift/templates/payment-reconciliations-deploy.json diff --git a/.github/workflows/pay-admin-cd.yml b/.github/workflows/pay-admin-cd.yml index 326adb54c..fb9d18ed8 100644 --- a/.github/workflows/pay-admin-cd.yml +++ b/.github/workflows/pay-admin-cd.yml @@ -4,111 +4,28 @@ on: push: branches: - main + - feature* paths: - "pay-admin/**" - - "pay-api/src/pay_api/models/**" workflow_dispatch: inputs: - environment: - description: "Environment (dev/test/prod)" + target: + description: "Deploy To" required: true - default: "dev" - -defaults: - run: - shell: bash - working-directory: ./pay-admin - -env: - APP_NAME: "pay-admin" - TAG_NAME: "dev" + type: choice + options: + - dev + - test + - sandbox + - prod jobs: - pay-admin-cd-by-push: - runs-on: ubuntu-20.04 - - if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' - environment: - name: "dev" - - steps: - - uses: actions/checkout@v4 - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Pay Admin Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} - - pay-admin-cd-by-dispatch: - runs-on: ubuntu-20.04 - - if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' - environment: - name: "${{ github.event.inputs.environment }}" - - steps: - - uses: actions/checkout@v4 - - name: Set env by input - run: | - echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Pay Admin Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} + pay-admin-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-admin" + working_directory: "./pay-admin" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file diff --git a/.github/workflows/pay-api-cd-gcp.yml b/.github/workflows/pay-api-cd-gcp.yml deleted file mode 100644 index a5fe4c8dc..000000000 --- a/.github/workflows/pay-api-cd-gcp.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Pay API CD - -on: - push: - branches: - - feature-queue-python-upgrade - paths: - - "pay-api/**" - workflow_dispatch: - inputs: - target: - description: "Deploy To" - required: true - type: choice - options: - - dev - - test - - sandbox - - prod - -jobs: - pay-api-cd: - uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main - with: - target: ${{ inputs.target }} - app_name: "pay-api" - working_directory: "./pay-api" - secrets: - WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file diff --git a/.github/workflows/pay-api-cd.yml b/.github/workflows/pay-api-cd.yml index 4373a3d51..0d2bb5020 100644 --- a/.github/workflows/pay-api-cd.yml +++ b/.github/workflows/pay-api-cd.yml @@ -3,7 +3,8 @@ name: Pay API CD on: push: branches: - - feature-queue-python-upgrade + - main + - feature* paths: - "pay-api/**" workflow_dispatch: diff --git a/.github/workflows/pay-queue-cd.yml b/.github/workflows/pay-queue-cd.yml index 48d125831..73e2b0518 100644 --- a/.github/workflows/pay-queue-cd.yml +++ b/.github/workflows/pay-queue-cd.yml @@ -4,112 +4,30 @@ on: push: branches: - main + - feature* paths: - "pay-queue/**" - "pay-api/src/pay_api/models/**" - "pay-api/src/pay_api/services/cfs_service.py" workflow_dispatch: inputs: - environment: - description: "Environment (dev/test/prod)" + target: + description: "Deploy To" required: true - default: "dev" - -defaults: - run: - shell: bash - working-directory: ./pay-queue - -env: - APP_NAME: "pay-queue" - TAG_NAME: "dev" + type: choice + options: + - dev + - test + - sandbox + - prod jobs: - pay-queue-cd-by-push: - runs-on: ubuntu-20.04 - - if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' - environment: - name: "dev" - - steps: - - uses: actions/checkout@v4 - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Pay Queue Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} - - pay-queue-cd-by-dispatch: - runs-on: ubuntu-20.04 - - if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' - environment: - name: "${{ github.event.inputs.environment }}" - - steps: - - uses: actions/checkout@v4 - - name: Set env by input - run: | - echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Pay Queue Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} + pay-queue-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-queue" + working_directory: "./pay-queue" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file diff --git a/.github/workflows/payment-jobs-cd.yml b/.github/workflows/payment-jobs-cd.yml index 93b190a5c..467f61096 100644 --- a/.github/workflows/payment-jobs-cd.yml +++ b/.github/workflows/payment-jobs-cd.yml @@ -4,112 +4,30 @@ on: push: branches: - main + - feature* paths: - "jobs/payment-jobs/**" - "pay-api/src/pay_api/models/**" - "pay-api/src/pay_api/services/cfs_service.py" workflow_dispatch: inputs: - environment: - description: "Environment (dev/test/prod)" + target: + description: "Deploy To" required: true - default: "dev" - -defaults: - run: - shell: bash - working-directory: ./jobs/payment-jobs - -env: - APP_NAME: "payment-job" - TAG_NAME: "dev" + type: choice + options: + - dev + - test + - sandbox + - prod jobs: - payment-jobs-cd-by-push: - runs-on: ubuntu-20.04 - - if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' - environment: - name: "dev" - - steps: - - uses: actions/checkout@v4 - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Payment Job Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} - - payment-jobs-cd-by-dispatch: - runs-on: ubuntu-20.04 - - if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' - environment: - name: "${{ github.event.inputs.environment }}" - - steps: - - uses: actions/checkout@v4 - - name: Set env by input - run: | - echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Payment Job Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} + payment-jobs-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-job-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "payment-jobs" + working_directory: "./jobs/payment-jobs" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index 426f77f01..558258fb5 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -95,8 +95,14 @@ class _Config(object): # pylint: disable=too-few-public-methods # legislative timezone for future effective dating LEGISLATIVE_TIMEZONE = os.getenv('LEGISLATIVE_TIMEZONE', 'America/Vancouver') - # notify-API URL - NOTIFY_API_URL = os.getenv('NOTIFY_API_URL') + # API Endpoints + AUTH_API_URL = os.getenv('AUTH_API_URL', '') + AUTH_API_VERSION = os.getenv('AUTH_API_VERSION', '') + NOTIFY_API_URL = os.getenv('NOTIFY_API_URL', '') + NOTIFY_API_VERSION = os.getenv('NOTIFY_API_VERSION', '') + + AUTH_API_ENDPOINT = f'{AUTH_API_URL + AUTH_API_VERSION}/' + NOTIFY_API_ENDPOINT = f'{NOTIFY_API_URL + NOTIFY_API_VERSION}/' # Service account details KEYCLOAK_SERVICE_ACCOUNT_ID = os.getenv('SBC_AUTH_ADMIN_CLIENT_ID') @@ -110,9 +116,6 @@ class _Config(object): # pylint: disable=too-few-public-methods AUTH_WEB_STATEMENT_URL = os.getenv('AUTH_WEB_STATEMENT_URL', 'account/orgId/settings/statements') REGISTRIES_LOGO_IMAGE_NAME = os.getenv('REGISTRIES_LOGO_IMAGE_NAME', 'bc_logo_for_email.png') - # Auth API Endpoint - AUTH_API_ENDPOINT = f'{os.getenv("AUTH_API_URL")}/' - CFS_ACCOUNT_DESCRIPTION = os.getenv('CFS_ACCOUNT_DESCRIPTION', 'BCR') CFS_INVOICE_PREFIX = os.getenv('CFS_INVOICE_PREFIX', 'REG') CFS_STOP_PAD_ACCOUNT_CREATION = os.getenv('CFS_STOP_PAD_ACCOUNT_CREATION', 'false').lower() == 'true' @@ -180,7 +183,7 @@ class _Config(object): # pylint: disable=too-few-public-methods CGI_AP_REMITTANCE_CODE = os.getenv('CGI_AP_REMITTANCE_CODE', '78') BCA_SUPPLIER_NUMBER = os.getenv('BCA_SUPPLIER_NUMBER', '') BCA_SUPPLIER_LOCATION = os.getenv('BCA_SUPPLIER_LOCATION', '') - + # FAS Client and secret CFS_FAS_CLIENT_ID = os.getenv('CFS_FAS_CLIENT_ID', '') CFS_FAS_CLIENT_SECRET = os.getenv('CFS_FAS_CLIENT_SECRET', '') diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml new file mode 100644 index 000000000..90c6c51b5 --- /dev/null +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -0,0 +1,86 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: dev +description: Dev Environment +deployParameters: + deploy-project-id: "gtksf3-dev" + job-name: "payment-jobs-dev" + app-env: "dev" + cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" + run-command: "./run.sh" +run: + location: projects/gtksf3-dev/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: test +description: Test Environment +deployParameters: + deploy-project-id: "gtksf3-test" + job-name: "payment-jobs-test" + app-env: "test" + cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" + run-command: "./run.sh" +run: + location: projects/gtksf3-test/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: sandbox +description: Sandbox Environment +requireApproval: true +deployParameters: + deploy-project-id: "gtksf3-integration" + job-name: "payment-jobs-sandbox" + app-env: "sandbox" + cloudsql-instances: "gtksf3-integration:northamerica-northeast1:pay-db-sandbox" + run-command: "./run.sh" +run: + location: projects/gtksf3-integration/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: prod +description: Production Environment +requireApproval: true +deployParameters: + deploy-project-id: "gtksf3-prod" + job-name: "payment-jobs-prod" + app-env: "production" + cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" + run-command: "./run.sh" +run: + location: projects/gtksf3-prod/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' \ No newline at end of file diff --git a/jobs/payment-jobs/devops/vaults.gcp.env b/jobs/payment-jobs/devops/vaults.gcp.env new file mode 100644 index 000000000..253a5b457 --- /dev/null +++ b/jobs/payment-jobs/devops/vaults.gcp.env @@ -0,0 +1,88 @@ +PAY_LD_SDK_KEY="op://launchdarkly/$APP_ENV/pay/PAY_LD_SDK_KEY" +DATABASE_NAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_NAME" +DATABASE_PASSWORD="op://database/$APP_ENV/pay-db-gcp/DATABASE_PASSWORD" +DATABASE_PORT="op://database/$APP_ENV/pay-db-gcp/DATABASE_PORT" +DATABASE_UNIX_SOCKET="op://database/$APP_ENV/pay-db-gcp/DATABASE_UNIX_SOCKET" +DATABASE_USERNAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_USERNAME" +JWT_OIDC_ISSUER="op://keycloak/$APP_ENV/jwt-base/JWT_OIDC_ISSUER" +SBC_AUTH_ADMIN_CLIENT_ID="op://keycloak/$APP_ENV/sbc-auth-admin/SBC_AUTH_ADMIN_CLIENT_ID" +SBC_AUTH_ADMIN_CLIENT_SECRET="op://keycloak/$APP_ENV/sbc-auth-admin/SBC_AUTH_ADMIN_CLIENT_SECRET" +CFS_BASE_URL="op://payment-external-services/$APP_ENV/cfs/CFS_BASE_URL" +CFS_CLIENT_ID="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_ID" +CFS_CLIENT_SECRET="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_SECRET" +CONNECT_TIMEOUT="op://payment-external-services/$APP_ENV/cfs/CONNECT_TIMEOUT" +CFS_GENERATE_RANDOM_INVOICE_NUMBER="op://payment-external-services/$APP_ENV/cfs/CFS_GENERATE_RANDOM_INVOICE_NUMBER" +CFS_ACCOUNT_DESCRIPTION="op://payment-external-services/$APP_ENV/cfs/CFS_ACCOUNT_DESCRIPTION" +CFS_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_INVOICE_PREFIX" +CFS_PARTY_PREFIX="op://payment-external-services/$APP_ENV/cfs/CFS_PARTY_PREFIX" +CFS_STOP_PAD_ACCOUNT_CREATION="op://payment-external-services/$APP_ENV/cfs/CFS_STOP_PAD_ACCOUNT_CREATION" +CFS_INVOICE_CUT_OFF_HOURS_UTC="op://payment-external-services/$APP_ENV/cfs/CFS_INVOICE_CUT_OFF_HOURS_UTC" +CFS_INVOICE_CUT_OFF_MINUTES_UTC="op://payment-external-services/$APP_ENV/cfs/CFS_INVOICE_CUT_OFF_MINUTES_UTC" +CFS_FAS_CLIENT_ID="op://payment-external-services/$APP_ENV/cfs/CFS_FAS_CLIENT_ID" +CFS_FAS_CLIENT_SECRET="op://payment-external-services/$APP_ENV/cfs/CFS_FAS_CLIENT_SECRET" +EFT_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/eft/EFT_INVOICE_PREFIX" +EFT_HOLDING_GL="" +EFT_TRANSFER_DESC="op://payment-external-services/$APP_ENV/eft/EFT_TRANSFER_DESC" +PAYBC_DIRECT_PAY_REF_NUMBER="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_REF_NUMBER" +PAYBC_DIRECT_PAY_API_KEY="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_API_KEY" +PAYBC_DIRECT_PAY_BASE_URL="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_BASE_URL" +PAYBC_DIRECT_PAY_CLIENT_ID="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CLIENT_ID" +PAYBC_DIRECT_PAY_CLIENT_SECRET="op://payment-external-services/$APP_ENV/paybc/PAYBC_DIRECT_PAY_CLIENT_SECRET" +AUDIENCE="op://gcp-queue/$APP_ENV/base/AUDIENCE" +GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/base/GCP_AUTH_KEY" +PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" +ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" +AUTH_API_URL="op://API/$APP_ENV/auth-api/AUTH_API_URL" +AUTH_API_VERSION="op://API/$APP_ENV/auth-api/AUTH_API_VERSION" +NOTIFY_API_URL="op://API/$APP_ENV/notify-api/NOTIFY_API_URL" +NOTIFY_API_VERSION="op://API/$APP_ENV/notify-api/NOTIFY_API_VERSION" +SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" +SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" +PAD_CONFIRMATION_PERIOD_IN_DAYS="op://relationship/$APP_ENV/pay-api/PAD_CONFIRMATION_PERIOD_IN_DAYS" +DISABLE_VALID_REDIRECT_URLS="op://relationship/$APP_ENV/pay-api/DISABLE_VALID_REDIRECT_URLS" +VALID_REDIRECT_URLS="op://relationship/$APP_ENV/pay-api/VALID_REDIRECT_URLS" +TRANSACTION_REPORT_DEFAULT_TOTAL="op://relationship/$APP_ENV/pay-api/TRANSACTION_REPORT_DEFAULT_TOTAL" +ROUTING_SLIP_DEFAULT_TOTAL="op://relationship/$APP_ENV/pay-api/ROUTING_SLIP_DEFAULT_TOTAL" +LEGISLATIVE_TIMEZONE="op://relationship/$APP_ENV/pay-api/LEGISLATIVE_TIMEZONE" +BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS="op://relationship/$APP_ENV/pay-api/BCOL_USERNAME_FOR_SERVICE_ACCOUNT_PAYMENTS" +MASK_LEN="op://relationship/$APP_ENV/pay-api/MASK_LEN" +DISABLE_ACTIVITY_LOGS="op://relationship/$APP_ENV/pay-api/DISABLE_ACTIVITY_LOGS" +ACCOUNT_SECRET_KEY="op://relationship/$APP_ENV/pay-api/ACCOUNT_SECRET_KEY" +MINIO_ENDPOINT="op://minio/$APP_ENV/base/MINIO_ENDPOINT" +MINIO_ACCESS_KEY="op://minio/$APP_ENV/base/MINIO_ACCESS_KEY" +MINIO_ACCESS_SECRET="op://minio/$APP_ENV/base/MINIO_ACCESS_SECRET" +MINIO_SECURE="op://minio/$APP_ENV/payment-jobs/MINIO_SECURE" +MINIO_EJV_BUCKET_NAME="op://minio/$APP_ENV/payment-jobs/MINIO_EJV_BUCKET_NAME" +CGI_FEEDER_NUMBER="op://relationship/$APP_ENV/payment-jobs/CGI_FEEDER_NUMBER" +CGI_MINISTRY_PREFIX="op://relationship/$APP_ENV/payment-jobs/CGI_MINISTRY_PREFIX" +CGI_DISBURSEMENT_DESC="op://relationship/$APP_ENV/payment-jobs/CGI_DISBURSEMENT_DESC" +CGI_MESSAGE_VERSION="op://relationship/$APP_ENV/payment-jobs/CGI_MESSAGE_VERSION" +CGI_BCREG_CLIENT_CODE="op://relationship/$APP_ENV/payment-jobs/CGI_BCREG_CLIENT_CODE" +CGI_EJV_SUPPLIER_NUMBER="op://relationship/$APP_ENV/payment-jobs/CGI_EJV_SUPPLIER_NUMBER" +CGI_TRIGGER_FILE_SUFFIX="op://relationship/$APP_ENV/payment-jobs/CGI_TRIGGER_FILE_SUFFIX" +NOTIFY_AFTER_DAYS="op://relationship/$APP_ENV/payment-jobs/NOTIFY_AFTER_DAYS" +CGI_AP_DISTRIBUTION="op://relationship/$APP_ENV/payment-jobs/CGI_AP_DISTRIBUTION" +CGI_AP_SUPPLIER_NUMBER="op://relationship/$APP_ENV/payment-jobs/CGI_AP_SUPPLIER_NUMBER" +CGI_AP_SUPPLIER_LOCATION="op://relationship/$APP_ENV/payment-jobs/CGI_AP_SUPPLIER_LOCATION" +CGI_AP_REMITTANCE_CODE="op://relationship/$APP_ENV/payment-jobs/CGI_AP_REMITTANCE_CODE" +BCA_SUPPLIER_NUMBER="op://relationship/$APP_ENV/payment-jobs/BCA_SUPPLIER_NUMBER" +BCA_SUPPLIER_LOCATION="op://relationship/$APP_ENV/payment-jobs/BCA_SUPPLIER_LOCATION" +CGI_SFTP_HOST="op://relationship/$APP_ENV/ftp-poller/CAS_SFTP_HOST" +CGI_SFTP_USERNAME="op://relationship/$APP_ENV/ftp-poller/CAS_SFTP_USER_NAME" +CGI_SFTP_PASSWORD="" +CGI_SFTP_VERIFY_HOST="op://relationship/$APP_ENV/ftp-poller/SFTP_VERIFY_HOST" +CGI_SFTP_HOST_KEY="op://relationship/$APP_ENV/ftp-poller/CAS_SFTP_HOST_KEY" +CGI_SFTP_PORT="21" +BCREG_CGI_FTP_PRIVATE_KEY="op://relationship/$APP_ENV/ftp-poller/BCREG_FTP_PRIVATE_KEY" +BCREG_CGI_FTP_PRIVATE_KEY_PASSPHRASE="op://relationship/$APP_ENV/ftp-poller/BCREG_FTP_PRIVATE_KEY_PASSPHRASE" +CGI_SFTP_DIRECTORY="op://relationship/$APP_ENV/ftp-poller/CAS_SFTP_DIRECTORY" +AUTH_WEB_PAY_TRANSACTION_URL="op://web-url/$APP_ENV/auth-web/AUTH_WEB_URL" +AUTH_WEB_STATEMENT_URL="""op://web-url/$APP_ENV/auth-web/AUTH_WEB_URL" +REGISTRIES_LOGO_IMAGE_NAME="bc_logo_for_email.png" +DISBURSEMENT_DELAY="5" +DISABLE_CFS_FAS_INTEGRATION="false" +ORACLE_USER="" +ORACLE_PASSWORD="" +ORACLE_DB_NAME="" +ORACLE_HOST="" +ORACLE_PORT="1521" \ No newline at end of file diff --git a/jobs/payment-jobs/devops/vaults.json b/jobs/payment-jobs/devops/vaults.json deleted file mode 100644 index 6cd1c8ebf..000000000 --- a/jobs/payment-jobs/devops/vaults.json +++ /dev/null @@ -1,63 +0,0 @@ -[ - { - "vault": "shared", - "application": [ - "api-endpoints", - "encryption-key" - ] - }, - { - "vault": "keycloak", - "application": [ - "jwt-base", - "sbc-auth-admin" - ] - }, - { - "vault": "payment-external-services", - "application": [ - "paybc", - "cfs" - ] - }, - { - "vault": "relationship", - "application": [ - "postgres-pay", - "pay-api", - "jwt", - "payment-jobs", - "ftp-poller" - ] - }, - { - "vault": "sentry", - "application": [ - "relationship-api" - ] - }, - { - "vault": "minio", - "application": [ - "payment-jobs" - ] - }, - { - "vault": "minio", - "application": [ - "base" - ] - }, - { - "vault": "launchdarkly", - "application": [ - "pay" - ] - }, - { - "vault": "entity", - "application": [ - "colin-api" - ] - } -] diff --git a/jobs/payment-jobs/openshift/payment-job-build.json b/jobs/payment-jobs/openshift/payment-job-build.json deleted file mode 100644 index 2960fadd2..000000000 --- a/jobs/payment-jobs/openshift/payment-job-build.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a payments job.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build-template" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "payment-job" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "jobs/payment-jobs" - }, - { - "name": "SOURCE_IMAGE_KIND", - "displayName": "Source Image Kind", - "required": true, - "description": "The 'kind' (type) of the source image; typically ImageStreamTag, or DockerImage.", - "value": "ImageStreamTag" - }, - { - "name": "SOURCE_IMAGE_NAME_SPACE", - "displayName": "Source Image Name Space", - "required": true, - "description": "The name space of the source image.", - "value": "d7eovc-tools" - }, - { - "name": "SOURCE_IMAGE_NAME", - "displayName": "Source Image Name", - "required": true, - "description": "The name of the source image.", - "value": "python" - }, - { - "name": "SOURCE_IMAGE_TAG", - "displayName": "Source Image Tag", - "required": true, - "description": "The tag of the source image.", - "value": "3.7" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/jobs/payment-jobs/openshift/payment-job-deploy.json b/jobs/payment-jobs/openshift/payment-job-deploy.json deleted file mode 100644 index 58d062e75..000000000 --- a/jobs/payment-jobs/openshift/payment-job-deploy.json +++ /dev/null @@ -1,417 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a payment job.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": 1, - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "volumes": [ - { - "name": "cron-config", - "configMap": { - "name": "${NAME}-${TAG_NAME}-cron-configuration", - "defaultMode": 420 - } - }, - { - "name": "sftp-private-key", - "configMap": { - "name": "ftp-poller-${TAG_NAME}-sftp-configuration", - "defaultMode": 420 - } - } - ], - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "volumeMounts": [ - { - "name": "cron-config", - "readOnly": true, - "mountPath": "/payment-jobs/cron/" - }, - { - "name": "sftp-private-key", - "readOnly": true, - "mountPath": "/payment-jobs/key/" - } - ], - "env": [ - { - "name": "DATABASE_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_USER" - } - } - }, - { - "name": "DATABASE_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PASSWORD" - } - } - }, - { - "name": "DATABASE_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_NAME" - } - } - }, - { - "name": "DATABASE_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_HOST" - } - } - }, - { - "name": "DATABASE_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_PORT" - } - } - }, - { - "name": "DATABASE_TEST_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_USER" - } - } - }, - { - "name": "DATABASE_TEST_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_TEST_PASSWORD" - } - } - }, - { - "name": "DATABASE_TEST_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_NAME" - } - } - }, - { - "name": "DATABASE_TEST_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_HOST" - } - } - }, - { - "name": "DATABASE_TEST_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_PORT" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_REF_NUMBER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_REF_NUMBER" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_API_KEY", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_API_KEY" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_BASE_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_BASE_URL" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_ID" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_SECRET" - } - } - }, - { - "name": "NOTIFY_API_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "NOTIFY_API_URL" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_ID" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_SECRET" - } - } - }, - { - "name": "JWT_OIDC_ISSUER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "JWT_OIDC_ISSUER" - } - } - }, - { - "name": "AUTH_WEB_PAY_TRANSACTION_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "AUTH_WEB_PAY_TRANSACTION_URL" - } - } - } - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "creationTimestamp": null, - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "payment-job" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} \ No newline at end of file diff --git a/jobs/payment-jobs/tasks/statement_notification_task.py b/jobs/payment-jobs/tasks/statement_notification_task.py index 25fee2a62..927bd368c 100644 --- a/jobs/payment-jobs/tasks/statement_notification_task.py +++ b/jobs/payment-jobs/tasks/statement_notification_task.py @@ -116,7 +116,7 @@ def send_email(cls, token, recipients: str, """Send the email asynchronously, using the given details.""" subject = 'Your BC Registries statement is available' current_app.logger.info(f'send_email to recipients: {recipients}') - notify_url = current_app.config.get('NOTIFY_API_URL') + '/notify/' + notify_url = current_app.config.get('NOTIFY_API_ENDPOINT') + 'notify/' notify_body = { 'recipients': recipients, 'content': { diff --git a/pay-admin/devops/gcp/clouddeploy-targets.yaml b/pay-admin/devops/gcp/clouddeploy-targets.yaml new file mode 100644 index 000000000..6a9ffc00d --- /dev/null +++ b/pay-admin/devops/gcp/clouddeploy-targets.yaml @@ -0,0 +1,100 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: dev +description: Dev Environment +deployParameters: + deploy-project-id: "gtksf3-dev" + service-name: "pay-admin-dev" + container-name: "pay-admin-dev" + app-env: "dev" + cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" + service-account: "sa-api@gtksf3-dev.iam.gserviceaccount.com" +run: + location: projects/gtksf3-dev/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: test +description: Test Environment +deployParameters: + deploy-project-id: "gtksf3-test" + service-name: "pay-admin-test" + container-name: "pay-admin-test" + app-env: "test" + cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" + service-account: "sa-api@gtksf3-test.iam.gserviceaccount.com" +run: + location: projects/gtksf3-test/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: sandbox +description: Sandbox Environment +requireApproval: true +deployParameters: + deploy-project-id: "gtksf3-tools" + service-name: "pay-admin-sandbox" + container-name: "pay-admin-sandbox" + app-env: "sandbox" + cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" + service-account: "sa-api@gtksf3-tools.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-tools/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: prod +description: Production Environment +requireApproval: true +deployParameters: + deploy-project-id: "gtksf3-prod" + service-name: "pay-admin-prod" + container-name: "pay-admin-prod" + app-env: "production" + cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" + service-account: "sa-api@gtksf3-prod.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-prod/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' \ No newline at end of file diff --git a/pay-admin/devops/vaults.gcp.env b/pay-admin/devops/vaults.gcp.env new file mode 100644 index 000000000..f7a3b4608 --- /dev/null +++ b/pay-admin/devops/vaults.gcp.env @@ -0,0 +1,8 @@ +DATABASE_NAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_NAME" +DATABASE_PASSWORD="op://database/$APP_ENV/pay-db-gcp/DATABASE_PASSWORD" +DATABASE_PORT="op://database/$APP_ENV/pay-db-gcp/DATABASE_PORT" +DATABASE_UNIX_SOCKET="op://database/$APP_ENV/pay-db-gcp/DATABASE_UNIX_SOCKET" +DATABASE_USERNAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_USERNAME" + +PAY_OIDC_CLIENT_SECRETS="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_CLIENT_SECRETS" +PAY_OIDC_ID_TOKEN_COOKIE_SECURE="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_ID_TOKEN_COOKIE_SECURE" diff --git a/pay-admin/devops/vaults.json b/pay-admin/devops/vaults.json deleted file mode 100644 index 11934b144..000000000 --- a/pay-admin/devops/vaults.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "vault": "relationship", - "application": [ - "postgres-pay", - "pay-admin" - ] - }, - { - "vault": "sentry", - "application": [ - "relationship-api" - ] - } -] diff --git a/pay-admin/openshift/templates/pay-admin-build.json b/pay-admin/openshift/templates/pay-admin-build.json deleted file mode 100644 index 51676aab7..000000000 --- a/pay-admin/openshift/templates/pay-admin-build.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a pay-admin service.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "pay-admin" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "main" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "pay-admin" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/pay-admin/openshift/templates/pay-admin-deploy.json b/pay-admin/openshift/templates/pay-admin-deploy.json deleted file mode 100644 index 76948762d..000000000 --- a/pay-admin/openshift/templates/pay-admin-deploy.json +++ /dev/null @@ -1,316 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a pay admin service.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "apps.openshift.io/v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "environment": "${TAG_NAME}", - "name": "${NAME}", - "role": "api" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - }, - "resources": { - }, - "activeDeadlineSeconds": 21600 - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "78c88a-tools", - "name": "${NAME}:${TAG_NAME}" - }, - "lastTriggeredImage": "image-registry.openshift-image-registry.svc:5000/78c88a-tools/${NAME}@sha256:66652d7231c3257c3b6752c58775ece775c9ddea3ef1e3423476c1c5fc4af99e" - } - } - ], - "replicas": 1, - "revisionHistoryLimit": 10, - "test": false, - "selector": { - "environment": "dev", - "name": "${NAME}" - }, - "template": { - "metadata": { - "creationTimestamp": null, - "labels": { - "environment": "dev", - "name": "${NAME}", - "role": "api" - } - }, - "spec": { - "volumes": [ - { - "configMap": { - "defaultMode": 420, - "name": "${NAME}-keycloak-configuration" - }, - "name": "${NAME}-keycloak-configuration" - } - ], - "containers": [ - { - "resources": { - "limits": { - "cpu": "750m", - "memory": "2Gi" - }, - "requests": { - "cpu": "100m", - "memory": "100Mi" - } - }, - "readinessProbe": { - "httpGet": { - "path": "/admin", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "name": "${NAME}-${TAG_NAME}", - "livenessProbe": { - "httpGet": { - "path": "/admin", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "env": null, - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "imagePullPolicy": "Always", - "volumeMounts": [ - { - "name": "${NAME}-keycloak-configuration", - "readOnly": true, - "mountPath": "/app/config" - } - ], - "terminationMessagePolicy": "File", - "image": "image-registry.openshift-image-registry.svc:5000/78c88a-tools/${NAME}@sha256:66652d7231c3257c3b6752c58775ece775c9ddea3ef1e3423476c1c5fc4af99e" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": { - }, - "schedulerName": "default-scheduler" - } - } - }, - "status": { - "observedGeneration": 5940, - "details": { - "message": "image change", - "causes": [ - { - "type": "ImageChange", - "imageTrigger": { - "from": { - "kind": "DockerImage", - "name": "image-registry.openshift-image-registry.svc:5000/78c88a-tools/${NAME}@sha256:66652d7231c3257c3b6752c58775ece775c9ddea3ef1e3423476c1c5fc4af99e" - } - } - } - ] - }, - "availableReplicas": 1, - "unavailableReplicas": 0, - "latestVersion": 271, - "updatedReplicas": 1, - "replicas": 1, - "readyReplicas": 1 - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app-group": "${APP_GROUP}", - "environment": "${TAG_NAME}", - "name": "${NAME}", - "role": "api" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "Route", - "apiVersion": "route.openshift.io/v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "environment": "${TAG_NAME}", - "name": "${NAME}", - "role": "api" - } - }, - "spec": { - "host": "${NAME}-${TAG_NAME}.apps.silver.devops.gov.bc.ca", - "to": { - "kind": "Service", - "name": "${NAME}-${TAG_NAME}", - "weight": 100 - }, - "port": { - "targetPort": "8080-tcp" - }, - "tls": { - "termination": "edge", - "insecureEdgeTerminationPolicy": "Redirect" - }, - "wildcardPolicy": "None" - }, - "status": { - "ingress": [ - { - "host": "${NAME}-${TAG_NAME}.apps.silver.devops.gov.bc.ca", - "routerName": "default", - "conditions": [ - { - "type": "Admitted", - "status": "True" - } - ], - "wildcardPolicy": "None", - "routerCanonicalHostname": "apps.silver.devops.gov.bc.ca" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "pay-admin" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} \ No newline at end of file diff --git a/pay-api/openshift/templates/dc.json b/pay-api/openshift/templates/dc.json deleted file mode 100644 index f69e36efe..000000000 --- a/pay-api/openshift/templates/dc.json +++ /dev/null @@ -1,368 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a pay api service.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%", - "pre": { - "failurePolicy": "Abort", - "execNewPod": { - "command": [ - "/opt/app-root/src/pre-hook-update-db.sh" - ], - "env": [ - { - "name": "DATABASE_ADMIN_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_ADMIN_PASSWORD" - } - } - }, - { - "name": "DATABASE_USERNAME", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_USER" - } - } - }, - { - "name": "DATABASE_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PASSWORD" - } - } - }, - { - "name": "DATABASE_NAME", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_NAME" - } - } - }, - { - "name": "DATABASE_HOST", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_HOST" - } - } - }, - { - "name": "DATABASE_PORT", - "valueFrom": { - "secretKeyRef": { - "name": "${NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PORT" - } - } - } - ], - "containerName": "${NAME}-${TAG_NAME}" - } - } - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": "${REPLICAS}", - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/ops/healthz", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "readinessProbe": { - "httpGet": { - "path": "/ops/readyz", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - }, - { - "name": "jaeger-agent", - "image": "jaegertracing/jaeger-agent", - "ports": [ - { - "containerPort": 5775, - "protocol": "UDP" - }, - { - "containerPort": 5778, - "protocol": "UDP" - }, - { - "containerPort": 6831, - "protocol": "UDP" - }, - { - "containerPort": 6832, - "protocol": "UDP" - } - ], - "args": [ - "${JAEGER_COLLECTOR}" - ] - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "creationTimestamp": null, - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "Route", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "host": "${NAME}-${TAG_NAME}.pathfinder.gov.bc.ca", - "to": { - "kind": "Service", - "name": "${NAME}-${TAG_NAME}", - "weight": 100 - }, - "port": { - "targetPort": "8080-tcp" - }, - "tls": { - "termination": "edge" - }, - "wildcardPolicy": "None" - }, - "status": { - "ingress": [ - { - "host": "${NAME}-${TAG_NAME}.pathfinder.gov.bc.ca", - "routerName": "router", - "conditions": [ - { - "type": "Admitted", - "status": "True" - } - ], - "wildcardPolicy": "None" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "pay-api" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - }, - { - "name": "JAEGER_COLLECTOR", - "displayName": "Jaeger Tracing collector address", - "description": "Jaeger Tracing collector address.", - "required": true, - "value": "--collector.host-port=jaeger-collector.d7eovc-${TAG_NAME}.svc:14267" - } - ] -} diff --git a/pay-api/openshift/templates/pay-api-build.json b/pay-api/openshift/templates/pay-api-build.json deleted file mode 100644 index fd0f06445..000000000 --- a/pay-api/openshift/templates/pay-api-build.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a pay-api service.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "pay-api" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "pay-api" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/pay-api/openshift/templates/pay-api-deploy.json b/pay-api/openshift/templates/pay-api-deploy.json deleted file mode 100644 index 895623d11..000000000 --- a/pay-api/openshift/templates/pay-api-deploy.json +++ /dev/null @@ -1,360 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a pay api service.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%", - "pre": { - "failurePolicy": "Abort", - "execNewPod": { - "command": [ - "/opt/app-root/pre-hook-update-db.sh" - ], - "env": [ - { - "name": "DATABASE_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_USER" - } - } - }, - { - "name": "DATABASE_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PASSWORD" - } - } - }, - { - "name": "DATABASE_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_NAME" - } - } - }, - { - "name": "DATABASE_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_HOST" - } - } - }, - { - "name": "DATABASE_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_PORT" - } - } - } - ], - "containerName": "${NAME}-${TAG_NAME}" - } - } - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": "${REPLICAS}", - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "env": [], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/ops/healthz", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "readinessProbe": { - "httpGet": { - "path": "/ops/readyz", - "port": 8080, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - }, - { - "name": "jaeger-agent", - "image": "jaegertracing/jaeger-agent", - "ports": [ - { - "containerPort": 5775, - "protocol": "UDP" - }, - { - "containerPort": 5778, - "protocol": "UDP" - }, - { - "containerPort": 6831, - "protocol": "UDP" - }, - { - "containerPort": 6832, - "protocol": "UDP" - } - ], - "args": [ - "${JAEGER_COLLECTOR}" - ] - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "creationTimestamp": null, - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "Route", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "host": "${NAME}-${TAG_NAME}.pathfinder.gov.bc.ca", - "to": { - "kind": "Service", - "name": "${NAME}-${TAG_NAME}", - "weight": 100 - }, - "port": { - "targetPort": "8080-tcp" - }, - "tls": { - "termination": "edge" - }, - "wildcardPolicy": "None" - }, - "status": { - "ingress": [ - { - "host": "${NAME}-${TAG_NAME}.pathfinder.gov.bc.ca", - "routerName": "router", - "conditions": [ - { - "type": "Admitted", - "status": "True" - } - ], - "wildcardPolicy": "None" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "pay-api" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - }, - { - "name": "JAEGER_COLLECTOR", - "displayName": "Jaeger Tracing collector address", - "description": "Jaeger Tracing collector address.", - "required": true, - "value": "--collector.host-port=jaeger-collector.d7eovc-${TAG_NAME}.svc:14267" - } - ] -} diff --git a/pay-queue/devops/gcp/clouddeploy-targets.yaml b/pay-queue/devops/gcp/clouddeploy-targets.yaml new file mode 100644 index 000000000..b9a6b1542 --- /dev/null +++ b/pay-queue/devops/gcp/clouddeploy-targets.yaml @@ -0,0 +1,100 @@ +# Copyright 2022 Google LLCpay-queue +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: dev +description: Dev Environment +deployParameters: + deploy-project-id: "gtksf3-dev" + service-name: "pay-queue-dev" + container-name: "pay-queue-dev" + app-env: "dev" + cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" + service-account: "sa-api@gtksf3-dev.iam.gserviceaccount.com" +run: + location: projects/gtksf3-dev/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: test +description: Test Environment +deployParameters: + deploy-project-id: "gtksf3-test" + service-name: "pay-queue-test" + container-name: "pay-queue-test" + app-env: "test" + cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" + service-account: "sa-api@gtksf3-test.iam.gserviceaccount.com" +run: + location: projects/gtksf3-test/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: sandbox +description: Sandbox Environment +requireApproval: true +deployParameters: + deploy-project-id: "gtksf3-tools" + service-name: "pay-queue-sandbox" + container-name: "pay-queue-sandbox" + app-env: "sandbox" + cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" + service-account: "sa-api@gtksf3-tools.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-tools/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' +--- + +apiVersion: deploy.cloud.google.com/v1 +kind: Target +metadata: + name: prod +description: Production Environment +requireApproval: true +deployParameters: + deploy-project-id: "gtksf3-prod" + service-name: "pay-queue-prod" + container-name: "pay-queue-prod" + app-env: "production" + cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" + service-account: "sa-api@gtksf3-prod.iam.gserviceaccount.com" + max-scale: "50" + container-concurrency: "20" + container-port: "8080" + resources-cpu: 4000m + resources-memory: 8Gi +run: + location: projects/gtksf3-prod/locations/northamerica-northeast1 +executionConfigs: +- usages: [DEPLOY, RENDER] + artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' \ No newline at end of file diff --git a/pay-queue/devops/vaults.gcp.env b/pay-queue/devops/vaults.gcp.env new file mode 100644 index 000000000..08cac8d26 --- /dev/null +++ b/pay-queue/devops/vaults.gcp.env @@ -0,0 +1,25 @@ +PAY_LD_SDK_KEY="op://launchdarkly/$APP_ENV/pay/PAY_LD_SDK_KEY" +DATABASE_NAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_NAME" +DATABASE_PASSWORD="op://database/$APP_ENV/pay-db-gcp/DATABASE_PASSWORD" +DATABASE_PORT="op://database/$APP_ENV/pay-db-gcp/DATABASE_PORT" +DATABASE_UNIX_SOCKET="op://database/$APP_ENV/pay-db-gcp/DATABASE_UNIX_SOCKET" +DATABASE_USERNAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_USERNAME" +MINIO_ENDPOINT="op://minio/$APP_ENV/base/MINIO_ENDPOINT" +MINIO_ACCESS_KEY="op://minio/$APP_ENV/base/MINIO_ACCESS_KEY" +MINIO_ACCESS_SECRET="op://minio/$APP_ENV/base/MINIO_ACCESS_SECRET" +MINIO_SECURE="op://minio/$APP_ENV/payment-reconciliations/MINIO_SECURE" +CFS_BASE_URL="op://payment-external-services/$APP_ENV/cfs/CFS_BASE_URL" +CFS_CLIENT_ID="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_ID" +CFS_CLIENT_SECRET="op://payment-external-services/$APP_ENV/cfs/CFS_CLIENT_SECRET" +CONNECT_TIMEOUT="op://payment-external-services/$APP_ENV/cfs/CONNECT_TIMEOUT" +EFT_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/eft/EFT_INVOICE_PREFIX" +AUDIENCE="op://gcp-queue/$APP_ENV/base/AUDIENCE" +GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/base/GCP_AUTH_KEY" +PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" +ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" +SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" +SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" +LEGISLATIVE_TIMEZONE="op://relationship/$APP_ENV/pay-api/LEGISLATIVE_TIMEZONE" +ACCOUNT_SECRET_KEY="op://relationship/$APP_ENV/pay-api/ACCOUNT_SECRET_KEY" +DISABLE_EJV_ERROR_EMAIL="True" +DISABLE_PAD_SUCCESS_EMAIL="False" \ No newline at end of file diff --git a/pay-queue/devops/vaults.json b/pay-queue/devops/vaults.json deleted file mode 100644 index 6b84697ac..000000000 --- a/pay-queue/devops/vaults.json +++ /dev/null @@ -1,39 +0,0 @@ -[ - { - "vault": "shared", - "application": [ - "encryption-key" - ] - }, - { - "vault": "minio", - "application": [ - "base", - "payment-reconciliations" - ] - }, - { - "vault": "payment-external-services", - "application": [ - "cfs" - ] - }, - { - "vault": "relationship", - "application": [ - "postgres-pay" - ] - }, - { - "vault": "sentry", - "application": [ - "relationship-api" - ] - }, - { - "vault": "launchdarkly", - "application": [ - "pay" - ] - } -] diff --git a/pay-queue/openshift/templates/payment-reconciliations-build.json b/pay-queue/openshift/templates/payment-reconciliations-build.json deleted file mode 100755 index 729bce130..000000000 --- a/pay-queue/openshift/templates/payment-reconciliations-build.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a payment reconciliations.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "payment-reconciliations" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "queue_services/payment-reconciliations" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} \ No newline at end of file diff --git a/pay-queue/openshift/templates/payment-reconciliations-deploy.json b/pay-queue/openshift/templates/payment-reconciliations-deploy.json deleted file mode 100755 index 327e02db0..000000000 --- a/pay-queue/openshift/templates/payment-reconciliations-deploy.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for payment reconciliations service.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": "${REPLICAS}", - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "env": [ - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/healthz", - "port": 7070, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "readinessProbe": { - "httpGet": { - "path": "/readyz", - "port": 7070, - "scheme": "HTTP" - }, - "timeoutSeconds": 1, - "periodSeconds": 10, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "payment-reconciliations" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} From edaa85763856e34f0aa887f5029e40784240d22c Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Mon, 18 Mar 2024 09:35:44 -0700 Subject: [PATCH 24/87] Fixed empty value issue. --- pay-admin/devops/vaults.gcp.env | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pay-admin/devops/vaults.gcp.env b/pay-admin/devops/vaults.gcp.env index f7a3b4608..5cc42836e 100644 --- a/pay-admin/devops/vaults.gcp.env +++ b/pay-admin/devops/vaults.gcp.env @@ -3,6 +3,5 @@ DATABASE_PASSWORD="op://database/$APP_ENV/pay-db-gcp/DATABASE_PASSWORD" DATABASE_PORT="op://database/$APP_ENV/pay-db-gcp/DATABASE_PORT" DATABASE_UNIX_SOCKET="op://database/$APP_ENV/pay-db-gcp/DATABASE_UNIX_SOCKET" DATABASE_USERNAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_USERNAME" - PAY_OIDC_CLIENT_SECRETS="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_CLIENT_SECRETS" -PAY_OIDC_ID_TOKEN_COOKIE_SECURE="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_ID_TOKEN_COOKIE_SECURE" +PAY_OIDC_ID_TOKEN_COOKIE_SECURE="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_ID_TOKEN_COOKIE_SECURE" \ No newline at end of file From eb377122f92a5b5e24e98873eedb2ff0ba333cee Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Mon, 18 Mar 2024 09:37:56 -0700 Subject: [PATCH 25/87] Fixed deployment config issue. --- jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml index 90c6c51b5..5577883ac 100644 --- a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -55,13 +55,13 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: - deploy-project-id: "gtksf3-integration" + deploy-project-id: "gtksf3-tools" job-name: "payment-jobs-sandbox" app-env: "sandbox" - cloudsql-instances: "gtksf3-integration:northamerica-northeast1:pay-db-sandbox" + cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" run-command: "./run.sh" run: - location: projects/gtksf3-integration/locations/northamerica-northeast1 + location: projects/gtksf3-tools/locations/northamerica-northeast1 executionConfigs: - usages: [DEPLOY, RENDER] artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' From 0c85bcad03b2a8a5ebcf8d5a390d81456712cf6f Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Mon, 18 Mar 2024 09:57:47 -0700 Subject: [PATCH 26/87] Fixed deployment environment variables issue. --- jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml | 4 ++++ jobs/payment-jobs/invoke_jobs.py | 2 +- pay-admin/admin/__init__.py | 2 +- pay-admin/devops/gcp/clouddeploy-targets.yaml | 4 ++++ pay-api/devops/gcp/clouddeploy-targets.yaml | 4 ++++ pay-api/src/pay_api/__init__.py | 2 +- pay-queue/devops/gcp/clouddeploy-targets.yaml | 4 ++++ pay-queue/src/pay_queue/__init__.py | 2 +- 8 files changed, 20 insertions(+), 4 deletions(-) diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml index 5577883ac..c6b49860c 100644 --- a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -18,6 +18,7 @@ metadata: name: dev description: Dev Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-dev" job-name: "payment-jobs-dev" app-env: "dev" @@ -36,6 +37,7 @@ metadata: name: test description: Test Environment deployParameters: + deploy-env: "testing" deploy-project-id: "gtksf3-test" job-name: "payment-jobs-test" app-env: "test" @@ -55,6 +57,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: + deploy-env: "sandbox" deploy-project-id: "gtksf3-tools" job-name: "payment-jobs-sandbox" app-env: "sandbox" @@ -74,6 +77,7 @@ metadata: description: Production Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-prod" job-name: "payment-jobs-prod" app-env: "production" diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index 47845a253..cc89471b0 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -34,7 +34,7 @@ setup_logging(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first -def create_app(run_mode=os.getenv('FLASK_ENV', 'production'), job_name='unknown', init_oracle=False): +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production'), job_name='unknown', init_oracle=False): """Return a configured Flask App using the Factory method.""" from pay_api.models import db, ma diff --git a/pay-admin/admin/__init__.py b/pay-admin/admin/__init__.py index 7dd1f3e92..411a5a317 100755 --- a/pay-admin/admin/__init__.py +++ b/pay-admin/admin/__init__.py @@ -33,7 +33,7 @@ setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) -def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.config.from_object(config.CONFIGURATION[run_mode]) diff --git a/pay-admin/devops/gcp/clouddeploy-targets.yaml b/pay-admin/devops/gcp/clouddeploy-targets.yaml index 6a9ffc00d..83ca85ae5 100644 --- a/pay-admin/devops/gcp/clouddeploy-targets.yaml +++ b/pay-admin/devops/gcp/clouddeploy-targets.yaml @@ -18,6 +18,7 @@ metadata: name: dev description: Dev Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-dev" service-name: "pay-admin-dev" container-name: "pay-admin-dev" @@ -37,6 +38,7 @@ metadata: name: test description: Test Environment deployParameters: + deploy-env: "testing" deploy-project-id: "gtksf3-test" service-name: "pay-admin-test" container-name: "pay-admin-test" @@ -57,6 +59,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: + deploy-env: "sandbox" deploy-project-id: "gtksf3-tools" service-name: "pay-admin-sandbox" container-name: "pay-admin-sandbox" @@ -82,6 +85,7 @@ metadata: description: Production Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-prod" service-name: "pay-admin-prod" container-name: "pay-admin-prod" diff --git a/pay-api/devops/gcp/clouddeploy-targets.yaml b/pay-api/devops/gcp/clouddeploy-targets.yaml index 45fffe18f..5831bd1a9 100644 --- a/pay-api/devops/gcp/clouddeploy-targets.yaml +++ b/pay-api/devops/gcp/clouddeploy-targets.yaml @@ -18,6 +18,7 @@ metadata: name: dev description: Dev Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-dev" service-name: "pay-api-dev" container-name: "pay-api-dev" @@ -37,6 +38,7 @@ metadata: name: test description: Test Environment deployParameters: + deploy-env: "testing" deploy-project-id: "gtksf3-test" service-name: "pay-api-test" container-name: "pay-api-test" @@ -57,6 +59,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: + deploy-env: "sandbox" deploy-project-id: "gtksf3-tools" service-name: "pay-api-sandbox" container-name: "pay-api-sandbox" @@ -82,6 +85,7 @@ metadata: description: Production Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-prod" service-name: "pay-api-prod" container-name: "pay-api-prod" diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index 0af1e12ea..fcf351816 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -39,7 +39,7 @@ setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) -def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.env = run_mode diff --git a/pay-queue/devops/gcp/clouddeploy-targets.yaml b/pay-queue/devops/gcp/clouddeploy-targets.yaml index b9a6b1542..8d78b712e 100644 --- a/pay-queue/devops/gcp/clouddeploy-targets.yaml +++ b/pay-queue/devops/gcp/clouddeploy-targets.yaml @@ -18,6 +18,7 @@ metadata: name: dev description: Dev Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-dev" service-name: "pay-queue-dev" container-name: "pay-queue-dev" @@ -37,6 +38,7 @@ metadata: name: test description: Test Environment deployParameters: + deploy-env: "testing" deploy-project-id: "gtksf3-test" service-name: "pay-queue-test" container-name: "pay-queue-test" @@ -57,6 +59,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: + deploy-env: "sandbox" deploy-project-id: "gtksf3-tools" service-name: "pay-queue-sandbox" container-name: "pay-queue-sandbox" @@ -82,6 +85,7 @@ metadata: description: Production Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-prod" service-name: "pay-queue-prod" container-name: "pay-queue-prod" diff --git a/pay-queue/src/pay_queue/__init__.py b/pay-queue/src/pay_queue/__init__.py index 331be818d..01c2cf845 100644 --- a/pay-queue/src/pay_queue/__init__.py +++ b/pay-queue/src/pay_queue/__init__.py @@ -33,7 +33,7 @@ from .services import queue -def create_app(run_mode=os.getenv('FLASK_ENV', 'production')) -> Flask: +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')) -> Flask: """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.env = run_mode From dfae89db2f2809c7eaecde71e8ac556b16c0cab2 Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Mon, 18 Mar 2024 10:16:38 -0700 Subject: [PATCH 27/87] Fixed the configuration. --- jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml | 4 ++-- pay-admin/devops/gcp/clouddeploy-targets.yaml | 4 ++-- pay-api/devops/gcp/clouddeploy-targets.yaml | 4 ++-- pay-queue/devops/gcp/clouddeploy-targets.yaml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml index c6b49860c..832f169ce 100644 --- a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -37,7 +37,7 @@ metadata: name: test description: Test Environment deployParameters: - deploy-env: "testing" + deploy-env: "development" deploy-project-id: "gtksf3-test" job-name: "payment-jobs-test" app-env: "test" @@ -57,7 +57,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: - deploy-env: "sandbox" + deploy-env: "production" deploy-project-id: "gtksf3-tools" job-name: "payment-jobs-sandbox" app-env: "sandbox" diff --git a/pay-admin/devops/gcp/clouddeploy-targets.yaml b/pay-admin/devops/gcp/clouddeploy-targets.yaml index 83ca85ae5..2dca1c847 100644 --- a/pay-admin/devops/gcp/clouddeploy-targets.yaml +++ b/pay-admin/devops/gcp/clouddeploy-targets.yaml @@ -38,7 +38,7 @@ metadata: name: test description: Test Environment deployParameters: - deploy-env: "testing" + deploy-env: "development" deploy-project-id: "gtksf3-test" service-name: "pay-admin-test" container-name: "pay-admin-test" @@ -59,7 +59,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: - deploy-env: "sandbox" + deploy-env: "production" deploy-project-id: "gtksf3-tools" service-name: "pay-admin-sandbox" container-name: "pay-admin-sandbox" diff --git a/pay-api/devops/gcp/clouddeploy-targets.yaml b/pay-api/devops/gcp/clouddeploy-targets.yaml index 5831bd1a9..578fdbcd0 100644 --- a/pay-api/devops/gcp/clouddeploy-targets.yaml +++ b/pay-api/devops/gcp/clouddeploy-targets.yaml @@ -38,7 +38,7 @@ metadata: name: test description: Test Environment deployParameters: - deploy-env: "testing" + deploy-env: "development" deploy-project-id: "gtksf3-test" service-name: "pay-api-test" container-name: "pay-api-test" @@ -59,7 +59,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: - deploy-env: "sandbox" + deploy-env: "production" deploy-project-id: "gtksf3-tools" service-name: "pay-api-sandbox" container-name: "pay-api-sandbox" diff --git a/pay-queue/devops/gcp/clouddeploy-targets.yaml b/pay-queue/devops/gcp/clouddeploy-targets.yaml index 8d78b712e..580546cf0 100644 --- a/pay-queue/devops/gcp/clouddeploy-targets.yaml +++ b/pay-queue/devops/gcp/clouddeploy-targets.yaml @@ -38,7 +38,7 @@ metadata: name: test description: Test Environment deployParameters: - deploy-env: "testing" + deploy-env: "development" deploy-project-id: "gtksf3-test" service-name: "pay-queue-test" container-name: "pay-queue-test" @@ -59,7 +59,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: - deploy-env: "sandbox" + deploy-env: "production" deploy-project-id: "gtksf3-tools" service-name: "pay-queue-sandbox" container-name: "pay-queue-sandbox" From 1f0a84be5d322736937caa6ecf76d5524974021d Mon Sep 17 00:00:00 2001 From: Patrick Wei <44277752+pwei1018@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:22:45 -0700 Subject: [PATCH 28/87] Fixed CD issues. (#1452) * Fixed empty value issue. * Fixed deployment config issue. * Fixed deployment environment variables issue. * Fixed the configuration. --- jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml | 10 +++++++--- jobs/payment-jobs/invoke_jobs.py | 2 +- pay-admin/admin/__init__.py | 2 +- pay-admin/devops/gcp/clouddeploy-targets.yaml | 4 ++++ pay-admin/devops/vaults.gcp.env | 3 +-- pay-api/devops/gcp/clouddeploy-targets.yaml | 4 ++++ pay-api/src/pay_api/__init__.py | 2 +- pay-queue/devops/gcp/clouddeploy-targets.yaml | 4 ++++ pay-queue/src/pay_queue/__init__.py | 2 +- 9 files changed, 24 insertions(+), 9 deletions(-) diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml index 90c6c51b5..832f169ce 100644 --- a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -18,6 +18,7 @@ metadata: name: dev description: Dev Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-dev" job-name: "payment-jobs-dev" app-env: "dev" @@ -36,6 +37,7 @@ metadata: name: test description: Test Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-test" job-name: "payment-jobs-test" app-env: "test" @@ -55,13 +57,14 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: - deploy-project-id: "gtksf3-integration" + deploy-env: "production" + deploy-project-id: "gtksf3-tools" job-name: "payment-jobs-sandbox" app-env: "sandbox" - cloudsql-instances: "gtksf3-integration:northamerica-northeast1:pay-db-sandbox" + cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" run-command: "./run.sh" run: - location: projects/gtksf3-integration/locations/northamerica-northeast1 + location: projects/gtksf3-tools/locations/northamerica-northeast1 executionConfigs: - usages: [DEPLOY, RENDER] artifactStorage: 'gs://c4hnrd-tools_clouddeploy/history' @@ -74,6 +77,7 @@ metadata: description: Production Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-prod" job-name: "payment-jobs-prod" app-env: "production" diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index 47845a253..cc89471b0 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -34,7 +34,7 @@ setup_logging(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first -def create_app(run_mode=os.getenv('FLASK_ENV', 'production'), job_name='unknown', init_oracle=False): +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production'), job_name='unknown', init_oracle=False): """Return a configured Flask App using the Factory method.""" from pay_api.models import db, ma diff --git a/pay-admin/admin/__init__.py b/pay-admin/admin/__init__.py index 7dd1f3e92..411a5a317 100755 --- a/pay-admin/admin/__init__.py +++ b/pay-admin/admin/__init__.py @@ -33,7 +33,7 @@ setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) -def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.config.from_object(config.CONFIGURATION[run_mode]) diff --git a/pay-admin/devops/gcp/clouddeploy-targets.yaml b/pay-admin/devops/gcp/clouddeploy-targets.yaml index 6a9ffc00d..2dca1c847 100644 --- a/pay-admin/devops/gcp/clouddeploy-targets.yaml +++ b/pay-admin/devops/gcp/clouddeploy-targets.yaml @@ -18,6 +18,7 @@ metadata: name: dev description: Dev Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-dev" service-name: "pay-admin-dev" container-name: "pay-admin-dev" @@ -37,6 +38,7 @@ metadata: name: test description: Test Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-test" service-name: "pay-admin-test" container-name: "pay-admin-test" @@ -57,6 +59,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-tools" service-name: "pay-admin-sandbox" container-name: "pay-admin-sandbox" @@ -82,6 +85,7 @@ metadata: description: Production Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-prod" service-name: "pay-admin-prod" container-name: "pay-admin-prod" diff --git a/pay-admin/devops/vaults.gcp.env b/pay-admin/devops/vaults.gcp.env index f7a3b4608..5cc42836e 100644 --- a/pay-admin/devops/vaults.gcp.env +++ b/pay-admin/devops/vaults.gcp.env @@ -3,6 +3,5 @@ DATABASE_PASSWORD="op://database/$APP_ENV/pay-db-gcp/DATABASE_PASSWORD" DATABASE_PORT="op://database/$APP_ENV/pay-db-gcp/DATABASE_PORT" DATABASE_UNIX_SOCKET="op://database/$APP_ENV/pay-db-gcp/DATABASE_UNIX_SOCKET" DATABASE_USERNAME="op://database/$APP_ENV/pay-db-gcp/DATABASE_USERNAME" - PAY_OIDC_CLIENT_SECRETS="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_CLIENT_SECRETS" -PAY_OIDC_ID_TOKEN_COOKIE_SECURE="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_ID_TOKEN_COOKIE_SECURE" +PAY_OIDC_ID_TOKEN_COOKIE_SECURE="op://relationship/$APP_ENV/pay-admin/PAY_OIDC_ID_TOKEN_COOKIE_SECURE" \ No newline at end of file diff --git a/pay-api/devops/gcp/clouddeploy-targets.yaml b/pay-api/devops/gcp/clouddeploy-targets.yaml index 45fffe18f..578fdbcd0 100644 --- a/pay-api/devops/gcp/clouddeploy-targets.yaml +++ b/pay-api/devops/gcp/clouddeploy-targets.yaml @@ -18,6 +18,7 @@ metadata: name: dev description: Dev Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-dev" service-name: "pay-api-dev" container-name: "pay-api-dev" @@ -37,6 +38,7 @@ metadata: name: test description: Test Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-test" service-name: "pay-api-test" container-name: "pay-api-test" @@ -57,6 +59,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-tools" service-name: "pay-api-sandbox" container-name: "pay-api-sandbox" @@ -82,6 +85,7 @@ metadata: description: Production Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-prod" service-name: "pay-api-prod" container-name: "pay-api-prod" diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index 0af1e12ea..fcf351816 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -39,7 +39,7 @@ setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) -def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.env = run_mode diff --git a/pay-queue/devops/gcp/clouddeploy-targets.yaml b/pay-queue/devops/gcp/clouddeploy-targets.yaml index b9a6b1542..580546cf0 100644 --- a/pay-queue/devops/gcp/clouddeploy-targets.yaml +++ b/pay-queue/devops/gcp/clouddeploy-targets.yaml @@ -18,6 +18,7 @@ metadata: name: dev description: Dev Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-dev" service-name: "pay-queue-dev" container-name: "pay-queue-dev" @@ -37,6 +38,7 @@ metadata: name: test description: Test Environment deployParameters: + deploy-env: "development" deploy-project-id: "gtksf3-test" service-name: "pay-queue-test" container-name: "pay-queue-test" @@ -57,6 +59,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-tools" service-name: "pay-queue-sandbox" container-name: "pay-queue-sandbox" @@ -82,6 +85,7 @@ metadata: description: Production Environment requireApproval: true deployParameters: + deploy-env: "production" deploy-project-id: "gtksf3-prod" service-name: "pay-queue-prod" container-name: "pay-queue-prod" diff --git a/pay-queue/src/pay_queue/__init__.py b/pay-queue/src/pay_queue/__init__.py index 331be818d..01c2cf845 100644 --- a/pay-queue/src/pay_queue/__init__.py +++ b/pay-queue/src/pay_queue/__init__.py @@ -33,7 +33,7 @@ from .services import queue -def create_app(run_mode=os.getenv('FLASK_ENV', 'production')) -> Flask: +def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')) -> Flask: """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.env = run_mode From 7d6fbe269ad60eb9201d0b15b5312a40eb9f89db Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Mon, 18 Mar 2024 11:59:16 -0700 Subject: [PATCH 29/87] Fixed missing service account. --- jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml index 832f169ce..96fde8372 100644 --- a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -24,6 +24,7 @@ deployParameters: app-env: "dev" cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" run-command: "./run.sh" + service-account: "sa-job@gtksf3-dev.iam.gserviceaccount.com" run: location: projects/gtksf3-dev/locations/northamerica-northeast1 executionConfigs: @@ -43,6 +44,7 @@ deployParameters: app-env: "test" cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" run-command: "./run.sh" + service-account: "sa-job@gtksf3-test.iam.gserviceaccount.com" run: location: projects/gtksf3-test/locations/northamerica-northeast1 executionConfigs: @@ -63,6 +65,7 @@ deployParameters: app-env: "sandbox" cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" run-command: "./run.sh" + service-account: "sa-job@gtksf3-tools.iam.gserviceaccount.com" run: location: projects/gtksf3-tools/locations/northamerica-northeast1 executionConfigs: @@ -83,6 +86,7 @@ deployParameters: app-env: "production" cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" run-command: "./run.sh" + service-account: "sa-job@gtksf3-prod.iam.gserviceaccount.com" run: location: projects/gtksf3-prod/locations/northamerica-northeast1 executionConfigs: From d8c12797fed5e3a3e4f3bb50bc72c15c07b8f54c Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Mon, 18 Mar 2024 09:37:56 -0700 Subject: [PATCH 30/87] Fixed deployment config issue. --- jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml index 832f169ce..f33596742 100644 --- a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -57,7 +57,10 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: +<<<<<<< HEAD deploy-env: "production" +======= +>>>>>>> Fixed deployment config issue. deploy-project-id: "gtksf3-tools" job-name: "payment-jobs-sandbox" app-env: "sandbox" From 051575c3f8afa636acab0146960263258ea0670e Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Mon, 18 Mar 2024 09:57:47 -0700 Subject: [PATCH 31/87] Fixed deployment environment variables issue. --- jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml index f33596742..832f169ce 100644 --- a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -57,10 +57,7 @@ metadata: description: Sandbox Environment requireApproval: true deployParameters: -<<<<<<< HEAD deploy-env: "production" -======= ->>>>>>> Fixed deployment config issue. deploy-project-id: "gtksf3-tools" job-name: "payment-jobs-sandbox" app-env: "sandbox" From 9a871a88f0251e016c2cf97c9dfc7d0f7e37156e Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Mon, 18 Mar 2024 11:59:16 -0700 Subject: [PATCH 32/87] Fixed missing service account. --- jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml index 832f169ce..96fde8372 100644 --- a/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml +++ b/jobs/payment-jobs/devops/gcp/clouddeploy-targets.yaml @@ -24,6 +24,7 @@ deployParameters: app-env: "dev" cloudsql-instances: "gtksf3-dev:northamerica-northeast1:pay-db-dev" run-command: "./run.sh" + service-account: "sa-job@gtksf3-dev.iam.gserviceaccount.com" run: location: projects/gtksf3-dev/locations/northamerica-northeast1 executionConfigs: @@ -43,6 +44,7 @@ deployParameters: app-env: "test" cloudsql-instances: "gtksf3-test:northamerica-northeast1:pay-db-test" run-command: "./run.sh" + service-account: "sa-job@gtksf3-test.iam.gserviceaccount.com" run: location: projects/gtksf3-test/locations/northamerica-northeast1 executionConfigs: @@ -63,6 +65,7 @@ deployParameters: app-env: "sandbox" cloudsql-instances: "gtksf3-tools:northamerica-northeast1:pay-db-sandbox" run-command: "./run.sh" + service-account: "sa-job@gtksf3-tools.iam.gserviceaccount.com" run: location: projects/gtksf3-tools/locations/northamerica-northeast1 executionConfigs: @@ -83,6 +86,7 @@ deployParameters: app-env: "production" cloudsql-instances: "gtksf3-prod:northamerica-northeast1:pay-db-prod" run-command: "./run.sh" + service-account: "sa-job@gtksf3-prod.iam.gserviceaccount.com" run: location: projects/gtksf3-prod/locations/northamerica-northeast1 executionConfigs: From ff34e907b6157b466aec1f393fb38b34ce920eda Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Mon, 18 Mar 2024 12:50:54 -0700 Subject: [PATCH 33/87] 19875 - PAY Jobs - Disbursement Process handle Partial Refunds (#1428) --- jobs/payment-jobs/poetry.lock | 257 ++++++++++-------- jobs/payment-jobs/pyproject.toml | 2 +- jobs/payment-jobs/tasks/ap_task.py | 11 +- jobs/payment-jobs/tasks/eft_transfer_task.py | 12 +- .../tasks/ejv_partner_distribution_task.py | 115 ++++++-- jobs/payment-jobs/tasks/ejv_payment_task.py | 13 +- jobs/payment-jobs/tests/jobs/factory.py | 20 +- .../tests/jobs/test_eft_transfer_task.py | 6 +- .../test_ejv_partner_distribution_task.py | 4 +- ...artner_partial_refund_distribution_task.py | 86 ++++++ .../tests/jobs/test_ejv_payment_task.py | 11 +- ...024_03_06_04b8a7bed74e_ejv_link_generic.py | 39 +++ pay-api/poetry.lock | 116 ++++---- pay-api/src/pay_api/models/__init__.py | 2 +- .../{ejv_invoice_link.py => ejv_link.py} | 17 +- pay-api/src/pay_api/models/refunds_partial.py | 6 +- pay-api/src/pay_api/utils/enums.py | 7 + pay-queue/poetry.lock | 18 +- pay-queue/pyproject.toml | 2 +- .../pay_queue/services/cgi_reconciliations.py | 26 +- .../integration/test_cgi_reconciliations.py | 56 ++-- 21 files changed, 560 insertions(+), 266 deletions(-) create mode 100644 jobs/payment-jobs/tests/jobs/test_ejv_partner_partial_refund_distribution_task.py create mode 100644 pay-api/migrations/versions/2024_03_06_04b8a7bed74e_ejv_link_generic.py rename pay-api/src/pay_api/models/{ejv_invoice_link.py => ejv_link.py} (80%) diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock index b71776aa8..b4942360b 100644 --- a/jobs/payment-jobs/poetry.lock +++ b/jobs/payment-jobs/poetry.lock @@ -119,17 +119,17 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.0.4" +version = "2.1.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, - {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, + {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, + {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, ] [package.dependencies] -pycodestyle = ">=2.10.0" +pycodestyle = ">=2.11.0" [[package]] name = "bcrypt" @@ -184,13 +184,13 @@ files = [ [[package]] name = "cachelib" -version = "0.12.0" +version = "0.9.0" description = "A collection of cache libraries in the same API interface." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "cachelib-0.12.0-py3-none-any.whl", hash = "sha256:038f4d855afc3eb8caab10458f6eac55c328911f9055824c22c2f259ef9ed3a3"}, - {file = "cachelib-0.12.0.tar.gz", hash = "sha256:8243029a028436fd23229113dee517c0700bb43a8a289ec5a963e4af9ca2b194"}, + {file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"}, + {file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"}, ] [[package]] @@ -428,63 +428,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.3" +version = "7.4.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, - {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, - {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, - {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, - {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, - {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, - {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, - {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, - {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, - {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, - {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, - {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, - {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, - {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, ] [package.extras] @@ -784,17 +784,17 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "1.11.1" +version = "2.1.0" description = "Adds caching support to Flask applications." optional = false python-versions = ">=3.7" files = [ - {file = "Flask-Caching-1.11.1.tar.gz", hash = "sha256:28af189e97defb9e39b43ebe197b54a58aaee81bdeb759f46d969c26d7aa7810"}, - {file = "Flask_Caching-1.11.1-py3-none-any.whl", hash = "sha256:36592812eec6cba86eca48bcda74eff24bfd6c8eaf6056ca0184474bb78c0dc4"}, + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, ] [package.dependencies] -cachelib = "*" +cachelib = ">=0.9.0,<0.10.0" Flask = "*" [[package]] @@ -1602,52 +1602,97 @@ gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] invoke = ["invoke (>=2.0)"] [[package]] -name = "pay_api" -version = "0.0.0" -description = "A short description of the project" +name = "pay-api" +version = "0.1.0" +description = "" optional = false -python-versions = ">=3.12" +python-versions = "^3.12" files = [] develop = false [package.dependencies] -cattrs = "*" -croniter = "*" -cryptography = "*" -dpath = "*" -Flask = "*" -Flask-Caching = "*" -Flask-Cors = "*" -flask-jwt-oidc = "*" -flask-marshmallow = "*" -Flask-Migrate = "*" -Flask-Moment = "*" -Flask-Script = "*" -Flask-SQLAlchemy = "*" +alembic = "1.13.1" +attrs = "23.2.0" +blinker = "1.7.0" +cachelib = "0.9.0" +cachetools = "5.3.3" +cattrs = "23.2.3" +certifi = "2024.2.2" +cffi = "1.16.0" +charset-normalizer = "3.3.2" +click = "8.1.7" +croniter = "2.0.2" +cryptography = "42.0.5" +dpath = "2.1.6" +ecdsa = "0.18.0" +expiringdict = "1.2.2" +flask = "3.0.2" +flask-caching = "2.1.0" +flask-cors = "4.0.0" +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +flask-marshmallow = "1.2.0" +flask-migrate = "4.0.7" +flask-moment = "1.0.5" +flask-script = "2.0.6" +flask-sqlalchemy = "3.1.1" +google-api-core = "2.17.1" google-auth = "2.28.1" google-cloud-pubsub = "2.20.0" -gunicorn = "*" +googleapis-common-protos = "1.63.0" +greenlet = "3.0.3" +grpc-google-iam-v1 = "0.13.0" +grpcio = "1.62.1" +grpcio-status = "1.62.1" +gunicorn = "21.2.0" holidays = "0.37" -itsdangerous = "*" -jaeger-client = "*" -Jinja2 = "*" +idna = "3.6" +itsdangerous = "2.1.2" +jaeger-client = "4.8.0" +jinja2 = "3.1.3" jsonschema = "4.17.3" -launchdarkly-server-sdk = "*" -marshmallow-sqlalchemy = "*" -psycopg2-binary = "*" -pyhumps = "*" -python-dotenv = "*" -requests = "*" -sentry-sdk = {version = "*", extras = ["flask"]} -sqlalchemy = "*" -sqlalchemy_utils = "*" -Werkzeug = "*" +launchdarkly-eventsource = "1.1.1" +launchdarkly-server-sdk = "9.2.2" +mako = "1.3.2" +markupsafe = "2.1.5" +marshmallow = "3.21.1" +marshmallow-sqlalchemy = "1.0.0" +opentracing = "2.4.0" +packaging = "24.0" +pg8000 = "^1.30.5" +proto-plus = "1.23.0" +protobuf = "4.25.3" +psycopg2-binary = "2.9.9" +pyasn1 = "0.5.1" +pyasn1-modules = "0.3.0" +pycparser = "2.21" +pyhumps = "3.8.0" +pyrfc3339 = "1.1" +pyrsistent = "0.20.0" +python-dateutil = "2.9.0.post0" +python-dotenv = "1.0.1" +python-jose = "3.3.0" +pytz = "2024.1" +requests = "2.31.0" +rsa = "4.9" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +semver = "3.0.2" +sentry-sdk = "1.41.0" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +six = "1.16.0" +sqlalchemy = "2.0.28" +sqlalchemy-utils = "0.41.1" +threadloop = "1.0.2" +thrift = "0.16.0" +tornado = "6.4" +typing-extensions = "4.10.0" +urllib3 = "2.2.1" +werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/seeker25/sbc-pay.git" -reference = "18263" -resolved_reference = "fe42bf81c86c77946a1df238f69f12ad6b83d804" +url = "https://github.com/Jxio/sbc-pay.git" +reference = "19875" +resolved_reference = "62ceadd6e60f53cd8cf3781b5e2f6b99c968c6bf" subdirectory = "pay-api" [[package]] @@ -2334,20 +2379,17 @@ files = [ [[package]] name = "sentry-sdk" -version = "1.42.0" +version = "1.41.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, - {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, + {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, + {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, ] [package.dependencies] -blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} certifi = "*" -flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} -markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} [package.extras] @@ -2367,7 +2409,6 @@ grpcio = ["grpcio (>=1.21.1)"] httpx = ["httpx (>=0.16.0)"] huey = ["huey (>=2)"] loguru = ["loguru (>=0.5)"] -openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] opentelemetry = ["opentelemetry-distro (>=0.35b0)"] opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] pure-eval = ["asttokens", "executing", "pure-eval"] @@ -2673,4 +2714,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "350c3fdfde2b9ef2aa795334a051e997a91eddd0e155897b958fd7f60492f4a5" +content-hash = "3f2c4e7b54827174e4fc8d638c5b3079df2f456376983184edef0fbfe91c5e9a" diff --git a/jobs/payment-jobs/pyproject.toml b/jobs/payment-jobs/pyproject.toml index a7f42bb90..dab1071b0 100644 --- a/jobs/payment-jobs/pyproject.toml +++ b/jobs/payment-jobs/pyproject.toml @@ -8,7 +8,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "18263", subdirectory = "pay-api"} +pay-api = {git = "https://github.com/Jxio/sbc-pay.git", rev = "19875", subdirectory = "pay-api"} flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} gunicorn = "^21.2.0" diff --git a/jobs/payment-jobs/tasks/ap_task.py b/jobs/payment-jobs/tasks/ap_task.py index 6506e8fab..0633a8c3b 100644 --- a/jobs/payment-jobs/tasks/ap_task.py +++ b/jobs/payment-jobs/tasks/ap_task.py @@ -23,12 +23,12 @@ from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import Refund as RefundModel from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db -from pay_api.utils.enums import DisbursementStatus, EjvFileType, RoutingSlipStatus +from pay_api.utils.enums import DisbursementStatus, EjvFileType, EJVLinkType, RoutingSlipStatus from tasks.common.cgi_ap import CgiAP from tasks.common.dataclasses import APLine from tasks.ejv_partner_distribution_task import EjvPartnerDistributionTask @@ -156,9 +156,10 @@ def _create_non_gov_disbursement_file(cls): # pylint:disable=too-many-locals ap_content = f'{ap_content}{batch_trailer}' for inv in invoices: - db.session.add(EjvInvoiceLinkModel(invoice_id=inv.id, - ejv_header_id=ejv_header_model.id, - disbursement_status_code=DisbursementStatus.UPLOADED.value)) + db.session.add(EjvLinkModel(link_id=inv.id, + link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header_model.id, + disbursement_status_code=DisbursementStatus.UPLOADED.value)) inv.disbursement_status_code = DisbursementStatus.UPLOADED.value db.session.flush() diff --git a/jobs/payment-jobs/tasks/eft_transfer_task.py b/jobs/payment-jobs/tasks/eft_transfer_task.py index d6f9bdfe9..c43bd7412 100644 --- a/jobs/payment-jobs/tasks/eft_transfer_task.py +++ b/jobs/payment-jobs/tasks/eft_transfer_task.py @@ -23,13 +23,14 @@ from pay_api.models import EFTShortnames as EFTShortnameModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import PaymentLineItem as PaymentLineItemModel from pay_api.models import db from pay_api.services.flags import flags -from pay_api.utils.enums import DisbursementStatus, EFTGlTransferType, EjvFileType, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import ( + DisbursementStatus, EFTGlTransferType, EjvFileType, EJVLinkType, InvoiceStatus, PaymentMethod) from sqlalchemy import exists, func from tasks.common.cgi_ejv import CgiEjv @@ -173,9 +174,10 @@ def process_invoice_ejv_links(invoices: List[InvoiceModel], ejv_header_model_id: for inv in invoices: current_app.logger.debug(f'Creating EJV Invoice Link for invoice id: {inv.id}') # Create Ejv file link and flush - ejv_invoice_link = EjvInvoiceLinkModel(invoice_id=inv.id, ejv_header_id=ejv_header_model_id, - disbursement_status_code=DisbursementStatus.UPLOADED.value, - sequence=sequence) + ejv_invoice_link = EjvLinkModel(link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header_model_id, + disbursement_status_code=DisbursementStatus.UPLOADED.value, + sequence=sequence) db.session.add(ejv_invoice_link) sequence += 1 diff --git a/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py b/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py index cdb0421b5..0ec8b13c1 100644 --- a/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py +++ b/jobs/payment-jobs/tasks/ejv_partner_distribution_task.py @@ -23,13 +23,14 @@ from pay_api.models import DistributionCodeLink as DistributionCodeLinkModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import FeeSchedule as FeeScheduleModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentLineItem as PaymentLineItemModel +from pay_api.models import RefundsPartial as RefundsPartialModel from pay_api.models import Receipt as ReceiptModel from pay_api.models import db -from pay_api.utils.enums import DisbursementStatus, EjvFileType, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import DisbursementStatus, EjvFileType, EJVLinkType, InvoiceStatus, PaymentMethod from sqlalchemy import Date, cast from tasks.common.cgi_ejv import CgiEjv @@ -68,6 +69,21 @@ def get_invoices_for_disbursement(partner): current_app.logger.info(invoices) return invoices + @staticmethod + def get_refund_partial_payment_line_items_for_disbursement(partner) -> List[PaymentLineItemModel]: + """Return payment line items with partial refunds for disbursement.""" + payment_line_items: List[PaymentLineItemModel] = db.session.query(PaymentLineItemModel) \ + .join(InvoiceModel, PaymentLineItemModel.invoice_id == InvoiceModel.id) \ + .join(RefundsPartialModel, PaymentLineItemModel.id == RefundsPartialModel.payment_line_item_id) \ + .filter(InvoiceModel.invoice_status_code == InvoiceStatus.PAID.value) \ + .filter(InvoiceModel.payment_method_code.in_([PaymentMethod.DIRECT_PAY.value])) \ + .filter((RefundsPartialModel.disbursement_status_code.is_(None)) | + (RefundsPartialModel.disbursement_status_code == DisbursementStatus.ERRORED.value)) \ + .filter(InvoiceModel.corp_type_code == partner.code) \ + .all() + current_app.logger.info(payment_line_items) + return payment_line_items + @classmethod def get_invoices_for_refund_reversal(cls, partner): """Return invoices for refund reversal.""" @@ -113,12 +129,16 @@ def _create_ejv_file_for_partner(cls, batch_type: str): # pylint:disable=too-ma for partner in partners: # Find all invoices for the partner to disburse. - # This includes invoices which are not PAID and invoices which are refunded. + # This includes invoices which are not PAID and invoices which are refunded and partial refunded. payment_invoices = cls.get_invoices_for_disbursement(partner) refund_reversals = cls.get_invoices_for_refund_reversal(partner) invoices = payment_invoices + refund_reversals + + # Process partial refunds for each partner + refund_partial_items = cls.get_refund_partial_payment_line_items_for_disbursement(partner) + # If no invoices continue. - if not invoices: + if not invoices and not refund_partial_items: continue effective_date: str = cls.get_effective_date() @@ -134,11 +154,16 @@ def _create_ejv_file_for_partner(cls, batch_type: str): # pylint:disable=too-ma # and create one JV Header and detail for each. distribution_code_set = set() invoice_id_list = [] + partial_line_item_id_list = [] for inv in invoices: invoice_id_list.append(inv.id) for line_item in inv.payment_line_items: distribution_code_set.add(line_item.fee_distribution_id) + for line_item in refund_partial_items: + partial_line_item_id_list.append(line_item.id) + distribution_code_set.add(line_item.fee_distribution_id) + for distribution_code_id in list(distribution_code_set): distribution_code: DistributionCodeModel = DistributionCodeModel.find_by_id(distribution_code_id) credit_distribution_code: DistributionCodeModel = DistributionCodeModel.find_by_id( @@ -147,13 +172,22 @@ def _create_ejv_file_for_partner(cls, batch_type: str): # pylint:disable=too-ma if credit_distribution_code.stop_ejv: continue - line_items = cls._find_line_items_by_invoice_and_distribution(distribution_code_id, invoice_id_list) + line_items = cls._find_line_items_by_invoice_and_distribution( + distribution_code_id, invoice_id_list) + + refund_partial_items = cls._find_refund_partial_items_by_distribution( + distribution_code_id, partial_line_item_id_list) total: float = 0 for line in line_items: total += line.total + partial_refund_total: float = 0 + for refund_partial in refund_partial_items: + partial_refund_total += refund_partial.refund_amount + batch_total += total + batch_total += partial_refund_total debit_distribution = cls.get_distribution_string(distribution_code) # Debit from BCREG GL credit_distribution = cls.get_distribution_string(credit_distribution_code) # Credit to partner GL @@ -193,20 +227,39 @@ def _create_ejv_file_for_partner(cls, batch_type: str): # pylint:disable=too-ma control_total += 1 + partial_refund_number: int = 0 + for refund_partial in refund_partial_items: + # JV Details for partial refunds + partial_refund_number += 1 + # Flow Through add it as the refunds_partial id. + flow_through = f'{refund_partial.id:<110}' + refund_partial_number = f'#{refund_partial.id}' + description = disbursement_desc[:-len(refund_partial_number)] + refund_partial_number + description = f'{description[:100]:<100}' + + ejv_content = '{}{}'.format(ejv_content, # pylint:disable=consider-using-f-string + cls.get_jv_line(batch_type, credit_distribution, description, + effective_date, flow_through, journal_name, + refund_partial.refund_amount, + partial_refund_number, 'D')) + partial_refund_number += 1 + control_total += 1 + + # Add a line here for debit too + ejv_content = '{}{}'.format(ejv_content, # pylint:disable=consider-using-f-string + cls.get_jv_line(batch_type, debit_distribution, description, + effective_date, flow_through, journal_name, + refund_partial.refund_amount, + partial_refund_number, 'C')) + control_total += 1 + + # Update partial refund status + refund_partial.disbursement_status_code = DisbursementStatus.UPLOADED.value + + # Create ejv invoice/partial_refund link records and set invoice status sequence = 1 - # Create ejv invoice link records and set invoice status - for inv in invoices: - # Create Ejv file link and flush - link_model = EjvInvoiceLinkModel(invoice_id=inv.id, - ejv_header_id=ejv_header_model.id, - disbursement_status_code=DisbursementStatus.UPLOADED.value, - sequence=sequence) - # Set distribution status to invoice - db.session.add(link_model) - sequence += 1 - inv.disbursement_status_code = DisbursementStatus.UPLOADED.value - - db.session.flush() + sequence = cls._create_ejv_link(invoices, ejv_header_model, sequence, EJVLinkType.INVOICE.value) + cls._create_ejv_link(refund_partial_items, ejv_header_model, sequence, EJVLinkType.REFUND.value) if not ejv_content: db.session.rollback() @@ -238,6 +291,18 @@ def _find_line_items_by_invoice_and_distribution(cls, distribution_code_id, invo .filter(PaymentLineItemModel.fee_distribution_id == distribution_code_id) return line_items + @classmethod + def _find_refund_partial_items_by_distribution(cls, distribution_code_id, partial_line_item_id_list) \ + -> List[RefundsPartialModel]: + """Find and return all payment line items for this distribution.""" + line_items: List[RefundsPartialModel] = db.session.query(RefundsPartialModel) \ + .join(PaymentLineItemModel, PaymentLineItemModel.id == RefundsPartialModel.payment_line_item_id) \ + .filter(RefundsPartialModel.payment_line_item_id.in_(partial_line_item_id_list)) \ + .filter(RefundsPartialModel.refund_amount > 0) \ + .filter(PaymentLineItemModel.fee_distribution_id == distribution_code_id) \ + .all() + return line_items + @classmethod def _get_partners_by_batch_type(cls, batch_type) -> List[CorpTypeModel]: """Return partners by batch type.""" @@ -272,3 +337,17 @@ def _get_partners_by_batch_type(cls, batch_type) -> List[CorpTypeModel]: corp_type_codes: List[str] = db.session.scalars(corp_type_query).all() return db.session.query(CorpTypeModel).filter(CorpTypeModel.code.in_(corp_type_codes)).all() + + @classmethod + def _create_ejv_link(cls, items, ejv_header_model, sequence, link_type): + for item in items: + link_model = EjvLinkModel(link_id=item.id, + link_type=link_type, + ejv_header_id=ejv_header_model.id, + disbursement_status_code=DisbursementStatus.UPLOADED.value, + sequence=sequence) + db.session.add(link_model) + sequence += 1 + item.disbursement_status_code = DisbursementStatus.UPLOADED.value + db.session.flush() + return sequence diff --git a/jobs/payment-jobs/tasks/ejv_payment_task.py b/jobs/payment-jobs/tasks/ejv_payment_task.py index d53d2143b..22ae8a172 100644 --- a/jobs/payment-jobs/tasks/ejv_payment_task.py +++ b/jobs/payment-jobs/tasks/ejv_payment_task.py @@ -20,12 +20,13 @@ from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import db -from pay_api.utils.enums import DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import ( + DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod) from pay_api.utils.util import generate_transaction_number from tasks.common.cgi_ejv import CgiEjv @@ -180,10 +181,10 @@ def _create_ejv_file_for_gov_account(cls, batch_type: str): # pylint:disable=to sequence = 1 for inv in invoices: current_app.logger.debug(f'Creating EJV Invoice Link for invoice id: {inv.id}') - # Create Ejv file link and flush - ejv_invoice_link = EjvInvoiceLinkModel(invoice_id=inv.id, ejv_header_id=ejv_header_model.id, - disbursement_status_code=DisbursementStatus.UPLOADED.value, - sequence=sequence) + ejv_invoice_link = EjvLinkModel(link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header_model.id, + disbursement_status_code=DisbursementStatus.UPLOADED.value, + sequence=sequence) db.session.add(ejv_invoice_link) sequence += 1 # Set distribution status to invoice diff --git a/jobs/payment-jobs/tests/jobs/factory.py b/jobs/payment-jobs/tests/jobs/factory.py index c91a69a30..6bf6fab04 100644 --- a/jobs/payment-jobs/tests/jobs/factory.py +++ b/jobs/payment-jobs/tests/jobs/factory.py @@ -21,7 +21,8 @@ from pay_api.models import ( CfsAccount, DistributionCode, DistributionCodeLink, EFTShortnames, Invoice, InvoiceReference, Payment, - PaymentAccount, PaymentLineItem, Receipt, Refund, RoutingSlip, StatementRecipients, StatementSettings) + PaymentAccount, PaymentLineItem, Receipt, Refund, RefundsPartial, RoutingSlip, StatementRecipients, + StatementSettings) from pay_api.utils.enums import ( CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, PaymentStatus, PaymentSystem, RoutingSlipStatus) @@ -334,3 +335,20 @@ def factory_refund_invoice( requested_by='TEST', details=details ).save() + + +def factory_refund_partial( + payment_line_item_id: int, + refund_amount: float, + refund_type: str, + created_by='test', + created_on: datetime = datetime.now() +): + """Return Factory.""" + return RefundsPartial( + payment_line_item_id=payment_line_item_id, + refund_amount=refund_amount, + refund_type=refund_type, + created_by=created_by, + created_on=created_on + ).save() diff --git a/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py b/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py index 0cd14d431..27aaf3256 100644 --- a/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py +++ b/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py @@ -19,7 +19,7 @@ from datetime import datetime from typing import List -from pay_api.models import DistributionCode, EFTGLTransfer, EjvFile, EjvHeader, EjvInvoiceLink, FeeSchedule, Invoice, db +from pay_api.models import DistributionCode, EFTGLTransfer, EjvFile, EjvHeader, EjvLink, FeeSchedule, Invoice, db from pay_api.utils.enums import DisbursementStatus, EFTGlTransferType, EjvFileType, InvoiceStatus, PaymentMethod from tasks.eft_transfer_task import EftTransferTask @@ -89,8 +89,8 @@ def test_eft_transfer(app, session, monkeypatch): # Lookup invoice and assert disbursement status for invoice in invoices: - ejv_inv_link: EjvInvoiceLink = db.session.query(EjvInvoiceLink) \ - .filter(EjvInvoiceLink.invoice_id == invoice.id).first() + ejv_inv_link: EjvLink = db.session.query(EjvLink) \ + .filter(EjvLink.link_id == invoice.id).first() assert ejv_inv_link ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_inv_link.ejv_header_id).first() diff --git a/jobs/payment-jobs/tests/jobs/test_ejv_partner_distribution_task.py b/jobs/payment-jobs/tests/jobs/test_ejv_partner_distribution_task.py index 7dac7cd3a..8d1191db6 100644 --- a/jobs/payment-jobs/tests/jobs/test_ejv_partner_distribution_task.py +++ b/jobs/payment-jobs/tests/jobs/test_ejv_partner_distribution_task.py @@ -22,7 +22,7 @@ from flask import current_app from freezegun import freeze_time from pay_api.models import CorpType as CorpTypeModel -from pay_api.models import DistributionCode, EjvFile, EjvHeader, EjvInvoiceLink, FeeSchedule, Invoice, db +from pay_api.models import DistributionCode, EjvFile, EjvHeader, EjvLink, FeeSchedule, Invoice, db from pay_api.utils.enums import CfsAccountStatus, DisbursementStatus, InvoiceStatus, PaymentMethod from tasks.ejv_partner_distribution_task import EjvPartnerDistributionTask @@ -89,7 +89,7 @@ def test_disbursement_for_partners(session, monkeypatch, client_code, batch_type invoice = Invoice.find_by_id(invoice.id) assert invoice.disbursement_status_code == DisbursementStatus.UPLOADED.value - ejv_inv_link = db.session.query(EjvInvoiceLink).filter(EjvInvoiceLink.invoice_id == invoice.id).first() + ejv_inv_link = db.session.query(EjvLink).filter(EjvLink.link_id == invoice.id).first() assert ejv_inv_link ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_inv_link.ejv_header_id).first() diff --git a/jobs/payment-jobs/tests/jobs/test_ejv_partner_partial_refund_distribution_task.py b/jobs/payment-jobs/tests/jobs/test_ejv_partner_partial_refund_distribution_task.py new file mode 100644 index 000000000..7ab4cce30 --- /dev/null +++ b/jobs/payment-jobs/tests/jobs/test_ejv_partner_partial_refund_distribution_task.py @@ -0,0 +1,86 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests to assure the CGI EJV Job. + +Test-Suite to ensure that the CgiEjvJob is working as expected. +""" +from datetime import datetime, timedelta + +from flask import current_app +from freezegun import freeze_time +from pay_api.models import CorpType as CorpTypeModel +from pay_api.models import DistributionCode, EjvFile, EjvHeader, EjvLink, FeeSchedule, db +from pay_api.utils.enums import DisbursementStatus, RefundsPartialType + +from tasks.ejv_partner_distribution_task import EjvPartnerDistributionTask + +from .factory import ( + factory_create_direct_pay_account, factory_distribution, factory_distribution_link, factory_invoice, + factory_invoice_reference, factory_payment, factory_payment_line_item, factory_refund_partial) + + +def test_partial_refund_disbursement(session, monkeypatch): + """Test partial refund disbursement.""" + monkeypatch.setattr('pysftp.Connection.put', lambda *args, **kwargs: None) + corp_type: CorpTypeModel = CorpTypeModel.find_by_code('VS') + + pay_account = factory_create_direct_pay_account() + + disbursement_distribution: DistributionCode = factory_distribution(name='VS Disbursement', client='112') + service_fee_distribution: DistributionCode = factory_distribution(name='VS Service Fee', client='112') + fee_distribution: DistributionCode = factory_distribution( + name='VS Fee distribution', client='112', service_fee_dist_id=service_fee_distribution.distribution_code_id, + disbursement_dist_id=disbursement_distribution.distribution_code_id + ) + fee_schedule: FeeSchedule = FeeSchedule.find_by_filing_type_and_corp_type(corp_type.code, 'WILLNOTICE') + factory_distribution_link(fee_distribution.distribution_code_id, fee_schedule.fee_schedule_id) + + invoice = factory_invoice(payment_account=pay_account, disbursement_status_code=DisbursementStatus.COMPLETED.value, + corp_type_code=corp_type.code, total=21.5, status_code='PAID') + pli = factory_payment_line_item(invoice_id=invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id, + filing_fees=0, total=1.5, service_fees=1.5, + fee_dist_id=fee_distribution.distribution_code_id) + + inv_ref = factory_invoice_reference(invoice_id=invoice.id) + factory_payment(invoice_number=inv_ref.invoice_number, payment_status_code='COMPLETED') + + refund_partial = factory_refund_partial(pli.id, refund_amount=1.5, created_by='test', + refund_type=RefundsPartialType.SERVICE_FEES.value) + + assert refund_partial.disbursement_status_code is None + # Lookup refund_partial_link + refund_partial_link = EjvLink.find_ejv_link_by_link_id(refund_partial.id) + assert refund_partial_link is None + + day_after_time_delay = datetime.today() + timedelta(days=( + current_app.config.get('DISBURSEMENT_DELAY_IN_DAYS') + 1)) + + with freeze_time(day_after_time_delay): + EjvPartnerDistributionTask.create_ejv_file() + + ejv_link = db.session.query(EjvLink).filter(EjvLink.link_id == refund_partial.id).first() + assert ejv_link + + ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_link.ejv_header_id).first() + assert ejv_header.disbursement_status_code == DisbursementStatus.UPLOADED.value + assert ejv_header + + ejv_file = EjvFile.find_by_id(ejv_header.ejv_file_id) + assert ejv_file + assert ejv_file.disbursement_status_code == DisbursementStatus.UPLOADED.value + + refund_partial_link = EjvLink.find_ejv_link_by_link_id(refund_partial.id) + assert refund_partial_link.disbursement_status_code == DisbursementStatus.UPLOADED.value + assert refund_partial.disbursement_status_code == DisbursementStatus.UPLOADED.value diff --git a/jobs/payment-jobs/tests/jobs/test_ejv_payment_task.py b/jobs/payment-jobs/tests/jobs/test_ejv_payment_task.py index 5eae96032..9c1a67363 100644 --- a/jobs/payment-jobs/tests/jobs/test_ejv_payment_task.py +++ b/jobs/payment-jobs/tests/jobs/test_ejv_payment_task.py @@ -16,8 +16,7 @@ Test-Suite to ensure that the CgiEjvJob is working as expected. """ -from pay_api.models import ( - DistributionCode, EjvFile, EjvHeader, EjvInvoiceLink, FeeSchedule, Invoice, InvoiceReference, db) +from pay_api.models import DistributionCode, EjvFile, EjvHeader, EjvLink, FeeSchedule, Invoice, InvoiceReference, db from pay_api.utils.enums import DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus from tasks.ejv_payment_task import EjvPaymentTask @@ -87,8 +86,8 @@ def test_payments_for_gov_accounts(session, monkeypatch): InvoiceReferenceStatus.ACTIVE.value) assert invoice_ref - ejv_inv_link: EjvInvoiceLink = db.session.query(EjvInvoiceLink)\ - .filter(EjvInvoiceLink.invoice_id == inv_id).first() + ejv_inv_link: EjvLink = db.session.query(EjvLink)\ + .filter(EjvLink.link_id == inv_id).first() assert ejv_inv_link ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_inv_link.ejv_header_id).first() @@ -127,8 +126,8 @@ def test_payments_for_gov_accounts(session, monkeypatch): InvoiceReferenceStatus.ACTIVE.value) assert invoice_ref - ejv_inv_link = db.session.query(EjvInvoiceLink).filter(EjvInvoiceLink.invoice_id == inv_id)\ - .filter(EjvInvoiceLink.disbursement_status_code == DisbursementStatus.UPLOADED.value).first() + ejv_inv_link = db.session.query(EjvLink).filter(EjvLink.link_id == inv_id)\ + .filter(EjvLink.disbursement_status_code == DisbursementStatus.UPLOADED.value).first() assert ejv_inv_link ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_inv_link.ejv_header_id).first() diff --git a/pay-api/migrations/versions/2024_03_06_04b8a7bed74e_ejv_link_generic.py b/pay-api/migrations/versions/2024_03_06_04b8a7bed74e_ejv_link_generic.py new file mode 100644 index 000000000..eca30695c --- /dev/null +++ b/pay-api/migrations/versions/2024_03_06_04b8a7bed74e_ejv_link_generic.py @@ -0,0 +1,39 @@ +"""Rename ejv_invoice_links to ejv_links, alter invoice_id to link_id, removed foreign constraint, and added link_type column + +Revision ID: 04b8a7bed74e +Revises: bacb2b859d78 +Create Date: 2024-03-06 11:40:00.153387 + +""" +from alembic import op +import sqlalchemy as sa + +from pay_api.utils.enums import EJVLinkType + +# revision identifiers, used by Alembic. +revision = '04b8a7bed74e' +down_revision = 'bacb2b859d78' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('ejv_invoice_links', sa.Column('link_type', sa.String(length=20), nullable=True)) + op.drop_constraint('ejv_invoice_links_invoice_id_fkey', 'ejv_invoice_links', type_='foreignkey') + op.drop_index(op.f('ix_ejv_invoice_links_invoice_id'), table_name='ejv_invoice_links') + op.alter_column('ejv_invoice_links', 'invoice_id', new_column_name='link_id', + existing_type=sa.Integer(), nullable=True) + op.rename_table('ejv_invoice_links', 'ejv_links') + # as there is no data for partial refunds yet. + op.execute(f"UPDATE ejv_links set link_type='{EJVLinkType.INVOICE.value}'") + + op.create_index('ix_ejv_links_link_type_link_id', 'ejv_links', ['link_type', 'link_id'], unique=False) + +def downgrade(): + op.drop_index('ix_ejv_links_link_type_link_id', table_name='ejv_links') + op.rename_table('ejv_links', 'ejv_invoice_links') + op.alter_column('ejv_invoice_links', 'link_id', new_column_name='invoice_id', + existing_type=sa.Integer(), nullable=False) + op.create_index(op.f('ix_ejv_invoice_links_invoice_id'), 'ejv_invoice_links', ['invoice_id'], unique=False) + op.drop_column('ejv_invoice_links', 'link_type') + op.create_foreign_key('ejv_invoice_links_invoice_id_fkey', 'ejv_invoice_links', 'invoices', ['invoice_id'], ['id']) diff --git a/pay-api/poetry.lock b/pay-api/poetry.lock index 0a825ea22..adadbd2c3 100644 --- a/pay-api/poetry.lock +++ b/pay-api/poetry.lock @@ -62,17 +62,17 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.0.4" +version = "2.1.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, - {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, + {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, + {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, ] [package.dependencies] -pycodestyle = ">=2.10.0" +pycodestyle = ">=2.11.0" [[package]] name = "blinker" @@ -331,63 +331,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.3" +version = "7.4.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, - {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, - {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, - {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, - {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, - {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, - {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, - {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, - {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, - {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, - {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, - {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, - {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, - {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, ] [package.extras] diff --git a/pay-api/src/pay_api/models/__init__.py b/pay-api/src/pay_api/models/__init__.py index c3a04b78f..9e119110f 100755 --- a/pay-api/src/pay_api/models/__init__.py +++ b/pay-api/src/pay_api/models/__init__.py @@ -36,7 +36,7 @@ from .eft_transaction import EFTTransaction, EFTTransactionSchema from .ejv_file import EjvFile from .ejv_header import EjvHeader -from .ejv_invoice_link import EjvInvoiceLink +from .ejv_link import EjvLink from .error_code import ErrorCode, ErrorCodeSchema from .fee_code import FeeCode, FeeCodeSchema # noqa: I001 from .fee_schedule import FeeSchedule, FeeScheduleSchema diff --git a/pay-api/src/pay_api/models/ejv_invoice_link.py b/pay-api/src/pay_api/models/ejv_link.py similarity index 80% rename from pay-api/src/pay_api/models/ejv_invoice_link.py rename to pay-api/src/pay_api/models/ejv_link.py index 7db67b761..4055df51d 100644 --- a/pay-api/src/pay_api/models/ejv_invoice_link.py +++ b/pay-api/src/pay_api/models/ejv_link.py @@ -19,10 +19,10 @@ from .db import db -class EjvInvoiceLink(BaseModel): # pylint: disable=too-few-public-methods +class EjvLink(BaseModel): # pylint: disable=too-few-public-methods """This class manages linkages between EJV and invoices.""" - __tablename__ = 'ejv_invoice_links' + __tablename__ = 'ejv_links' # this mapper is used so that new and old versions of the service can be run simultaneously, # making rolling upgrades easier # This is used by SQLAlchemy to explicitly define which fields we're interested @@ -38,15 +38,22 @@ class EjvInvoiceLink(BaseModel): # pylint: disable=too-few-public-methods 'id', 'disbursement_status_code', 'ejv_header_id', - 'invoice_id', + 'link_id', + 'link_type', 'message', 'sequence' ] } id = db.Column(db.Integer, primary_key=True, autoincrement=True) - invoice_id = db.Column(db.Integer, ForeignKey('invoices.id'), nullable=False, index=True) - ejv_header_id = db.Column(db.Integer, ForeignKey('ejv_headers.id'), nullable=False, index=True) disbursement_status_code = db.Column(db.String(20), ForeignKey('disbursement_status_codes.code'), nullable=True) + ejv_header_id = db.Column(db.Integer, ForeignKey('ejv_headers.id'), nullable=False, index=True) + link_id = db.Column(db.Integer, nullable=True, index=True) # Repurposed for generic linking + link_type = db.Column(db.String(50), nullable=True, index=True) message = db.Column('message', db.String, nullable=True, index=False) sequence = db.Column(db.Integer, nullable=True) + + @classmethod + def find_ejv_link_by_link_id(cls, link_id: str): + """Return any ejv link by link_id.""" + return cls.query.filter_by(link_id=link_id).first() diff --git a/pay-api/src/pay_api/models/refunds_partial.py b/pay-api/src/pay_api/models/refunds_partial.py index 9fda23d51..de85c361f 100644 --- a/pay-api/src/pay_api/models/refunds_partial.py +++ b/pay-api/src/pay_api/models/refunds_partial.py @@ -40,11 +40,13 @@ class RefundsPartial(Audit, VersionedModel): # pylint: disable=too-many-instanc __mapper_args__ = { 'include_properties': [ 'id', + 'created_by', + 'created_on', + 'disbursement_date', + 'disbursement_status_code', 'payment_line_item_id', 'refund_amount', 'refund_type', - 'disbursement_status_code', - 'disbursement_date' ] } diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index c7bc97b71..7b6e40c9a 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -375,3 +375,10 @@ class QueueSources(Enum): PAY_JOBS = 'pay-jobs' PAY_QUEUE = 'pay-queue' FTP_POLLER = 'ftp-poller' + + +class EJVLinkType(Enum): + """EJV link types for ejv_link table.""" + + INVOICE = 'invoice' + REFUND = 'refund' diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index d3d57005b..8bc2c6d99 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -119,17 +119,17 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.0.4" +version = "2.1.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, - {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, + {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, + {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, ] [package.dependencies] -pycodestyle = ">=2.10.0" +pycodestyle = ">=2.11.0" [[package]] name = "blinker" @@ -1648,9 +1648,9 @@ werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/seeker25/sbc-pay.git" -reference = "pubsub_emulation" -resolved_reference = "e2c9d5fe0e51ce2be585b45ed26d40ba6e03ecbd" +url = "https://github.com/Jxio/sbc-pay.git" +reference = "19875" +resolved_reference = "62ceadd6e60f53cd8cf3781b5e2f6b99c968c6bf" subdirectory = "pay-api" [[package]] @@ -2647,4 +2647,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "bc1d52223cc3c9ae97947aeb1a795405091f9c1d771d0a088090cdfde97d9093" +content-hash = "b74265a904a8f0ea46d5e829d3643326ea5f204a805c8486c1782bf97d643c6f" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml index f2c245e7f..0ad74480b 100644 --- a/pay-queue/pyproject.toml +++ b/pay-queue/pyproject.toml @@ -22,7 +22,7 @@ protobuf = "4.25.3" launchdarkly-server-sdk = "^9.2.2" cachecontrol = "^0.14.0" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "pubsub_emulation", subdirectory = "pay-api"} +pay-api = {git = "https://github.com/Jxio/sbc-pay.git", rev = "19875", subdirectory = "pay-api"} flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} pg8000 = "^1.30.5" diff --git a/pay-queue/src/pay_queue/services/cgi_reconciliations.py b/pay-queue/src/pay_queue/services/cgi_reconciliations.py index d3040f9b9..9926acb86 100644 --- a/pay-queue/src/pay_queue/services/cgi_reconciliations.py +++ b/pay-queue/src/pay_queue/services/cgi_reconciliations.py @@ -20,7 +20,7 @@ from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel from pay_api.models import Payment as PaymentModel @@ -32,8 +32,8 @@ from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.utils.enums import ( - DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, - PaymentSystem, QueueSources, RoutingSlipStatus) + DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, + PaymentStatus, PaymentSystem, QueueSources, RoutingSlipStatus) from sentry_sdk import capture_message from pay_queue import config @@ -78,10 +78,12 @@ def _update_acknowledgement(msg: Dict[str, any]): for ejv_header in ejv_headers: ejv_header.disbursement_status_code = DisbursementStatus.ACKNOWLEDGED.value if ejv_file.file_type == EjvFileType.DISBURSEMENT.value: - ejv_links: List[EjvInvoiceLinkModel] = db.session.query(EjvInvoiceLinkModel).filter( - EjvInvoiceLinkModel.ejv_header_id == ejv_header.id).all() + ejv_links: List[EjvLinkModel] = db.session.query(EjvLinkModel) \ + .filter(EjvLinkModel.ejv_header_id == ejv_header.id) \ + .filter(EjvLinkModel.link_type == EJVLinkType.INVOICE.value) \ + .all() for ejv_link in ejv_links: - invoice: InvoiceModel = InvoiceModel.find_by_id(ejv_link.invoice_id) + invoice: InvoiceModel = InvoiceModel.find_by_id(ejv_link.link_id) invoice.disbursement_status_code = DisbursementStatus.ACKNOWLEDGED.value db.session.commit() @@ -164,9 +166,10 @@ def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number): # invoice_id = int(line[205:315]) current_app.logger.info('Invoice id - %s', invoice_id) invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id) - invoice_link: EjvInvoiceLinkModel = db.session.query(EjvInvoiceLinkModel).filter( - EjvInvoiceLinkModel.ejv_header_id == ejv_header_model_id).filter( - EjvInvoiceLinkModel.invoice_id == invoice_id).one_or_none() + invoice_link: EjvLinkModel = db.session.query(EjvLinkModel).filter( + EjvLinkModel.ejv_header_id == ejv_header_model_id).filter( + EjvLinkModel.link_id == invoice_id).filter( + EjvLinkModel.link_type == EJVLinkType.INVOICE.value).one_or_none() invoice_return_code = line[315:319] invoice_return_message = line[319:469] # If the JV process failed, then mark the GL code against the invoice to be stopped @@ -385,10 +388,11 @@ def _process_ap_header_non_gov_disbursement(line, ejv_file: EjvFileModel) -> boo ap_header_return_code = line[414:418] ap_header_error_message = line[418:568] disbursement_status = _get_disbursement_status(ap_header_return_code) - invoice_link = db.session.query(EjvInvoiceLinkModel)\ + invoice_link = db.session.query(EjvLinkModel)\ .join(EjvHeaderModel).join(EjvFileModel)\ .filter(EjvFileModel.id == ejv_file.id)\ - .filter(EjvInvoiceLinkModel.invoice_id == invoice_id)\ + .filter(EjvLinkModel.link_id == invoice_id)\ + .filter(EjvLinkModel.link_type == EJVLinkType.INVOICE.value) \ .one_or_none() invoice_link.disbursement_status_code = disbursement_status invoice_link.message = ap_header_error_message diff --git a/pay-queue/tests/integration/test_cgi_reconciliations.py b/pay-queue/tests/integration/test_cgi_reconciliations.py index 8b3adaac9..f308d9f46 100644 --- a/pay-queue/tests/integration/test_cgi_reconciliations.py +++ b/pay-queue/tests/integration/test_cgi_reconciliations.py @@ -21,7 +21,7 @@ from pay_api.models import DistributionCode as DistributionCodeModel from pay_api.models import EjvFile as EjvFileModel from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvInvoiceLink as EjvInvoiceLinkModel +from pay_api.models import EjvLink as EjvLinkModel from pay_api.models import FeeSchedule as FeeScheduleModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel @@ -33,7 +33,7 @@ from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db from pay_api.utils.enums import ( - CfsAccountStatus, DisbursementStatus, EjvFileType, InvoiceReferenceStatus, InvoiceStatus, MessageType, + CfsAccountStatus, DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, RoutingSlipStatus) from tests.integration.utils import add_file_event_to_queue_and_process @@ -93,8 +93,9 @@ def test_successful_partner_ejv_reconciliations(client): partner_code=partner_code, payment_account_id=pay_account.id).save() ejv_header_id = ejv_header.id - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() ack_file_name = f'ACK.{file_ref}' @@ -207,8 +208,10 @@ def test_failed_partner_ejv_reconciliations(client): partner_code=partner_code, payment_account_id=pay_account.id).save() ejv_header_id = ejv_header.id - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, + link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() ack_file_name = f'ACK.{file_ref}' @@ -325,8 +328,9 @@ def test_successful_partner_reversal_ejv_reconciliations(client): partner_code=partner_code, payment_account_id=pay_account.id).save() ejv_header_id = ejv_header.id - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() ack_file_name = f'ACK.{file_ref}' @@ -457,8 +461,9 @@ def test_succesful_payment_ejv_reconciliations(client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() - EjvInvoiceLinkModel( - invoice_id=inv.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) @@ -615,8 +620,9 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() - EjvInvoiceLinkModel( - invoice_id=inv.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) @@ -1041,12 +1047,13 @@ def test_successful_ap_disbursement(client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=account.id).save() - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() - EjvInvoiceLinkModel( - invoice_id=refund_invoice.id, ejv_header_id=ejv_header.id, + EjvLinkModel( + link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() @@ -1187,12 +1194,13 @@ def test_failure_ap_disbursement(client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=account.id).save() - EjvInvoiceLinkModel( - invoice_id=invoice.id, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + EjvLinkModel( + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() - EjvInvoiceLinkModel( - invoice_id=refund_invoice.id, ejv_header_id=ejv_header.id, + EjvLinkModel( + link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() @@ -1287,14 +1295,14 @@ def test_failure_ap_disbursement(client): invoice_1 = InvoiceModel.find_by_id(invoice_ids[0]) assert invoice_1.disbursement_status_code == DisbursementStatus.COMPLETED.value assert invoice_1.disbursement_date is not None - invoice_link = db.session.query(EjvInvoiceLinkModel)\ - .filter(EjvInvoiceLinkModel.invoice_id == invoice_ids[0])\ + invoice_link = db.session.query(EjvLinkModel)\ + .filter(EjvLinkModel.link_id == invoice_ids[0])\ .one_or_none() assert invoice_link.disbursement_status_code == DisbursementStatus.COMPLETED.value invoice_2 = InvoiceModel.find_by_id(invoice_ids[1]) assert invoice_2.disbursement_status_code == DisbursementStatus.ERRORED.value - invoice_link = db.session.query(EjvInvoiceLinkModel)\ - .filter(EjvInvoiceLinkModel.invoice_id == invoice_ids[1])\ + invoice_link = db.session.query(EjvLinkModel)\ + .filter(EjvLinkModel.link_id == invoice_ids[1])\ .one_or_none() assert invoice_link.disbursement_status_code == DisbursementStatus.ERRORED.value From 0f86928a398009d48532c2a8642cc5c2c33c1894 Mon Sep 17 00:00:00 2001 From: pwei1018 Date: Mon, 18 Mar 2024 14:14:50 -0700 Subject: [PATCH 34/87] Fixed typo. --- jobs/payment-jobs/devops/vaults.gcp.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jobs/payment-jobs/devops/vaults.gcp.env b/jobs/payment-jobs/devops/vaults.gcp.env index 253a5b457..81e0907b7 100644 --- a/jobs/payment-jobs/devops/vaults.gcp.env +++ b/jobs/payment-jobs/devops/vaults.gcp.env @@ -77,7 +77,7 @@ BCREG_CGI_FTP_PRIVATE_KEY="op://relationship/$APP_ENV/ftp-poller/BCREG_FTP_PRIVA BCREG_CGI_FTP_PRIVATE_KEY_PASSPHRASE="op://relationship/$APP_ENV/ftp-poller/BCREG_FTP_PRIVATE_KEY_PASSPHRASE" CGI_SFTP_DIRECTORY="op://relationship/$APP_ENV/ftp-poller/CAS_SFTP_DIRECTORY" AUTH_WEB_PAY_TRANSACTION_URL="op://web-url/$APP_ENV/auth-web/AUTH_WEB_URL" -AUTH_WEB_STATEMENT_URL="""op://web-url/$APP_ENV/auth-web/AUTH_WEB_URL" +AUTH_WEB_STATEMENT_URL="op://web-url/$APP_ENV/auth-web/AUTH_WEB_URL" REGISTRIES_LOGO_IMAGE_NAME="bc_logo_for_email.png" DISBURSEMENT_DELAY="5" DISABLE_CFS_FAS_INTEGRATION="false" From 66983495045b25fe59c04299eed60af7763a02ba Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 19 Mar 2024 11:00:58 -0700 Subject: [PATCH 35/87] 14521 - Get rid of sqlalchemy-continuum (#1450) * Use new versioning library thanks to Thor/ people working on LEAR. Disable activity log for now, we'll add audit columns and just use the history tables. Still need a migration to move data from verisons -> history tables. * Add in version column * Add in default for version * Small nudge * Another tweak refunds_partial table missing * Migration fixes along the way * Migration query * Easier for copy and paste * Credit for script * Fix multiple head issue * Small tweaks, implement payment_methods again * Fix warnings + depreciated cls.query.get() * remove pytest-asyncio * Implement versioning with a unit test * Remove unused statement, lint cleanup * Change commit -> flush * Add in some checks to see if column has changed before updating it * Rollback conditional checks. This also reverts commit 1e84bec8bb11f939cd330368c371768fdeb42fdf. * Minor touch ups. --- ..._03_15_fb3ba97b603a_new_version_tables_.py | 241 ++++++++++++ ...ed2340a43c_move_from_version_to_history.py | 359 ++++++++++++++++++ pay-api/migrations/versions/6a6b042b831a_.py | 4 +- .../b6e28faea978_rename_tables_to_plurals.py | 6 +- .../c67213f860ea_incorporation_fee_codes.py | 6 +- pay-api/migrations/versions/e748b5c19247_.py | 6 +- pay-api/poetry.lock | 42 +- pay-api/pyproject.toml | 2 +- pay-api/src/pay_api/config.py | 2 +- pay-api/src/pay_api/models/account_fee.py | 5 +- pay-api/src/pay_api/models/base_model.py | 42 +- pay-api/src/pay_api/models/cfs_account.py | 5 +- pay-api/src/pay_api/models/db.py | 11 +- .../src/pay_api/models/distribution_code.py | 4 +- pay-api/src/pay_api/models/eft_short_names.py | 6 +- pay-api/src/pay_api/models/payment.py | 8 +- pay-api/src/pay_api/models/payment_account.py | 13 +- pay-api/src/pay_api/models/refunds_partial.py | 11 +- pay-api/src/pay_api/models/routing_slip.py | 2 +- pay-api/src/pay_api/models/statement.py | 62 ++- pay-api/src/pay_api/utils/util.py | 4 +- pay-api/tests/conftest.py | 5 +- pay-api/tests/unit/api/fas/test_refund.py | 6 +- .../tests/unit/api/fas/test_routing_slip.py | 60 +-- .../tests/unit/api/test_payment_request.py | 6 +- .../unit/services/test_direct_pay_service.py | 10 +- pay-api/tests/unit/services/test_statement.py | 39 +- report-api/Dockerfile | 24 -- 28 files changed, 792 insertions(+), 199 deletions(-) create mode 100644 pay-api/migrations/versions/2024_03_15_fb3ba97b603a_new_version_tables_.py create mode 100644 pay-api/migrations/versions/2024_03_18_52ed2340a43c_move_from_version_to_history.py diff --git a/pay-api/migrations/versions/2024_03_15_fb3ba97b603a_new_version_tables_.py b/pay-api/migrations/versions/2024_03_15_fb3ba97b603a_new_version_tables_.py new file mode 100644 index 000000000..5e4616332 --- /dev/null +++ b/pay-api/migrations/versions/2024_03_15_fb3ba97b603a_new_version_tables_.py @@ -0,0 +1,241 @@ +"""Migration for new history tables + +A special thanks to LEAR devs (Thor, Argus, Vysakh) for this migration and the history table implementation: +https://github.com/bcgov/lear/blob/feature-legal-name/legal-api/scripts/manual_db_scripts/legal_name_change/transfer_to_new_lear.sql + +Revision ID: fb3ba97b603a +Revises: 04b8a7bed74e +Create Date: 2024-03-15 15:22:53.140353 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utils +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'fb3ba97b603a' +down_revision = '04b8a7bed74e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('account_fees', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('cfs_accounts', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('distribution_codes', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('eft_short_names', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('payment_accounts', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + with op.batch_alter_table('refunds_partial', schema=None) as batch_op: + batch_op.add_column(sa.Column('version', sa.Integer(), nullable=False, server_default='1')) + + op.create_table('eft_short_names_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('auth_account_id', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('short_name', sa.String(), autoincrement=False, nullable=False), + sa.Column('linked_by', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('linked_by_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('linked_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('eft_short_names_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_eft_short_names_history_auth_account_id'), ['auth_account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_names_history_short_name'), ['short_name'], unique=False) + + op.create_table('payment_accounts_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('auth_account_id', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('name', sa.String(length=250), autoincrement=False, nullable=True), + sa.Column('branch_name', sa.String(length=250), autoincrement=False, nullable=True), + sa.Column('payment_method', sa.String(length=15), autoincrement=False, nullable=True), + sa.Column('bcol_user_id', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('bcol_account', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('statement_notification_enabled', sa.Boolean(), autoincrement=False, nullable=True), + sa.Column('credit', sa.Numeric(precision=19, scale=2), autoincrement=False, nullable=True), + sa.Column('billable', sa.Boolean(), autoincrement=False, nullable=True), + sa.Column('eft_enable', sa.Boolean(), autoincrement=False, nullable=False), + sa.Column('pad_activation_date', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('pad_tos_accepted_date', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('pad_tos_accepted_by', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['payment_method'], ['payment_methods.code'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('payment_accounts_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_payment_accounts_history_auth_account_id'), ['auth_account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_payment_accounts_history_bcol_account'), ['bcol_account'], unique=False) + batch_op.create_index(batch_op.f('ix_payment_accounts_history_bcol_user_id'), ['bcol_user_id'], unique=False) + + op.create_table('account_fees_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('account_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('apply_filing_fees', sa.Boolean(), autoincrement=False, nullable=True), + sa.Column('service_fee_code', sa.String(length=10), autoincrement=False, nullable=True), + sa.Column('product', sa.String(length=20), autoincrement=False, nullable=True), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('updated_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('created_by', sa.String(length=50), autoincrement=False, nullable=False), + sa.Column('created_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('updated_name', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['account_id'], ['payment_accounts.id'], ), + sa.ForeignKeyConstraint(['service_fee_code'], ['fee_codes.code'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('account_fees_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_account_fees_history_account_id'), ['account_id'], unique=False) + + op.create_table('cfs_accounts_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('cfs_account', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('cfs_party', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('cfs_site', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('payment_instrument_number', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('contact_party', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('bank_number', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('bank_branch_number', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('status', sa.String(length=40), autoincrement=False, nullable=True), + sa.Column('account_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('bank_account_number', sqlalchemy_utils.types.encrypted.encrypted_type.StringEncryptedType(), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['account_id'], ['payment_accounts.id'], ), + sa.ForeignKeyConstraint(['status'], ['cfs_account_status_codes.code'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('cfs_accounts_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_account_id'), ['account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_bank_account_number'), ['bank_account_number'], unique=False) + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_bank_branch_number'), ['bank_branch_number'], unique=False) + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_bank_number'), ['bank_number'], unique=False) + batch_op.create_index(batch_op.f('ix_cfs_accounts_history_cfs_account'), ['cfs_account'], unique=False) + + op.create_table('distribution_codes_history', + sa.Column('distribution_code_id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('name', sa.String(length=250), autoincrement=False, nullable=True), + sa.Column('client', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('responsibility_centre', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('service_line', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('stob', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('project_code', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('start_date', sa.Date(), autoincrement=False, nullable=False), + sa.Column('end_date', sa.Date(), autoincrement=False, nullable=True), + sa.Column('stop_ejv', sa.Boolean(), autoincrement=False, nullable=True), + sa.Column('service_fee_distribution_code_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('disbursement_distribution_code_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('account_id', sa.Integer(), autoincrement=False, nullable=True), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('updated_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('created_by', sa.String(length=50), autoincrement=False, nullable=False), + sa.Column('created_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('updated_name', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['account_id'], ['payment_accounts.id'], ), + # sa.ForeignKeyConstraint(['disbursement_distribution_code_id'], ['distribution_codes_history.distribution_code_id'], ), + # sa.ForeignKeyConstraint(['service_fee_distribution_code_id'], ['distribution_codes_history.distribution_code_id'], ), + sa.PrimaryKeyConstraint('distribution_code_id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('distribution_codes_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_distribution_codes_history_account_id'), ['account_id'], unique=False) + + op.create_table('refunds_partial_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('payment_line_item_id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('refund_amount', sa.Numeric(precision=19, scale=2), autoincrement=False, nullable=False), + sa.Column('refund_type', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('disbursement_status_code', sa.String(length=20), autoincrement=False, nullable=True), + sa.Column('disbursement_date', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('updated_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('created_by', sa.String(length=50), autoincrement=False, nullable=False), + sa.Column('created_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('updated_name', sa.String(length=50), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['disbursement_status_code'], ['disbursement_status_codes.code'], ), + sa.ForeignKeyConstraint(['payment_line_item_id'], ['payment_line_items.id'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('refunds_partial_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_refunds_partial_history_payment_line_item_id'), ['payment_line_item_id'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('refunds_partial_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_refunds_partial_history_payment_line_item_id')) + + op.drop_table('refunds_partial_history') + with op.batch_alter_table('distribution_codes_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_distribution_codes_history_account_id')) + + op.drop_table('distribution_codes_history') + with op.batch_alter_table('cfs_accounts_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_cfs_account')) + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_bank_number')) + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_bank_branch_number')) + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_bank_account_number')) + batch_op.drop_index(batch_op.f('ix_cfs_accounts_history_account_id')) + + op.drop_table('cfs_accounts_history') + with op.batch_alter_table('account_fees_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_account_fees_history_account_id')) + + op.drop_table('account_fees_history') + with op.batch_alter_table('payment_accounts_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_payment_accounts_history_bcol_user_id')) + batch_op.drop_index(batch_op.f('ix_payment_accounts_history_bcol_account')) + batch_op.drop_index(batch_op.f('ix_payment_accounts_history_auth_account_id')) + + op.drop_table('payment_accounts_history') + with op.batch_alter_table('eft_short_names_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_eft_short_names_history_short_name')) + batch_op.drop_index(batch_op.f('ix_eft_short_names_history_auth_account_id')) + + op.drop_table('eft_short_names_history') + + with op.batch_alter_table('refunds_partial', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('payment_accounts', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('eft_short_names', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('distribution_codes', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('cfs_accounts', schema=None) as batch_op: + batch_op.drop_column('version') + + with op.batch_alter_table('account_fees', schema=None) as batch_op: + batch_op.drop_column('version') + # ### end Alembic commands ### diff --git a/pay-api/migrations/versions/2024_03_18_52ed2340a43c_move_from_version_to_history.py b/pay-api/migrations/versions/2024_03_18_52ed2340a43c_move_from_version_to_history.py new file mode 100644 index 000000000..7b779fb2a --- /dev/null +++ b/pay-api/migrations/versions/2024_03_18_52ed2340a43c_move_from_version_to_history.py @@ -0,0 +1,359 @@ +"""Move data from version to history table. +A special thanks to LEAR devs (Thor, Argus, Vysakh) for this migration and the history table implementation: +https://github.com/bcgov/lear/blob/feature-legal-name/legal-api/scripts/manual_db_scripts/legal_name_change/transfer_to_new_lear.sql + +Revision ID: 52ed2340a43c +Revises: fb3ba97b603a +Create Date: 2024-03-18 09:53:33.369110 + +""" +from alembic import op + + +# revision identifiers, used by Alembic. +revision = '52ed2340a43c' +down_revision = 'fb3ba97b603a' +branch_labels = None +depends_on = None + + +def upgrade(): + # Currently these are only set to version = 1 + op.execute(""" + update account_fees set version = + (select coalesce( + (select count(transaction_id) as version + from account_fees_version + where + account_fees.id = account_fees_version.id + group by + id + ), 1)); + """) + + op.execute(""" + update cfs_accounts set version = + (select coalesce( + (select count(transaction_id) as version + from cfs_accounts_version + where cfs_accounts.id = cfs_accounts_version.id + group by id + ), 1)); + """) + + op.execute(""" + update distribution_codes set version = + (select coalesce( + (select count(transaction_id) as version + from distribution_codes_version + where distribution_codes.distribution_code_id = distribution_codes_version.distribution_code_id + group by distribution_code_id + ),1)); + """) + + op.execute(""" + update eft_short_names set version = + (select coalesce( + (select count(transaction_id) as version + from eft_short_names_version + where eft_short_names.id = eft_short_names_version.id + group by id + ),1)); + """) + + op.execute(""" + update payment_accounts set version = + (select coalesce( + (select count(transaction_id) as version + from payment_accounts_version + where payment_accounts.id = payment_accounts_version.id + group by id + ),1)); + """) + + op.execute(""" + update refunds_partial set version = + (select coalesce( + (select count(transaction_id) as version + from refunds_partial_version + where refunds_partial.id = refunds_partial_version.id + group by id + ),1)); + """) + + op.execute(""" + with subquery as ( + select + afv.id, + account_id, + apply_filing_fees, + service_fee_code, + product, + created_on, + updated_on, + created_by, + created_name, + updated_by, + updated_name, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY afv.id ORDER BY afv.transaction_id ASC), 1) as version + from + account_fees_version afv + left join + public.transaction t on afv.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from + subquery sq + group by id + ) + + insert into + account_fees_history (id, apply_filing_fees, service_fee_code, product, created_on, updated_on, + created_by, created_name, updated_by, updated_name, changed, version) + select + sq.id, apply_filing_fees, service_fee_code, product, created_on, updated_on, + created_by, created_name, updated_by, updated_name, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where + sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + cav.id, + cfs_account, + cfs_party, + cfs_site, + payment_instrument_number, + contact_party, + bank_number, + bank_branch_number, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY cav.id ORDER BY cav.transaction_id ASC), 1) as version + from cfs_accounts_version cav + left join public.transaction t on cav.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from + subquery sq + group by id + ) + insert into + cfs_accounts_history (id, cfs_account, cfs_party, cfs_site, payment_instrument_number, + contact_party, bank_number, bank_branch_number, changed, version) + select + sq.id, cfs_account, cfs_party, cfs_site, payment_instrument_number, + contact_party, bank_number, bank_branch_number, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where + sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + dcv.distribution_code_id, + name, + client, + responsibility_centre, + service_line, + stob, + project_code, + start_date, + end_date, + stop_ejv, + service_fee_distribution_code_id, + disbursement_distribution_code_id, + account_id, + created_on, + updated_on, + created_by, + created_name, + updated_by, + updated_name, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY dcv.distribution_code_id ORDER BY dcv.transaction_id ASC), 1) as version + from distribution_codes_version dcv + left join public.transaction t on dcv.transaction_id = t.id + ), + max_versions as ( + select + sq.distribution_code_id, + max(version) as max_version + from subquery sq + group by sq.distribution_code_id + ) + + insert into + distribution_codes_history (distribution_code_id, name, client, responsibility_centre, + service_line, stob, project_code, start_date, end_date, stop_ejv, service_fee_distribution_code_id, + disbursement_distribution_code_id, account_id, created_on, updated_on, created_by, created_name, + updated_by, updated_name, changed, version) + select + sq.distribution_code_id, name, client, responsibility_centre, + service_line, stob, project_code, start_date, end_date, stop_ejv, service_fee_distribution_code_id, + disbursement_distribution_code_id, account_id, created_on, updated_on, created_by, created_name, + updated_by, updated_name, changed, version + from + subquery sq + left join + max_versions mv on mv.distribution_code_id = sq.distribution_code_id + where sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + esnv.id, + auth_account_id, + created_on, + short_name, + linked_by, + linked_by_name, + linked_on, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY esnv.id ORDER BY esnv.transaction_id ASC), 1) as version + from eft_short_names_version esnv + left join public.transaction t on esnv.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from + subquery sq + group by id + ) + insert into + eft_short_names_history (id, auth_account_id, created_on, short_name, linked_by, linked_by_name, linked_on, changed, version) + select + sq.id, auth_account_id, created_on, short_name, linked_by, linked_by_name, linked_on, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + pav.id, + auth_account_id, + name, + branch_name, + payment_method, + bcol_user_id, + bcol_account, + statement_notification_enabled, + credit, + billable, + eft_enable, + pad_activation_date, + pad_tos_accepted_date, + pad_tos_accepted_by, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY pav.id ORDER BY pav.transaction_id ASC), 1) as version + from + payment_accounts_version pav + left join + public.transaction t on pav.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from + subquery sq + group by id + ) + insert into + payment_accounts_history (id, auth_account_id, name, branch_name, payment_method, bcol_user_id, + bcol_account, statement_notification_enabled, credit, billable, eft_enable, pad_activation_date, + pad_tos_accepted_date, pad_tos_accepted_by, changed, version) + select + sq.id, auth_account_id, name, branch_name, payment_method, bcol_user_id, + bcol_account, statement_notification_enabled, credit, billable, eft_enable, pad_activation_date, + pad_tos_accepted_date, pad_tos_accepted_by, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where + sq.version != mv.max_version; + """) + + op.execute(""" + with subquery as ( + select + rpv.id, + payment_line_item_id, + refund_amount, + refund_type, + disbursement_status_code, + disbursement_date, + created_on, + updated_on, + created_by, + created_name, + updated_by, + updated_name, + t.issued_at as changed, + COALESCE(ROW_NUMBER() OVER (PARTITION BY rpv.id ORDER BY rpv.transaction_id ASC), 1) as version + from + refunds_partial_version rpv + left join + public.transaction t on rpv.transaction_id = t.id + ), + max_versions as ( + select + id, + max(version) as max_version + from subquery sq + group by id + ) + + insert into + refunds_partial_history (id, payment_line_item_id, refund_amount, refund_type, disbursement_status_code, + disbursement_date, created_on, updated_on, created_by, created_name, updated_by, + updated_name, changed, version) + select + sq.id, payment_line_item_id, refund_amount, refund_type, disbursement_status_code, + disbursement_date, created_on, updated_on, created_by, created_name, updated_by, + updated_name, changed, version + from + subquery sq + left join + max_versions mv on mv.id = sq.id + where + sq.version != mv.max_version; + """) + + +def downgrade(): + op.execute('update refunds_partial set version = 1;') + op.execute('update payment_accounts set version = 1;') + op.execute('update eft_short_names set version = 1;') + op.execute('update distribution_codes set version = 1;') + op.execute('update cfs_accounts set version = 1;') + op.execute('update account_fees set version = 1;') + op.execute('delete from refunds_partial_history;') + op.execute('delete from payment_accounts_history;') + op.execute('delete from eft_short_names_history;') + op.execute('delete from distribution_codes_history;') + op.execute('delete from cfs_accounts_history;') + op.execute('delete from account_fees_history;') diff --git a/pay-api/migrations/versions/6a6b042b831a_.py b/pay-api/migrations/versions/6a6b042b831a_.py index da308951f..adf1185f5 100644 --- a/pay-api/migrations/versions/6a6b042b831a_.py +++ b/pay-api/migrations/versions/6a6b042b831a_.py @@ -60,8 +60,8 @@ def upgrade(): ) distribution_code_link_table = table('distribution_code_links', - column('distribution_code_id', String), - column('fee_schedule_id', String) + column('distribution_code_id', Integer), + column('fee_schedule_id', Integer) ) # Product code/corp type diff --git a/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py b/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py index 866243b23..a2bceb3ae 100644 --- a/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py +++ b/pay-api/migrations/versions/b6e28faea978_rename_tables_to_plurals.py @@ -8,7 +8,7 @@ import re from alembic import op -from sqlalchemy import MetaData, create_engine +from sqlalchemy import MetaData, create_engine, inspect from sqlalchemy.engine import reflection # revision identifiers, used by Alembic. from sqlalchemy.engine.reflection import Inspector @@ -72,7 +72,7 @@ def upgrade(): """ conn = op.get_bind() - inspector = Inspector.from_engine(conn) + inspector = inspect(conn) tables = inspector.get_table_names() metadata = MetaData() metadata.reflect(conn) @@ -88,7 +88,7 @@ def upgrade(): def downgrade(): conn = op.get_bind() - inspector = Inspector.from_engine(conn) + inspector = inspect(conn) tables = inspector.get_table_names() metadata = MetaData() metadata.reflect(conn) diff --git a/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py b/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py index 20ab083f6..16b279295 100644 --- a/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py +++ b/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py @@ -8,7 +8,7 @@ from datetime import date from alembic import op -from sqlalchemy import Date, String, Boolean, text +from sqlalchemy import Date, String, Boolean, text, Integer from sqlalchemy.sql import column, table # revision identifiers, used by Alembic. @@ -29,8 +29,8 @@ def upgrade(): column('bcol_staff_fee_code', String) ) distribution_code_link_table = table('distribution_code_links', - column('distribution_code_id', String), - column('fee_schedule_id', String) + column('distribution_code_id', Integer), + column('fee_schedule_id', Integer) ) fee_schedule_table = table('fee_schedules', column('filing_type_code', String), diff --git a/pay-api/migrations/versions/e748b5c19247_.py b/pay-api/migrations/versions/e748b5c19247_.py index c58a8d01c..7f60b7356 100644 --- a/pay-api/migrations/versions/e748b5c19247_.py +++ b/pay-api/migrations/versions/e748b5c19247_.py @@ -10,7 +10,7 @@ from alembic import op import sqlalchemy as sa -from sqlalchemy import Date, Float, Integer, Boolean, String +from sqlalchemy import Date, Integer, Boolean, String from sqlalchemy.sql import column, table @@ -56,8 +56,8 @@ def upgrade(): ) distribution_code_link_table = table('distribution_code_links', - column('distribution_code_id', String), - column('fee_schedule_id', String) + column('distribution_code_id', Integer), + column('fee_schedule_id', Integer) ) # Product code/corp type diff --git a/pay-api/poetry.lock b/pay-api/poetry.lock index adadbd2c3..49cd2bdc5 100644 --- a/pay-api/poetry.lock +++ b/pay-api/poetry.lock @@ -522,13 +522,13 @@ tests = ["coverage", "coveralls", "dill", "mock", "nose"] [[package]] name = "faker" -version = "24.2.0" +version = "24.3.0" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-24.2.0-py3-none-any.whl", hash = "sha256:dce4754921f9fa7e2003c26834093361b8f45072e0f46f172d6ca1234774ecd4"}, - {file = "Faker-24.2.0.tar.gz", hash = "sha256:87d5e7730426e7b36817921679c4eaf3d810cedb8c81194f47adc3df2122ca18"}, + {file = "Faker-24.3.0-py3-none-any.whl", hash = "sha256:9978025e765ba79f8bf6154c9630a9c2b7f9c9b0f175d4ad5e04b19a82a8d8d6"}, + {file = "Faker-24.3.0.tar.gz", hash = "sha256:5fb5aa9749d09971e04a41281ae3ceda9414f683d4810a694f8a8eebb8f9edec"}, ] [package.dependencies] @@ -1819,24 +1819,6 @@ pluggy = ">=1.4,<2.0" [package.extras] testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] -[[package]] -name = "pytest-asyncio" -version = "0.18.3" -description = "Pytest support for asyncio" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-asyncio-0.18.3.tar.gz", hash = "sha256:7659bdb0a9eb9c6e3ef992eef11a2b3e69697800ad02fb06374a210d85b29f91"}, - {file = "pytest_asyncio-0.18.3-1-py3-none-any.whl", hash = "sha256:16cf40bdf2b4fb7fc8e4b82bd05ce3fbcd454cbf7b92afc445fe299dabb88213"}, - {file = "pytest_asyncio-0.18.3-py3-none-any.whl", hash = "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84"}, -] - -[package.dependencies] -pytest = ">=6.1.0" - -[package.extras] -testing = ["coverage (==6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (==0.931)", "pytest-trio (>=0.7.0)"] - [[package]] name = "pytest-cov" version = "4.1.0" @@ -2116,6 +2098,22 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] +[[package]] +name = "sql-versioning" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "feature-legal-name" +resolved_reference = "bb3209f8e8894c9b9f7be95a9fd871c644e2ec69" +subdirectory = "python/common/sql-versioning" + [[package]] name = "sqlalchemy" version = "2.0.28" @@ -2352,4 +2350,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "d61ec06879fc556df5d136cdee89a237a5148971b5b4e34f334b397cc64c7e97" +content-hash = "6ed39989416242b0917aea66317b7c94e2d6bdc34e58c391ce23c15defbd55e6" diff --git a/pay-api/pyproject.toml b/pay-api/pyproject.toml index 58938ac0c..c9639f753 100644 --- a/pay-api/pyproject.toml +++ b/pay-api/pyproject.toml @@ -83,6 +83,7 @@ urllib3 = "2.2.1" simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} pg8000 = "^1.30.5" +sql-versioning = { git = "https://github.com/bcgov/lear.git", subdirectory = "python/common/sql-versioning", branch = "feature-legal-name" } [tool.poetry.group.dev.dependencies] @@ -106,7 +107,6 @@ pylint-flask = "^0.6" pydocstyle = "^6.3.0" isort = "^5.13.2" lovely-pytest-docker = "^0.3.1" -pytest-asyncio = "0.18.3" faker = "^24.2.0" [build-system] diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 3fd18204c..8f7b201b1 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -239,7 +239,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods DB_PORT = _get_config('DATABASE_TEST_PORT', default='5432') SQLALCHEMY_DATABASE_URI = _get_config( 'DATABASE_TEST_URL', - default=f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}', + default=f'postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}', ) JWT_OIDC_TEST_MODE = True diff --git a/pay-api/src/pay_api/models/account_fee.py b/pay-api/src/pay_api/models/account_fee.py index e0383b920..7df2f5663 100644 --- a/pay-api/src/pay_api/models/account_fee.py +++ b/pay-api/src/pay_api/models/account_fee.py @@ -17,9 +17,10 @@ from marshmallow import fields, post_dump from sqlalchemy import Boolean, ForeignKey from sqlalchemy.orm import relationship +from sql_versioning import Versioned from .audit import Audit -from .base_model import VersionedModel +from .base_model import BaseModel from .base_schema import BaseSchema from .corp_type import CorpType from .db import db @@ -27,7 +28,7 @@ from .payment_account import PaymentAccount -class AccountFee(Audit, VersionedModel): +class AccountFee(Audit, Versioned, BaseModel): """This class manages all of the base data about Account Fees.""" __tablename__ = 'account_fees' diff --git a/pay-api/src/pay_api/models/base_model.py b/pay-api/src/pay_api/models/base_model.py index f4cb85e86..22a10e467 100644 --- a/pay-api/src/pay_api/models/base_model.py +++ b/pay-api/src/pay_api/models/base_model.py @@ -13,10 +13,6 @@ # limitations under the License. """Super class to handle all operations related to base model.""" -# from flask import current_app -# from sqlalchemy_continuum.plugins.flask import fetch_remote_addr - -from pay_api.utils.user_context import user_context from .db import db @@ -35,7 +31,6 @@ def flush(self): """Save and flush.""" db.session.add(self) db.session.flush() - self.create_activity(self) return self def save_or_add(self, auto_save: bool): @@ -49,16 +44,12 @@ def save_or_add(self, auto_save: bool): def save(self): """Save and commit.""" db.session.add(self) - db.session.flush() - self.create_activity(self) db.session.commit() return self def delete(self): """Delete and commit.""" db.session.delete(self) - db.session.flush() - self.create_activity(self, is_delete=True) db.session.commit() @staticmethod @@ -69,35 +60,4 @@ def rollback(): @classmethod def find_by_id(cls, identifier: int): """Return model by id.""" - return cls.query.get(identifier) - - @classmethod - def create_activity(cls, obj, is_delete=False): - """Create activity records if the model is versioned.""" - # TODO fix this later - # if isinstance(obj, VersionedModel) and not current_app.config.get('DISABLE_ACTIVITY_LOGS'): - # if is_delete: - # verb = 'delete' - # else: - # verb = 'update' - # activity = activity_plugin.activity_cls(verb=verb, object=obj, data={ - # 'user_name': cls._get_user_name(), - # 'remote_addr': fetch_remote_addr() - # }) - # db.session.add(activity) - - @staticmethod - @user_context - def _get_user_name(**kwargs): - """Return current user user_name.""" - return kwargs['user'].user_name - - -class VersionedModel(BaseModel): - """This class manages all of the base code, type or status model functions.""" - - __abstract__ = True - - __versioned__ = { - 'exclude': [] - } + return db.session.get(cls, identifier) diff --git a/pay-api/src/pay_api/models/cfs_account.py b/pay-api/src/pay_api/models/cfs_account.py index 259b4a835..b16eb778b 100644 --- a/pay-api/src/pay_api/models/cfs_account.py +++ b/pay-api/src/pay_api/models/cfs_account.py @@ -15,6 +15,7 @@ from __future__ import annotations from typing import List from flask import current_app +from sql_versioning import Versioned from sqlalchemy import ForeignKey from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import relationship @@ -24,11 +25,11 @@ from pay_api.utils.enums import CfsAccountStatus from .base_schema import BaseSchema -from .base_model import VersionedModel +from .base_model import BaseModel from .db import db -class CfsAccount(VersionedModel): # pylint:disable=too-many-instance-attributes +class CfsAccount(Versioned, BaseModel): # pylint:disable=too-many-instance-attributes """This class manages all of the base data about PayBC Account.""" __tablename__ = 'cfs_accounts' diff --git a/pay-api/src/pay_api/models/db.py b/pay-api/src/pay_api/models/db.py index 7f35342e0..dee2fa81c 100755 --- a/pay-api/src/pay_api/models/db.py +++ b/pay-api/src/pay_api/models/db.py @@ -17,18 +17,11 @@ """ from flask_marshmallow import Marshmallow from flask_sqlalchemy import SQLAlchemy -# from sqlalchemy_continuum import make_versioned -# from sqlalchemy_continuum.plugins import ActivityPlugin +from sql_versioning import versioned_session from .custom_query import CustomQuery # by convention in the Flask community these are lower case, # whereas pylint wants them upper case ma = Marshmallow() # pylint: disable=invalid-name db = SQLAlchemy(query_class=CustomQuery) # pylint: disable=invalid-name - - -# TODO FIX THIS -# activity_plugin = ActivityPlugin() # pylint: disable=invalid-name - -# TODO fix this -# make_versioned(user_cls=None, plugins=[activity_plugin]) +versioned_session(db.session) diff --git a/pay-api/src/pay_api/models/distribution_code.py b/pay-api/src/pay_api/models/distribution_code.py index 494ea8883..1722c8ded 100644 --- a/pay-api/src/pay_api/models/distribution_code.py +++ b/pay-api/src/pay_api/models/distribution_code.py @@ -17,11 +17,11 @@ from datetime import date from marshmallow import fields +from sql_versioning import Versioned from sqlalchemy import Boolean, ForeignKey from sqlalchemy.orm import relationship from .audit import Audit, AuditSchema, BaseModel -from .base_model import VersionedModel from .base_schema import BaseSchema from .db import db, ma @@ -72,7 +72,7 @@ def bulk_save_links(cls, links: list): BaseModel.commit() -class DistributionCode(Audit, VersionedModel): # pylint:disable=too-many-instance-attributes +class DistributionCode(Audit, Versioned, BaseModel): # pylint:disable=too-many-instance-attributes """This class manages all of the base data about distribution code. Distribution code holds details on the codes for how the collected payment is going to be distributed. diff --git a/pay-api/src/pay_api/models/eft_short_names.py b/pay-api/src/pay_api/models/eft_short_names.py index bae3ccc00..bec08a98c 100644 --- a/pay-api/src/pay_api/models/eft_short_names.py +++ b/pay-api/src/pay_api/models/eft_short_names.py @@ -17,12 +17,14 @@ from attrs import define -from .base_model import VersionedModel +from sql_versioning import Versioned + +from .base_model import BaseModel from .db import db from ..utils.util import cents_to_decimal -class EFTShortnames(VersionedModel): # pylint: disable=too-many-instance-attributes +class EFTShortnames(Versioned, BaseModel): # pylint: disable=too-many-instance-attributes """This class manages the EFT short name to auth account mapping.""" __tablename__ = 'eft_short_names' diff --git a/pay-api/src/pay_api/models/payment.py b/pay-api/src/pay_api/models/payment.py index 2faa840b0..be3c35823 100644 --- a/pay-api/src/pay_api/models/payment.py +++ b/pay-api/src/pay_api/models/payment.py @@ -164,7 +164,7 @@ def search_account_payments(cls, auth_account_id: str, payment_status: str, page # If call is to get NSF payments, get only active failed payments. # Exclude any payments which failed first and paid later. query = query.filter(or_(InvoiceReference.status_code == InvoiceReferenceStatus.ACTIVE.value, - Payment.cons_inv_number.in_(consolidated_inv_subquery))) + Payment.cons_inv_number.in_(consolidated_inv_subquery.select()))) query = query.order_by(Payment.id.asc()) pagination = query.paginate(per_page=limit, page=page) @@ -186,7 +186,7 @@ def find_payments_to_consolidate(cls, auth_account_id: str): .filter(InvoiceReference.status_code == InvoiceReferenceStatus.ACTIVE.value) \ .filter(PaymentAccount.auth_account_id == auth_account_id) \ .filter(or_(Payment.payment_status_code == PaymentStatus.FAILED.value, - Payment.invoice_number.in_(consolidated_inv_subquery))) + Payment.invoice_number.in_(consolidated_inv_subquery.select()))) return query.all() @@ -245,14 +245,14 @@ def search_purchase_history(cls, # noqa:E501; pylint:disable=too-many-arguments count = cls.get_count(auth_account_id, search_filter) # Add pagination sub_query = cls.generate_subquery(auth_account_id, search_filter, limit, page) - result = query.order_by(Invoice.id.desc()).filter(Invoice.id.in_(sub_query.subquery())).all() + result = query.order_by(Invoice.id.desc()).filter(Invoice.id.in_(sub_query.subquery().select())).all() # If maximum number of records is provided, return it as total if max_no_records > 0: count = max_no_records if max_no_records < count else count elif max_no_records > 0: # If maximum number of records is provided, set the page with that number sub_query = cls.generate_subquery(auth_account_id, search_filter, max_no_records, page=None) - result, count = query.filter(Invoice.id.in_(sub_query.subquery())).all(), sub_query.count() + result, count = query.filter(Invoice.id.in_(sub_query.subquery().select())).all(), sub_query.count() else: count = cls.get_count(auth_account_id, search_filter) if count > 60000: diff --git a/pay-api/src/pay_api/models/payment_account.py b/pay-api/src/pay_api/models/payment_account.py index 21a0bb16f..3e2b7b72a 100644 --- a/pay-api/src/pay_api/models/payment_account.py +++ b/pay-api/src/pay_api/models/payment_account.py @@ -16,13 +16,14 @@ from attrs import define from marshmallow import fields from sqlalchemy import Boolean, ForeignKey +from sql_versioning import Versioned -from .base_model import VersionedModel +from .base_model import BaseModel from .db import db from .base_schema import BaseSchema -class PaymentAccount(VersionedModel): # pylint: disable=too-many-instance-attributes +class PaymentAccount(Versioned, BaseModel): # pylint: disable=too-many-instance-attributes """This class manages all of the base data about Payment Account.""" __tablename__ = 'payment_accounts' @@ -44,9 +45,6 @@ class PaymentAccount(VersionedModel): # pylint: disable=too-many-instance-attri 'bcol_user_id', 'billable', 'branch_name', - 'created_by', - 'created_name', - 'created_on', 'credit', 'eft_enable', 'name', @@ -54,10 +52,7 @@ class PaymentAccount(VersionedModel): # pylint: disable=too-many-instance-attri 'pad_tos_accepted_by', 'pad_tos_accepted_date', 'payment_method', - 'statement_notification_enabled', - 'updated_by', - 'updated_name', - 'updated_on' + 'statement_notification_enabled' ] } diff --git a/pay-api/src/pay_api/models/refunds_partial.py b/pay-api/src/pay_api/models/refunds_partial.py index de85c361f..8147ba687 100644 --- a/pay-api/src/pay_api/models/refunds_partial.py +++ b/pay-api/src/pay_api/models/refunds_partial.py @@ -15,15 +15,16 @@ from decimal import Decimal from attrs import define +from sql_versioning import Versioned from sqlalchemy import ForeignKey from .audit import Audit -from .base_model import VersionedModel +from .base_model import BaseModel from .db import db from ..utils.enums import RefundsPartialType -class RefundsPartial(Audit, VersionedModel): # pylint: disable=too-many-instance-attributes +class RefundsPartial(Audit, Versioned, BaseModel): # pylint: disable=too-many-instance-attributes """This class manages the data for payment line item partial refunds.""" __tablename__ = 'refunds_partial' @@ -42,11 +43,15 @@ class RefundsPartial(Audit, VersionedModel): # pylint: disable=too-many-instanc 'id', 'created_by', 'created_on', - 'disbursement_date', + 'created_name', 'disbursement_status_code', + 'disbursement_date', 'payment_line_item_id', 'refund_amount', 'refund_type', + 'updated_by', + 'updated_on', + 'updated_name' ] } diff --git a/pay-api/src/pay_api/models/routing_slip.py b/pay-api/src/pay_api/models/routing_slip.py index 6ed6baf24..8a5cd062c 100644 --- a/pay-api/src/pay_api/models/routing_slip.py +++ b/pay-api/src/pay_api/models/routing_slip.py @@ -215,7 +215,7 @@ def search(cls, search_filter: Dict, # pylint: disable=too-many-arguments, too- limit(limit).\ offset((page - 1) * limit).\ subquery() - query = query.filter(RoutingSlip.id.in_(sub_query)) + query = query.filter(RoutingSlip.id.in_(sub_query.select())) result = query.all() count = len(result) diff --git a/pay-api/src/pay_api/models/statement.py b/pay-api/src/pay_api/models/statement.py index fd3b0c5ed..422bf1ffd 100644 --- a/pay-api/src/pay_api/models/statement.py +++ b/pay-api/src/pay_api/models/statement.py @@ -13,12 +13,12 @@ # limitations under the License. """Model to handle statements data.""" +from datetime import datetime import pytz from marshmallow import fields +from sql_versioning import history_cls from sqlalchemy import ForeignKey, and_, case, literal_column from sqlalchemy.ext.hybrid import hybrid_property -# from sqlalchemy.orm import aliased -# from sqlalchemy_continuum import transaction_class, version_class from pay_api.utils.constants import LEGISLATIVE_TIMEZONE from pay_api.utils.enums import StatementFrequency @@ -72,24 +72,46 @@ class Statement(BaseModel): @hybrid_property def payment_methods(self): """Return all payment methods that were active during the statement period based on payment account versions.""" - return [] - # TODO FIX THIS. - # payment_account_version = version_class(PaymentAccount) - # transaction_start = aliased(transaction_class(PaymentAccount)) - # transaction_end = aliased(transaction_class(PaymentAccount)) - - # subquery = db.session.query(func.array_agg(func.DISTINCT(payment_account_version.payment_method)) - # .label('payment_methods'))\ - # .join(Statement, Statement.payment_account_id == payment_account_version.id)\ - # .join(transaction_start, payment_account_version.transaction_id == transaction_start.id)\ - # .outerjoin(transaction_end, payment_account_version.end_transaction_id == transaction_end.id)\ - # .filter(payment_account_version.id == self.payment_account_id) \ - # .filter(and_(Statement.id == self.id, transaction_start.issued_at <= Statement.to_date, - # or_(transaction_end.issued_at >= Statement.from_date, - # transaction_end.id.is_(None))))\ - # .group_by(Statement.id).first() - - # return subquery[0] if subquery else [] + payment_account = PaymentAccount.find_by_id(self.payment_account_id) + payment_account_history_class = history_cls(PaymentAccount) + payment_account_history = db.session.query(payment_account_history_class) \ + .join(Statement, Statement.payment_account_id == payment_account_history_class.id) \ + .filter(payment_account_history_class.id == self.payment_account_id) \ + .filter(Statement.id == self.id) \ + .order_by(payment_account_history_class.changed.asc()) \ + .all() + + # The code below combines the history rows with the current state of payment_account. + # This is necessary because the new versioning doesn't have from and to dates, only changed. + # It is possible to handle this through SQL using LEAD and LAG functions. + # Since the volume of rows is low, the pythonic approach should be sufficient. + history_ranges = [ + { + 'from_date': datetime.min.date() if idx == 0 else payment_account_history[idx - 1].changed.date(), + 'to_date': historical.changed.date(), + 'payment_method': payment_account.payment_method if idx == len(payment_account_history) - 1 + else historical.payment_method + } + for idx, historical in enumerate(payment_account_history) + ] + + history_ranges.append({ + 'from_date': payment_account_history[-1].changed.date() if payment_account_history else datetime.min.date(), + 'to_date': datetime.max.date(), + 'payment_method': payment_account.payment_method + }) + + payment_methods = { + history_item['payment_method'] + for history_item in history_ranges + if ( + history_item['from_date'] <= self.from_date <= history_item['to_date'] or + history_item['from_date'] <= self.to_date <= history_item['to_date'] or + self.from_date <= history_item['from_date'] <= self.to_date <= history_item['to_date'] + ) + } + + return list(payment_methods) @classmethod def find_all_statements_for_account(cls, auth_account_id: str, page, limit): diff --git a/pay-api/src/pay_api/utils/util.py b/pay-api/src/pay_api/utils/util.py index 49343b370..0637f4b01 100755 --- a/pay-api/src/pay_api/utils/util.py +++ b/pay-api/src/pay_api/utils/util.py @@ -25,7 +25,7 @@ from holidays.constants import GOVERNMENT, OPTIONAL, PUBLIC from holidays.countries import Canada import pytz -from dpath import util as dpath_util +from dpath import get as dpath_get from flask import current_app from .constants import DT_SHORT_FORMAT @@ -73,7 +73,7 @@ def get_str_by_path(payload: Dict, path: str) -> str: return None try: - raw = dpath_util.get(payload, path) + raw = dpath_get(payload, path) return str(raw) if raw is not None else raw except (IndexError, KeyError, TypeError): return None diff --git a/pay-api/tests/conftest.py b/pay-api/tests/conftest.py index 3eb46795b..33d553869 100755 --- a/pay-api/tests/conftest.py +++ b/pay-api/tests/conftest.py @@ -80,7 +80,7 @@ def db(app): # pylint: disable=redefined-outer-name, invalid-name return _db -@pytest.fixture(scope='function', autouse=True) +@pytest.fixture(scope='function') def session(db, app): # pylint: disable=redefined-outer-name, invalid-name """Return a function-scoped session.""" with app.app_context(): @@ -89,6 +89,7 @@ def session(db, app): # pylint: disable=redefined-outer-name, invalid-name sess = db._make_scoped_session(dict(bind=conn)) # pylint: disable=protected-access # Establish SAVEPOINT (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint) nested = sess.begin_nested() + old_session = db.session db.session = sess db.session.commit = nested.commit db.session.rollback = nested.rollback @@ -114,6 +115,8 @@ def restart_savepoint(sess2, trans): # pylint: disable=unused-variable finally: db.session.remove() transaction.rollback() + event.remove(sess, 'after_transaction_end', restart_savepoint) + db.session = old_session @pytest.fixture() diff --git a/pay-api/tests/unit/api/fas/test_refund.py b/pay-api/tests/unit/api/fas/test_refund.py index 10f1e4ef1..18cfc28f2 100644 --- a/pay-api/tests/unit/api/fas/test_refund.py +++ b/pay-api/tests/unit/api/fas/test_refund.py @@ -29,7 +29,7 @@ fake = Faker() -def test_refund_routing_slips(client, jwt): +def test_refund_routing_slips(session, client, jwt): """Assert refund works for routing slips.""" payload = get_routing_slip_request() token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value, Role.FAS_REFUND.value]), @@ -88,7 +88,7 @@ def test_refund_routing_slips(client, jwt): assert rv.json.get('status') == RoutingSlipStatus.REFUND_AUTHORIZED.value -def test_refund_routing_slips_reject(client, jwt): +def test_refund_routing_slips_reject(session, client, jwt): """Assert refund works for routing slips.""" payload = get_routing_slip_request() token = jwt.create_jwt( @@ -117,7 +117,7 @@ def test_refund_routing_slips_reject(client, jwt): assert rv.json.get('status') == RoutingSlipStatus.ACTIVE.value -def test_refund_routing_slips_zero_dollar_error(client, jwt): +def test_refund_routing_slips_zero_dollar_error(session, client, jwt): """Assert zero dollar refund fails.""" payload = get_routing_slip_request(cheque_receipt_numbers=[('1234567890', PaymentMethod.CHEQUE.value, 0.00)]) token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value, Role.FAS_REFUND.value]), diff --git a/pay-api/tests/unit/api/fas/test_routing_slip.py b/pay-api/tests/unit/api/fas/test_routing_slip.py index 12561677b..de829e550 100755 --- a/pay-api/tests/unit/api/fas/test_routing_slip.py +++ b/pay-api/tests/unit/api/fas/test_routing_slip.py @@ -44,7 +44,7 @@ ]), get_routing_slip_request(cheque_receipt_numbers=[('0001', PaymentMethod.CASH.value, 2000)]) ]) -def test_create_routing_slips(client, jwt, payload): +def test_create_routing_slips(session, client, jwt, payload): """Assert that the endpoint returns 200.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -59,7 +59,7 @@ def test_create_routing_slips(client, jwt, payload): assert len(allowed_statuses) == len(RoutingSlipStatusTransitionService.STATUS_TRANSITIONS.get('ACTIVE')) -def test_create_routing_slips_search(client, jwt, app): +def test_create_routing_slips_search(session, client, jwt, app): """Assert that the search works.""" claims = get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value]) token = jwt.create_jwt(claims, token_header) @@ -137,7 +137,7 @@ def test_create_routing_slips_search(client, jwt, app): assert len(items) == 0 -def test_link_routing_slip_parent_is_a_child(client, jwt): +def test_link_routing_slip_parent_is_a_child(session, client, jwt): """Assert linking to a child fails.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, Role.FAS_SEARCH.value]), token_header) @@ -163,7 +163,7 @@ def test_link_routing_slip_parent_is_a_child(client, jwt): assert rv.status_code == 400 -def test_link_nsf(client, jwt): +def test_link_nsf(session, client, jwt): """Assert linking to a child fails.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, Role.FAS_SEARCH.value, Role.FAS_EDIT.value]), @@ -186,7 +186,7 @@ def test_link_nsf(client, jwt): assert rv.json.get('title') == 'Routing Slip cannot be linked.' -def test_link_routing_slip_invalid_status(client, jwt, app): +def test_link_routing_slip_invalid_status(session, client, jwt, app): """Assert that the linking of routing slip works as expected.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, Role.FAS_SEARCH.value, Role.FAS_REFUND.value, @@ -225,7 +225,7 @@ def test_link_routing_slip_invalid_status(client, jwt, app): assert rv.json.get('type') == 'RS_IN_INVALID_STATUS', 'parent is invalid.' -def test_link_routing_slip(client, jwt, app): +def test_link_routing_slip(session, client, jwt, app): """Assert that the linking of routing slip works as expected.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, Role.FAS_SEARCH.value, Role.FAS_EDIT.value]), @@ -328,7 +328,7 @@ def test_link_routing_slip(client, jwt, app): assert rv.status_code == 200, 'parent can have transactions' -def test_create_routing_slips_search_with_folio_number(client, jwt, app): +def test_create_routing_slips_search_with_folio_number(session, client, jwt, app): """Assert that the search works.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -398,7 +398,7 @@ def test_create_routing_slips_search_with_folio_number(client, jwt, app): assert len(items[0].get('invoices')) == 2, 'folio alone works.' -def test_create_routing_slips_search_with_receipt(client, jwt, app): +def test_create_routing_slips_search_with_receipt(session, client, jwt, app): """Assert that the search works.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -455,7 +455,7 @@ def test_create_routing_slips_search_with_receipt(client, jwt, app): @pytest.mark.parametrize('payload', [ get_routing_slip_request(number='559555333'), ]) -def test_create_routing_slips_unauthorized(client, jwt, payload): +def test_create_routing_slips_unauthorized(session, client, jwt, payload): """Assert that the endpoint returns 401 for users with no fas_editor role.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_USER.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -475,7 +475,7 @@ def test_create_routing_slips_unauthorized(client, jwt, payload): @pytest.mark.parametrize('payload', [ get_routing_slip_request(number='559555333'), ]) -def test_create_routing_slips_invalid_digits(client, jwt, payload): +def test_create_routing_slips_invalid_digits(session, client, jwt, payload): """Assert POST returns 400 when providing invalid routing slip number.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_USER.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -486,7 +486,7 @@ def test_create_routing_slips_invalid_digits(client, jwt, payload): current_app.config['ALLOW_LEGACY_ROUTING_SLIPS'] = True -def test_get_routing_slips_invalid_digits(client, jwt, app): +def test_get_routing_slips_invalid_digits(session, client, jwt, app): """Assert GET returns 400 when providing invalid routing slip number.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_VIEW.value, Role.FAS_CREATE.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -500,7 +500,7 @@ def test_get_routing_slips_invalid_digits(client, jwt, app): @pytest.mark.parametrize('payload', [ get_routing_slip_request(number='206380883'), ]) -def test_routing_slips_for_errors(client, jwt, payload): +def test_routing_slips_for_errors(session, client, jwt, payload): """Assert that the endpoint returns 401 for users with no fas_editor role.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -521,7 +521,7 @@ def test_routing_slips_for_errors(client, jwt, payload): assert rv.status_code == 204 -def test_update_routing_slip_status(client, jwt, app): +def test_update_routing_slip_status(session, client, jwt, app): """Assert that the endpoint returns 200.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_EDIT.value, Role.FAS_VIEW.value]), token_header) @@ -552,7 +552,7 @@ def test_update_routing_slip_status(client, jwt, app): assert rv.status_code == 400 -def test_routing_slip_report(client, jwt, app): +def test_routing_slip_report(session, client, jwt, app): """Assert that the endpoint returns 201.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_REPORTS.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -561,7 +561,7 @@ def test_routing_slip_report(client, jwt, app): assert rv.status_code == 201 -def test_create_comment_with_valid_routing_slips(client, jwt): +def test_create_comment_with_valid_routing_slips(session, client, jwt): """Assert that the endpoint returns 201.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -577,7 +577,7 @@ def test_create_comment_with_valid_routing_slips(client, jwt): assert rv.json.get('submitterDisplayName') -def test_create_comment_with_invalid_routing_slips(client, jwt): +def test_create_comment_with_invalid_routing_slips(session, client, jwt): """Assert that the endpoint returns 201.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -589,7 +589,7 @@ def test_create_comment_with_invalid_routing_slips(client, jwt): assert rv.status_code == 400 -def test_create_comment_with_invalid_body_request(client, jwt): +def test_create_comment_with_invalid_body_request(session, client, jwt): """Assert that the endpoint returns 201.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -602,7 +602,7 @@ def test_create_comment_with_invalid_body_request(client, jwt): assert rv.status_code == 400 -def test_create_comment_with_valid_comment_schema(client, jwt): +def test_create_comment_with_valid_comment_schema(session, client, jwt): """Assert that the endpoint returns 201 for valid comment schema.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -618,7 +618,7 @@ def test_create_comment_with_valid_comment_schema(client, jwt): assert rv.json.get('submitterDisplayName') -def test_create_comment_with_invalid_comment_schema(client, jwt): +def test_create_comment_with_invalid_comment_schema(session, client, jwt): """Assert that the endpoint returns 400 for invalid comment schema.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -634,7 +634,7 @@ def test_create_comment_with_invalid_comment_schema(client, jwt): assert rv.status_code == 400 -def test_create_comment_with_valid_comment_bcrs_schema(client, jwt): +def test_create_comment_with_valid_comment_bcrs_schema(session, client, jwt): """Assert that the endpoint returns 201 for valid comment schema.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -650,7 +650,7 @@ def test_create_comment_with_valid_comment_bcrs_schema(client, jwt): assert rv.json.get('submitterDisplayName') -def test_create_comment_with_invalid_comment_bcrs_schema(client, jwt): +def test_create_comment_with_invalid_comment_bcrs_schema(session, client, jwt): """Assert that the endpoint returns 201 for valid comment schema.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -666,7 +666,7 @@ def test_create_comment_with_invalid_comment_bcrs_schema(client, jwt): assert rv.status_code == 400 -def test_get_valid_comments(client, jwt): +def test_get_valid_comments(session, client, jwt): """Assert that the endpoint returns 200.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -696,7 +696,7 @@ def test_get_valid_comments(client, jwt): assert rv.status_code == 400 -def test_get_invalid_comments(client, jwt): +def test_get_invalid_comments(session, client, jwt): """Assert that the endpoint returns 400 based on conditions.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_VIEW.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -706,7 +706,7 @@ def test_get_invalid_comments(client, jwt): assert rv.json.get('type') == 'FAS_INVALID_ROUTING_SLIP_NUMBER' -def test_create_routing_slips_invalid_number(client, jwt, app): +def test_create_routing_slips_invalid_number(session, client, jwt, app): """Assert that the rs number validation works.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -720,7 +720,7 @@ def test_create_routing_slips_invalid_number(client, jwt, app): assert rv.status_code == 400 -def test_update_routing_slip_writeoff(client, jwt, app): +def test_update_routing_slip_writeoff(session, client, jwt, app): """Assert that the endpoint returns 200.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_EDIT.value, Role.FAS_VIEW.value]), token_header) @@ -748,7 +748,7 @@ def test_update_routing_slip_writeoff(client, jwt, app): assert rv.status_code == 200 -def test_create_routing_slip_null_cheque_date(client, jwt, app): +def test_create_routing_slip_null_cheque_date(session, client, jwt, app): """Assert that the endpoint returns invalid request for null payment date.""" token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_EDIT.value, Role.FAS_VIEW.value]), token_header) @@ -772,7 +772,7 @@ def test_create_routing_slip_null_cheque_date(client, jwt, app): assert rv.status_code == 400 -def test_routing_slip_link_attempt(client, jwt, app): +def test_routing_slip_link_attempt(session, client, jwt, app): """12033 - Scenario 3. Routing slip is Completed, attempt to be linked. @@ -803,7 +803,7 @@ def test_routing_slip_link_attempt(client, jwt, app): assert rv.status_code == 400 -def test_routing_slip_status_to_nsf_attempt(client, jwt, app): +def test_routing_slip_status_to_nsf_attempt(session, client, jwt, app): """12033 - Scenario 4. Routing slip in Completed, @@ -831,7 +831,7 @@ def test_routing_slip_status_to_nsf_attempt(client, jwt, app): assert rv.status_code == 200, 'status changed successfully.' -def test_routing_slip_void(client, jwt, app): +def test_routing_slip_void(session, client, jwt, app): """For testing void routing slips.""" # Create routing slip. token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, @@ -872,7 +872,7 @@ def test_routing_slip_void(client, jwt, app): assert rv.json.get('remainingAmount') == 0 -def test_routing_slip_correction(client, jwt, app): +def test_routing_slip_correction(session, client, jwt, app): """For testing correction of routing slips.""" # Create routing slip. token = jwt.create_jwt(get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_LINK.value, diff --git a/pay-api/tests/unit/api/test_payment_request.py b/pay-api/tests/unit/api/test_payment_request.py index 5894fc145..b7348b6e5 100755 --- a/pay-api/tests/unit/api/test_payment_request.py +++ b/pay-api/tests/unit/api/test_payment_request.py @@ -320,7 +320,7 @@ def test_payment_creation_with_routing_slip(session, client, jwt, app): assert schema_utils.validate(rv.json, 'invoice')[0] -def test_zero_dollar_payment_creation_with_existing_routing_slip(client, jwt): +def test_zero_dollar_payment_creation_with_existing_routing_slip(session, client, jwt): """Assert that the endpoint returns 201.""" claims = get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value, Role.STAFF.value, 'make_payment']) token = jwt.create_jwt(claims, token_header) @@ -349,7 +349,7 @@ def test_zero_dollar_payment_creation_with_existing_routing_slip(client, jwt): get_payment_request(), get_payment_request_without_bn() ]) -def test_payment_creation_with_existing_routing_slip(client, jwt, payment_requests): +def test_payment_creation_with_existing_routing_slip(session, client, jwt, payment_requests): """Assert that the endpoint returns 201.""" claims = get_claims(roles=[Role.FAS_CREATE.value, Role.FAS_SEARCH.value, Role.STAFF.value, 'make_payment']) token = jwt.create_jwt(claims, token_header) @@ -375,7 +375,7 @@ def test_payment_creation_with_existing_routing_slip(client, jwt, payment_reques assert items[0].get('remainingAmount') == payload.get('payments')[0].get('paidAmount') - total -def test_payment_creation_with_existing_invalid_routing_slip_invalid(client, jwt): +def test_payment_creation_with_existing_invalid_routing_slip_invalid(session, client, jwt): """Assert that the endpoint returns 201.""" claims = get_claims( roles=[Role.FAS_CREATE.value, Role.FAS_EDIT.value, Role.STAFF.value, 'make_payment', Role.FAS_LINK.value]) diff --git a/pay-api/tests/unit/services/test_direct_pay_service.py b/pay-api/tests/unit/services/test_direct_pay_service.py index 8a2539465..17f0d2fb7 100644 --- a/pay-api/tests/unit/services/test_direct_pay_service.py +++ b/pay-api/tests/unit/services/test_direct_pay_service.py @@ -180,7 +180,7 @@ def test_get_receipt(session, public_user_mock): assert rcpt is not None -def test_process_cfs_refund_success(monkeypatch): +def test_process_cfs_refund_success(session, monkeypatch): """Assert refund is successful, when providing a PAID invoice, receipt, a COMPLETED invoice reference.""" payment_account = factory_payment_account() invoice = factory_invoice(payment_account) @@ -199,7 +199,7 @@ def test_process_cfs_refund_success(monkeypatch): assert True -def test_process_cfs_refund_bad_request(): +def test_process_cfs_refund_bad_request(session): """ Assert refund is rejected, only PAID and UPDATE_REVENUE_ACCOUNT are allowed. @@ -215,7 +215,7 @@ def test_process_cfs_refund_bad_request(): assert excinfo.value.code == Error.INVALID_REQUEST.name -def test_process_cfs_refund_duplicate_refund(monkeypatch): +def test_process_cfs_refund_duplicate_refund(session, monkeypatch): """ Assert duplicate refund throws an exception. @@ -325,7 +325,7 @@ def _automated_refund_preparation(): refund_amount=3, refund_type=RefundsPartialType.SERVICE_FEES.value)]), ]) -def test_build_automated_refund_payload_validation(test_name, refund_partial): +def test_build_automated_refund_payload_validation(session, test_name, refund_partial): """Assert validations are working correctly for building refund payload.""" invoice, payment_line_item = _automated_refund_preparation() if test_name == 'pay_pli_not_exist': @@ -344,7 +344,7 @@ def test_build_automated_refund_payload_validation(test_name, refund_partial): ('paybc_amount_too_high', True), ('paybc_already_refunded', True), ]) -def test_build_automated_refund_payload_paybc_validation(test_name, has_exception): +def test_build_automated_refund_payload_paybc_validation(session, test_name, has_exception): """Assert refund payload building works correctly with various PAYBC responses.""" invoice, payment_line_item = _automated_refund_preparation() refund_partial = [ diff --git a/pay-api/tests/unit/services/test_statement.py b/pay-api/tests/unit/services/test_statement.py index 73f4acd7c..99239f168 100644 --- a/pay-api/tests/unit/services/test_statement.py +++ b/pay-api/tests/unit/services/test_statement.py @@ -20,6 +20,7 @@ import pytz from freezegun import freeze_time +from sqlalchemy import text from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Statement as StatementModel @@ -31,7 +32,8 @@ from tests.utilities.base_test import ( factory_invoice, factory_invoice_reference, factory_payment, factory_payment_line_item, factory_premium_payment_account, factory_statement, factory_statement_invoices, factory_statement_settings, - get_auth_premium_user, get_eft_enable_account_payload, get_premium_account_payload) + get_auth_premium_user, get_basic_account_payload, get_eft_enable_account_payload, get_premium_account_payload, + get_unlinked_pad_account_payload) def test_statement_find_by_account(session): @@ -263,3 +265,38 @@ def localize_date(date: datetime): """Localize date object by adding timezone information.""" pst = pytz.timezone('America/Vancouver') return pst.localize(date) + + +def test_statement_various_payment_methods_history(db, app): + """Unit test to test various payment methods over the life of a statement.""" + # We aren't using the session fixture here because we want to test the history_cls + # history_cls wont work on scoped session flush. + with app.app_context(): + db.session.commit = db.session.flush + account: PaymentAccountService = PaymentAccountService.create( + get_premium_account_payload(payment_method=PaymentMethod.DRAWDOWN.value)) + + statement_settings: StatementSettingsModel = StatementSettingsModel \ + .find_active_settings(str(account.auth_account_id), datetime.today()) + + statement = StatementModel( + statement_settings_id=statement_settings.id, + payment_account_id=account.id, + created_on=datetime.today(), + from_date=datetime(2024, 1, 2, 12, 0).date(), + to_date=datetime(2024, 1, 7, 12, 0).date() + ).flush() + + account = PaymentAccountService.update(account.auth_account_id, get_unlinked_pad_account_payload()) + # History row wont be generated for this line: + account = PaymentAccountService.update(account.auth_account_id, get_basic_account_payload()) + # Freezegun, pytest-freezegun don't work here. + db.session.execute(text("update payment_accounts_history set changed = '2024-01-02' " + f"where payment_method = '{PaymentMethod.DRAWDOWN.value}'")) + db.session.execute(text("update payment_accounts_history set changed = '2024-01-03' " + f"where payment_method = '{PaymentMethod.PAD.value}'")) + + payment_methods = statement.payment_methods + assert 'DIRECT_PAY' in payment_methods + assert 'DRAWDOWN' in payment_methods + assert 'PAD' in payment_methods diff --git a/report-api/Dockerfile b/report-api/Dockerfile index ce53064b3..9cc892b82 100644 --- a/report-api/Dockerfile +++ b/report-api/Dockerfile @@ -1,27 +1,4 @@ FROM python:3.12.2-bullseye -#FROM python:.6-stretch - -#RUN echo "deb http://ftp.debian.org/debian stretch main contrib" > /etc/apt/sources.list - -# todo: Revert this entire pull request when libcairo2 >= 1.14.2 is available from the debian -# jessie repo. This is a temporary fix for https://github.com/Kozea/WeasyPrint/issues/233 - -# reconfigure Debian to allow installs from both stretch (testing) repo and jessie (stable) repo -# install all the dependencies except libcairo2 from jessie, then install libcairo2 from stretch - -#RUN apt-get -y update \ -# && apt-get install -y \ -# fonts-font-awesome \ -# libffi-dev \ -# libgdk-pixbuf2.0-0 \ -# python-dev \ -# python-lxml \ -# shared-mime-info \ -# && apt-get install -y ttf-mscorefonts-installer \ -# libpango1.0-0 \ -# libcairo2 \ -# libpangocairo-1.0-0 \ -# && apt-get -y clean ARG VCS_REF="missing" ARG BUILD_DATE="missing" @@ -51,5 +28,4 @@ EXPOSE 5001 ENV NUM_WORKERS=3 ENV TIMEOUT=360 -#CMD ["gunicorn", "--bind", "0.0.0.0:5001", "--timeout", "$TIMEOUT", "--workers", "$NUM_WORKERS", "wsgi:app"] CMD gunicorn --bind 0.0.0.0:5001 --timeout $TIMEOUT --workers $NUM_WORKERS wsgi:application From 93d6c4e93b88477e09bac1b6148c39dd1b6590ef Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Tue, 19 Mar 2024 15:23:42 -0700 Subject: [PATCH 36/87] 19936 - EFT - PAY-API changes to handle CFS when switching payment methods (#1445) * add eft cfs_account create to pay-api * add unit test for eft payment account switch * pylint fix * lint fix * update eft cfs account creation --- pay-api/src/pay_api/services/eft_service.py | 20 +++++++++++++++++-- .../src/pay_api/services/payment_account.py | 5 ++++- pay-api/tests/unit/api/test_account.py | 15 ++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/pay-api/src/pay_api/services/eft_service.py b/pay-api/src/pay_api/services/eft_service.py index 57e8a83bb..3cf65dde7 100644 --- a/pay-api/src/pay_api/services/eft_service.py +++ b/pay-api/src/pay_api/services/eft_service.py @@ -23,7 +23,8 @@ from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import CfsAccountStatus, InvoiceReferenceStatus, PaymentMethod, PaymentStatus +from pay_api.utils.enums import CfsAccountStatus, InvoiceReferenceStatus, PaymentMethod, PaymentStatus, PaymentSystem + from .deposit_service import DepositService from .invoice import Invoice from .invoice_reference import InvoiceReference @@ -35,9 +36,13 @@ class EftService(DepositService): """Service to manage electronic fund transfers.""" def get_payment_method_code(self): - """Return EFT as the system code.""" + """Return EFT as the payment method code.""" return PaymentMethod.EFT.value + def get_payment_system_code(self): + """Return PAYBC as the system code.""" + return PaymentSystem.PAYBC.value + def create_account(self, identifier: str, contact_info: Dict[str, Any], payment_info: Dict[str, Any], **kwargs) -> CfsAccountModel: """Create an account for the EFT transactions.""" @@ -47,6 +52,17 @@ def create_account(self, identifier: str, contact_info: Dict[str, Any], payment_ cfs_account.status = CfsAccountStatus.PENDING.value return cfs_account + def update_account(self, name: str, cfs_account: CfsAccountModel, payment_info: Dict[str, Any]) -> CfsAccountModel: + """Update pad account.""" + if str(payment_info.get('bankInstitutionNumber')) != cfs_account.bank_number or \ + str(payment_info.get('bankTransitNumber')) != cfs_account.bank_branch_number or \ + str(payment_info.get('bankAccountNumber')) != cfs_account.bank_account_number: + # This means, the current cfs_account is for PAD, not EFT + # Make the current CFS Account as INACTIVE in DB + cfs_account.status = CfsAccountStatus.INACTIVE.value + cfs_account.flush() + return cfs_account + def create_invoice(self, payment_account: PaymentAccount, line_items: List[PaymentLineItem], invoice: Invoice, **kwargs) -> InvoiceReference: """Do nothing here, we create invoice references on the create CFS_INVOICES job.""" diff --git a/pay-api/src/pay_api/services/payment_account.py b/pay-api/src/pay_api/services/payment_account.py index 84fd20a8d..d052ce4c7 100644 --- a/pay-api/src/pay_api/services/payment_account.py +++ b/pay-api/src/pay_api/services/payment_account.py @@ -484,7 +484,10 @@ def _handle_payment_details(cls, account_request, is_sandbox, pay_system, paymen cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) \ if payment_account.id else None if pay_system.get_payment_system_code() == PaymentSystem.PAYBC.value: - if cfs_account is None: + if cfs_account is None or (payment_account.payment_method == PaymentMethod.EFT and cfs_account): + if payment_account.payment_method == PaymentMethod.EFT and cfs_account: + pay_system.update_account(name=payment_account.name, cfs_account=cfs_account, + payment_info=payment_info) cfs_account = pay_system.create_account( # pylint:disable=assignment-from-none identifier=payment_account.auth_account_id, contact_info=account_request.get('contactInfo'), diff --git a/pay-api/tests/unit/api/test_account.py b/pay-api/tests/unit/api/test_account.py index 4b3a7f00b..d417df26b 100755 --- a/pay-api/tests/unit/api/test_account.py +++ b/pay-api/tests/unit/api/test_account.py @@ -475,6 +475,21 @@ def test_update_pad_account_when_cfs_up(session, client, jwt, app): assert rv.status_code == 200 +def test_switch_eft_account_when_cfs_up(session, client, jwt, app, admin_users_mock): + """Assert that the payment records are created with 202.""" + token = jwt.create_jwt(get_claims(role=Role.SYSTEM.value), token_header) + headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + rv = client.post('/api/v1/accounts', + data=json.dumps(get_basic_account_payload(payment_method=PaymentMethod.PAD.value)), + headers=headers) + auth_account_id = rv.json.get('accountId') + rv = client.put(f'/api/v1/accounts/{auth_account_id}', + data=json.dumps(get_basic_account_payload(payment_method=PaymentMethod.EFT.value)), + headers=headers) + + assert rv.status_code == 200 + + def test_update_online_banking_account_when_cfs_down(session, client, jwt, app): """Assert that the payment records are created with 200, as there is no CFS update.""" token = jwt.create_jwt(get_claims(role=Role.SYSTEM.value), token_header) From 16e70f76babe2a7637fe04416d7532649bf8bd6e Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 19 Mar 2024 16:47:51 -0700 Subject: [PATCH 37/87] Add in logging --- pay-api/wsgi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pay-api/wsgi.py b/pay-api/wsgi.py index 55ac54a0e..b0f52fbb0 100755 --- a/pay-api/wsgi.py +++ b/pay-api/wsgi.py @@ -19,7 +19,9 @@ # Openshift s2i expects a lower case name of application app = create_app() # pylint: disable=invalid-name +app.logger.info('Starting migrations.') migrate = Migrate(app, db) +app.logger.info('Migrations complete.') if __name__ == "__main__": app.run() From 594743e97245bea574da4923314e566e49f4ce58 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 19 Mar 2024 16:52:39 -0700 Subject: [PATCH 38/87] move Migrate into __init__.py instead of in wsgi.py --- pay-api/src/pay_api/__init__.py | 2 ++ pay-api/wsgi.py | 12 +++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index fcf351816..068bbbe83 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -18,6 +18,7 @@ import os +from flask_migrate import Migrate import sentry_sdk # noqa: I001; pylint: disable=ungrouped-imports,wrong-import-order; conflicts with Flake8 from flask import Flask from sbc_common_components.exception_handling.exception_handler import ExceptionHandler @@ -47,6 +48,7 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): flags.init_app(app) db.init_app(app) + Migrate(app, db) ma.init_app(app) endpoints.init_app(app) diff --git a/pay-api/wsgi.py b/pay-api/wsgi.py index b0f52fbb0..d584bdf8a 100755 --- a/pay-api/wsgi.py +++ b/pay-api/wsgi.py @@ -11,17 +11,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Provides the WSGI entry point for running the application -""" -from pay_api import create_app, db +"""Provides the WSGI entry point for running the application.""" -from flask_migrate import Migrate +from pay_api import create_app -# Openshift s2i expects a lower case name of application app = create_app() # pylint: disable=invalid-name -app.logger.info('Starting migrations.') -migrate = Migrate(app, db) -app.logger.info('Migrations complete.') -if __name__ == "__main__": +if __name__ == '__main__': app.run() From d009aec05f5fef6da1a100968c61ea3cf471e284 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 19 Mar 2024 16:54:44 -0700 Subject: [PATCH 39/87] move out check for migration mode --- pay-api/src/pay_api/__init__.py | 66 ++++++++++++++++----------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index 068bbbe83..b9f37eced 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -52,40 +52,38 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): ma.init_app(app) endpoints.init_app(app) - if run_mode != 'migration': - - # Configure Sentry - if str(app.config.get('SENTRY_ENABLE')).lower() == 'true': - if app.config.get('SENTRY_DSN', None): # pragma: no cover - sentry_sdk.init( # pylint: disable=abstract-class-instantiated - dsn=app.config.get('SENTRY_DSN'), - integrations=[FlaskIntegration()] - ) - - app.after_request(convert_to_camel) - - setup_jwt_manager(app, jwt) - - ExceptionHandler(app) - - @app.after_request - def handle_after_request(response): # pylint: disable=unused-variable - add_version(response) - set_access_control_header(response) - return response - - def set_access_control_header(response): - response.headers['Access-Control-Allow-Origin'] = '*' - response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, registries-trace-id, ' \ - 'Account-Id' - - def add_version(response): # pylint: disable=unused-variable - version = get_run_version() - response.headers['API'] = f'pay_api/{version}' - return response - - register_shellcontext(app) - build_cache(app) + # Configure Sentry + if str(app.config.get('SENTRY_ENABLE')).lower() == 'true': + if app.config.get('SENTRY_DSN', None): # pragma: no cover + sentry_sdk.init( # pylint: disable=abstract-class-instantiated + dsn=app.config.get('SENTRY_DSN'), + integrations=[FlaskIntegration()] + ) + + app.after_request(convert_to_camel) + + setup_jwt_manager(app, jwt) + + ExceptionHandler(app) + + @app.after_request + def handle_after_request(response): # pylint: disable=unused-variable + add_version(response) + set_access_control_header(response) + return response + + def set_access_control_header(response): + response.headers['Access-Control-Allow-Origin'] = '*' + response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, registries-trace-id, ' \ + 'Account-Id' + + def add_version(response): # pylint: disable=unused-variable + version = get_run_version() + response.headers['API'] = f'pay_api/{version}' + return response + + register_shellcontext(app) + build_cache(app) return app From 1e7b33119944e40eeb3f18a168bcc43712bb80a9 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 19 Mar 2024 17:20:39 -0700 Subject: [PATCH 40/87] Enhance logging --- pay-api/src/pay_api/logging.conf | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/pay-api/src/pay_api/logging.conf b/pay-api/src/pay_api/logging.conf index 3d8bb0e2d..c5bc5c219 100755 --- a/pay-api/src/pay_api/logging.conf +++ b/pay-api/src/pay_api/logging.conf @@ -1,5 +1,5 @@ [loggers] -keys=root,api,tracing +keys=root,api,sqlalchemy,alembic,flask_migrate [handlers] keys=console @@ -17,11 +17,20 @@ handlers=console qualname=pay_api propagate=0 -[logger_tracing] -level=ERROR -handlers=console -qualname=jaeger_tracing -propagate=0 +[logger_sqlalchemy] +level = DEBUG +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = DEBUG +handlers = +qualname = alembic + +[logger_flask_migrate] +level = DEBUG +handlers = +qualname = flask_migrate [handler_console] class=StreamHandler From ad17608ceeac1a27d0e3651a4d40540e949b3d5d Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Tue, 19 Mar 2024 18:35:25 -0700 Subject: [PATCH 41/87] Small touch ups to get it to work in docker and hopefully gcp --- .../c67213f860ea_incorporation_fee_codes.py | 39 ++++++++++--------- pay-api/src/pay_api/__init__.py | 10 ++++- pay-api/src/pay_api/logging.conf | 18 ++------- 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py b/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py index 16b279295..6de5707af 100644 --- a/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py +++ b/pay-api/migrations/versions/c67213f860ea_incorporation_fee_codes.py @@ -111,25 +111,26 @@ def upgrade(): ] ) # Now find out the distribution code for other BCINC and map it to them. - distribution_code_id_query = "select dc.distribution_code_id from fee_schedules fs " \ - "left join distribution_code_links dcl on fs.fee_schedule_id=dcl.fee_schedule_id " \ - "left join distribution_codes dc on dc.distribution_code_id = dcl.distribution_code_id " \ - "where fs.filing_type_code = 'BCINC' and dc.start_date <= CURRENT_DATE " \ - "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" - conn = op.get_bind() - res = conn.execute(text(distribution_code_id_query)) - distribution_code_id = res.fetchall()[0][0] - new_codes = ('CCC', 'ULC', 'LTD', 'BC') - distr_code_link_values = [] - for new_code in new_codes: - res = conn.execute( - text(f"select fee_schedule_id from fee_schedules where corp_type_code='{new_code}' and filing_type_code='BCINC'")) - fee_schedule_id = res.fetchall()[0][0] - distr_code_link_values.append({ - 'distribution_code_id': distribution_code_id, - 'fee_schedule_id': fee_schedule_id - }) - op.bulk_insert(distribution_code_link_table, distr_code_link_values) + # Causing errors: + # distribution_code_id_query = "select dc.distribution_code_id from fee_schedules fs " \ + # "left join distribution_code_links dcl on fs.fee_schedule_id=dcl.fee_schedule_id " \ + # "left join distribution_codes dc on dc.distribution_code_id = dcl.distribution_code_id " \ + # "where fs.filing_type_code = 'BCINC' and dc.start_date <= CURRENT_DATE " \ + # "and (dc.end_date is null or dc.end_date > CURRENT_DATE)" + # conn = op.get_bind() + # res = conn.execute(text(distribution_code_id_query)) + # distribution_code_id = res.fetchall()[0][0] + # new_codes = ('CCC', 'ULC', 'LTD', 'BC') + # distr_code_link_values = [] + # for new_code in new_codes: + # res = conn.execute( + # text(f"select fee_schedule_id from fee_schedules where corp_type_code='{new_code}' and filing_type_code='BCINC'")) + # fee_schedule_id = res.fetchall()[0][0] + # distr_code_link_values.append({ + # 'distribution_code_id': distribution_code_id, + # 'fee_schedule_id': fee_schedule_id + # }) + # op.bulk_insert(distribution_code_link_table, distr_code_link_values) # ### end Alembic commands ### diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index b9f37eced..0432ed0bd 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -18,7 +18,7 @@ import os -from flask_migrate import Migrate +from flask_migrate import Migrate, upgrade import sentry_sdk # noqa: I001; pylint: disable=ungrouped-imports,wrong-import-order; conflicts with Flake8 from flask import Flask from sbc_common_components.exception_handling.exception_handler import ExceptionHandler @@ -49,6 +49,12 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): flags.init_app(app) db.init_app(app) Migrate(app, db) + app.logger.info('Running migration upgrade.') + with app.app_context(): + upgrade(directory='migrations', revision='head', sql=False, tag=None) + # Alembic has it's own logging config, we'll need to restore our logging here. + setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) + app.logger.info('Finished migration upgrade.') ma.init_app(app) endpoints.init_app(app) @@ -75,7 +81,7 @@ def handle_after_request(response): # pylint: disable=unused-variable def set_access_control_header(response): response.headers['Access-Control-Allow-Origin'] = '*' response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, registries-trace-id, ' \ - 'Account-Id' + 'Account-Id' def add_version(response): # pylint: disable=unused-variable version = get_run_version() diff --git a/pay-api/src/pay_api/logging.conf b/pay-api/src/pay_api/logging.conf index c5bc5c219..d2accedd5 100755 --- a/pay-api/src/pay_api/logging.conf +++ b/pay-api/src/pay_api/logging.conf @@ -1,5 +1,5 @@ [loggers] -keys=root,api,sqlalchemy,alembic,flask_migrate +keys=root,api,sqlalchemy [handlers] keys=console @@ -18,19 +18,9 @@ qualname=pay_api propagate=0 [logger_sqlalchemy] -level = DEBUG -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = DEBUG -handlers = -qualname = alembic - -[logger_flask_migrate] -level = DEBUG -handlers = -qualname = flask_migrate +level=DEBUG +handlers=console +qualname=sqlalchemy.engine [handler_console] class=StreamHandler From 0dc3993be1b7f5d85ef52036b839ebd84a025bd4 Mon Sep 17 00:00:00 2001 From: Rodrigo Barraza Date: Wed, 20 Mar 2024 09:55:44 -0700 Subject: [PATCH 42/87] 19721 - Updating EFT service for EFT invoice task (#1455) * Update EFT service * Expose EFT Service --- pay-api/src/pay_api/services/__init__.py | 1 + pay-api/src/pay_api/services/eft_service.py | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pay-api/src/pay_api/services/__init__.py b/pay-api/src/pay_api/services/__init__.py index 790e8b586..92b737ff5 100755 --- a/pay-api/src/pay_api/services/__init__.py +++ b/pay-api/src/pay_api/services/__init__.py @@ -15,6 +15,7 @@ from .cfs_service import CFSService from .distribution_code import DistributionCode +from .eft_service import EftService from .eft_gl_transfer import EFTGlTransfer from .fee_schedule import FeeSchedule from .hashing import HashingService diff --git a/pay-api/src/pay_api/services/eft_service.py b/pay-api/src/pay_api/services/eft_service.py index 3cf65dde7..774cf1f03 100644 --- a/pay-api/src/pay_api/services/eft_service.py +++ b/pay-api/src/pay_api/services/eft_service.py @@ -113,14 +113,16 @@ def create_payment(self, payment_account: PaymentAccountModel, invoice: InvoiceM return payment @staticmethod - def create_invoice_reference(invoice: InvoiceModel, payment: PaymentModel) -> InvoiceReferenceModel: + def create_invoice_reference(invoice: InvoiceModel, invoice_number: str, + reference_number: str) -> InvoiceReferenceModel: """Create an invoice reference record.""" if not (invoice_reference := InvoiceReferenceModel - .find_any_active_reference_by_invoice_number(payment.invoice_number)): + .find_any_active_reference_by_invoice_number(invoice_number)): invoice_reference = InvoiceReferenceModel() invoice_reference.invoice_id = invoice.id - invoice_reference.invoice_number = payment.invoice_number + invoice_reference.invoice_number = invoice_number + invoice_reference.reference_number = reference_number invoice_reference.status_code = InvoiceReferenceStatus.ACTIVE.value return invoice_reference From 35df8c9ec5e1a88c5e91b034b3a113deb808057b Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Wed, 20 Mar 2024 10:43:26 -0700 Subject: [PATCH 43/87] Update logging --- pay-api/src/pay_api/logging.conf | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pay-api/src/pay_api/logging.conf b/pay-api/src/pay_api/logging.conf index d2accedd5..ec7f7f03f 100755 --- a/pay-api/src/pay_api/logging.conf +++ b/pay-api/src/pay_api/logging.conf @@ -1,5 +1,5 @@ [loggers] -keys=root,api,sqlalchemy +keys=root,api [handlers] keys=console @@ -17,11 +17,6 @@ handlers=console qualname=pay_api propagate=0 -[logger_sqlalchemy] -level=DEBUG -handlers=console -qualname=sqlalchemy.engine - [handler_console] class=StreamHandler level=DEBUG From f34b20fd5d6c6710523f5935cc315f0ef733310f Mon Sep 17 00:00:00 2001 From: Rodrigo Barraza Date: Fri, 22 Mar 2024 08:43:11 -0700 Subject: [PATCH 44/87] Enum support for invoice tasks (#1457) --- pay-api/src/pay_api/utils/enums.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index 7b6e40c9a..4eedf65b3 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -357,6 +357,11 @@ class MessageType(Enum): CGI_ACK_RECEIVED = 'ACKReceived' CGI_FEEDBACK_RECEIVED = 'FEEDBACKReceived' EFT_FILE_UPLOADED = 'eftFileUploaded' + EFT_INVOICE_CREATED = 'eft.invoiceCreated' + PAD_INVOICE_CREATED = 'pad.invoiceCreated' + PAD_SETUP_FAILED = 'PadSetupFailed' + PAD_CONFIRMATION_PERIOD_OVER = 'confirmationPeriodOver' + ONLINE_BANKING_OUTSTANDING_INVOICE = 'ob.outstandingInvoice' class PaymentDetailsGlStatus(Enum): From 620e89e596fb0b67c21af5c06cabf8f9c841a5fd Mon Sep 17 00:00:00 2001 From: Rodrigo Barraza Date: Mon, 1 Apr 2024 12:58:51 -0700 Subject: [PATCH 45/87] 19721- Create EFT Invoices task (#1456) * Create EFT Invoices task * Linting * Remove comments, update commit hash * Adding enums, code feedback * timezone fix * Refactoring into smaller functions * enum support * Poetry lock update * Linting * pylint cleanup * Delete enums.py * Increased tests and final fixes * Remove print --- jobs/payment-jobs/config.py | 3 - jobs/payment-jobs/poetry.lock | 45 ++++-- jobs/payment-jobs/pyproject.toml | 2 +- .../tasks/activate_pad_account_task.py | 4 +- .../tasks/cfs_create_account_task.py | 4 +- .../tasks/cfs_create_invoice_task.py | 146 ++++++++++++++++-- .../tasks/unpaid_invoice_notify_task.py | 5 +- .../jobs/test_cfs_create_invoice_task.py | 131 +++++++++++++++- 8 files changed, 303 insertions(+), 37 deletions(-) diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index 558258fb5..cede16606 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -173,9 +173,6 @@ class _Config(object): # pylint: disable=too-few-public-methods # disbursement delay DISBURSEMENT_DELAY_IN_DAYS = int(os.getenv('DISBURSEMENT_DELAY', 5)) - # Is FAS-CFS integration disabled - DISABLE_CFS_FAS_INTEGRATION = os.getenv('DISABLE_CFS_FAS_INTEGRATION', 'false').lower() == 'true' - # CP Job variables CGI_AP_DISTRIBUTION = os.getenv('CGI_AP_DISTRIBUTION', '') CGI_AP_SUPPLIER_NUMBER = os.getenv('CGI_AP_SUPPLIER_NUMBER', '') diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock index b4942360b..ed192f29e 100644 --- a/jobs/payment-jobs/poetry.lock +++ b/jobs/payment-jobs/poetry.lock @@ -660,13 +660,13 @@ tests = ["coverage", "coveralls", "dill", "mock", "nose"] [[package]] name = "faker" -version = "24.2.0" +version = "24.3.0" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-24.2.0-py3-none-any.whl", hash = "sha256:dce4754921f9fa7e2003c26834093361b8f45072e0f46f172d6ca1234774ecd4"}, - {file = "Faker-24.2.0.tar.gz", hash = "sha256:87d5e7730426e7b36817921679c4eaf3d810cedb8c81194f47adc3df2122ca18"}, + {file = "Faker-24.3.0-py3-none-any.whl", hash = "sha256:9978025e765ba79f8bf6154c9630a9c2b7f9c9b0f175d4ad5e04b19a82a8d8d6"}, + {file = "Faker-24.3.0.tar.gz", hash = "sha256:5fb5aa9749d09971e04a41281ae3ceda9414f683d4810a694f8a8eebb8f9edec"}, ] [package.dependencies] @@ -1679,6 +1679,7 @@ semver = "3.0.2" sentry-sdk = "1.41.0" simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} six = "1.16.0" +sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} sqlalchemy = "2.0.28" sqlalchemy-utils = "0.41.1" threadloop = "1.0.2" @@ -1690,9 +1691,9 @@ werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/Jxio/sbc-pay.git" -reference = "19875" -resolved_reference = "62ceadd6e60f53cd8cf3781b5e2f6b99c968c6bf" +url = "https://github.com/bcgov/sbc-pay.git" +reference = "feature-queue-python-upgrade" +resolved_reference = "f34b20fd5d6c6710523f5935cc315f0ef733310f" subdirectory = "pay-api" [[package]] @@ -2183,13 +2184,13 @@ testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygm [[package]] name = "pytest-asyncio" -version = "0.23.5.post1" +version = "0.23.6" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, - {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, + {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, + {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, ] [package.dependencies] @@ -2219,17 +2220,17 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.12.0" +version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, - {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, ] [package.dependencies] -pytest = ">=5.0" +pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] @@ -2478,6 +2479,22 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] +[[package]] +name = "sql-versioning" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "feature-legal-name" +resolved_reference = "1d171cb28fc5044cbe40014ea89a3e8eb20a4266" +subdirectory = "python/common/sql-versioning" + [[package]] name = "sqlalchemy" version = "2.0.28" @@ -2714,4 +2731,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "3f2c4e7b54827174e4fc8d638c5b3079df2f456376983184edef0fbfe91c5e9a" +content-hash = "eaa76036fa004456010050a8138aa7b147b9f3f8f614708710e146224d71b8a4" diff --git a/jobs/payment-jobs/pyproject.toml b/jobs/payment-jobs/pyproject.toml index dab1071b0..91f347013 100644 --- a/jobs/payment-jobs/pyproject.toml +++ b/jobs/payment-jobs/pyproject.toml @@ -8,7 +8,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/Jxio/sbc-pay.git", rev = "19875", subdirectory = "pay-api"} +pay-api = {git = "https://github.com/bcgov/sbc-pay.git", branch = "feature-queue-python-upgrade", subdirectory = "pay-api"} flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} gunicorn = "^21.2.0" diff --git a/jobs/payment-jobs/tasks/activate_pad_account_task.py b/jobs/payment-jobs/tasks/activate_pad_account_task.py index 766a1dd30..d24127537 100644 --- a/jobs/payment-jobs/tasks/activate_pad_account_task.py +++ b/jobs/payment-jobs/tasks/activate_pad_account_task.py @@ -19,7 +19,7 @@ from flask import current_app from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.utils.enums import CfsAccountStatus, PaymentMethod +from pay_api.utils.enums import CfsAccountStatus, MessageType, PaymentMethod from utils import mailer @@ -57,4 +57,4 @@ def activate_pad_accounts(cls): if pay_account.payment_method != PaymentMethod.PAD.value: pay_account.payment_method = PaymentMethod.PAD.value pay_account.save() - mailer.publish_mailer_events('confirmationPeriodOver', pay_account) + mailer.publish_mailer_events(MessageType.PAD_CONFIRMATION_PERIOD_OVER, pay_account) diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index 0be3475be..4c075604c 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -22,7 +22,7 @@ from pay_api.services.cfs_service import CFSService from pay_api.services.oauth_service import OAuthService from pay_api.utils.constants import RECEIPT_METHOD_EFT_MONTHLY, RECEIPT_METHOD_PAD_DAILY -from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, PaymentMethod +from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, MessageType, PaymentMethod from sentry_sdk import capture_message from services import routing_slip from utils import mailer @@ -140,7 +140,7 @@ def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: Paym capture_message(f'User Input needed for creating CFS Account: account id={pay_account.id}, ' f'auth account : {pay_account.auth_account_id}, ERROR : Invalid Bank Details', level='error') - mailer.publish_mailer_events('PadSetupFailed', pay_account) + mailer.publish_mailer_events(MessageType.PAD_SETUP_FAILED, pay_account) pending_account.status = CfsAccountStatus.INACTIVE.value pending_account.save() return diff --git a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py index 44009a88c..bdc24ec4a 100644 --- a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Task to create CFS invoices offline.""" -from datetime import datetime +from datetime import datetime, timezone from decimal import Decimal import time from typing import List @@ -26,12 +26,13 @@ from pay_api.models import Receipt as ReceiptModel from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db +from pay_api.services import EftService from pay_api.services.cfs_service import CFSService from pay_api.services.invoice_reference import InvoiceReference from pay_api.services.payment import Payment from pay_api.services.payment_account import PaymentAccount as PaymentAccountService from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, PaymentSystem) + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, PaymentSystem) from pay_api.utils.util import generate_transaction_number from sentry_sdk import capture_message @@ -57,13 +58,15 @@ def create_invoices(cls): current_app.logger.info('<< Starting PAD Invoice Creation') cls._create_pad_invoices() current_app.logger.info('>> Done PAD Invoice Creation') + + current_app.logger.info('<< Starting EFT Invoice Creation') + cls._create_eft_invoices() + current_app.logger.info('>> Done EFT Invoice Creation') + current_app.logger.info('<< Starting Online Banking Invoice Creation') cls._create_online_banking_invoices() current_app.logger.info('>> Done Online Banking Invoice Creation') - if current_app.config.get('DISABLE_CFS_FAS_INTEGRATION'): - return - # Cancel invoice is the only non-creation of invoice in this job. current_app.logger.info('<< Starting CANCEL Routing Slip Invoices') cls._cancel_rs_invoices() @@ -239,16 +242,13 @@ def _create_pad_invoices(cls): # pylint: disable=too-many-locals current_app.logger.info(f'Found {len(pad_accounts)} with PAD transactions.') - invoice_ref_subquery = db.session.query(InvoiceReferenceModel.invoice_id). \ - filter(InvoiceReferenceModel.status_code.in_((InvoiceReferenceStatus.ACTIVE.value,))) - for account in pad_accounts: # Find all PAD invoices for this account account_invoices = db.session.query(InvoiceModel) \ .filter(InvoiceModel.payment_account_id == account.id) \ .filter(InvoiceModel.payment_method_code == PaymentMethod.PAD.value) \ .filter(InvoiceModel.invoice_status_code == InvoiceStatus.APPROVED.value) \ - .filter(InvoiceModel.id.notin_(invoice_ref_subquery)) \ + .filter(InvoiceModel.id.notin_(cls._active_invoice_reference_subquery())) \ .order_by(InvoiceModel.created_on.desc()).all() # Get cfs account @@ -320,7 +320,7 @@ def _create_pad_invoices(cls): # pylint: disable=too-many-locals 'invoice_total': float(invoice_total), 'invoice_process_date': f'{datetime.now()}' } - mailer.publish_mailer_events('pad.invoiceCreated', payment_account, additional_params) + mailer.publish_mailer_events(MessageType.PAD_INVOICE_CREATED, payment_account, additional_params) # Iterate invoice and create invoice reference records for invoice in account_invoices: invoice_reference = InvoiceReferenceModel( @@ -333,6 +333,132 @@ def _create_pad_invoices(cls): # pylint: disable=too-many-locals invoice.cfs_account_id = cfs_account.id db.session.commit() + @classmethod + def _return_eft_accounts(cls): + """Return EFT accounts.""" + invoice_subquery = db.session.query(InvoiceModel.payment_account_id) \ + .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ + .filter(InvoiceModel.invoice_status_code == InvoiceStatus.APPROVED.value).subquery() + + eft_accounts: List[PaymentAccountModel] = db.session.query(PaymentAccountModel) \ + .join(CfsAccountModel, CfsAccountModel.account_id == PaymentAccountModel.id) \ + .filter(CfsAccountModel.status != CfsAccountStatus.FREEZE.value) \ + .filter(PaymentAccountModel.id.in_(invoice_subquery)).all() + + current_app.logger.info(f'Found {len(eft_accounts)} with EFT transactions.') + + return eft_accounts + + @classmethod + def _save_invoice_reference_records(cls, account_invoices, cfs_account, invoice_response): + """Save invoice reference records.""" + for invoice in account_invoices: + + invoice_reference = EftService.create_invoice_reference( + invoice=invoice, + invoice_number=invoice_response.get('invoice_number'), + reference_number=invoice_response.get('pbc_ref_number', None) + ) + db.session.add(invoice_reference) + + invoice.cfs_account_id = cfs_account.id + db.session.commit() + + @classmethod + def _active_invoice_reference_subquery(cls): + return db.session.query(InvoiceReferenceModel.invoice_id). \ + filter(InvoiceReferenceModel.status_code.in_((InvoiceReferenceStatus.ACTIVE.value,))) + + @classmethod + def _create_eft_invoices(cls): + """Create EFT invoices in CFS.""" + eft_accounts = cls._return_eft_accounts() + + for eft_account in eft_accounts: + account_invoices = db.session.query(InvoiceModel) \ + .filter(InvoiceModel.payment_account_id == eft_account.id) \ + .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ + .filter(InvoiceModel.invoice_status_code == InvoiceStatus.APPROVED.value) \ + .filter(InvoiceModel.id.notin_(cls._active_invoice_reference_subquery())) \ + .order_by(InvoiceModel.created_on.desc()).all() + + if not account_invoices: + continue + + payment_account: PaymentAccountService = PaymentAccountService.find_by_id(eft_account.id) + + if not payment_account: + continue + + current_app.logger.debug( + f'Found {len(account_invoices)} invoices for account {payment_account.auth_account_id}') + + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) + + # If no CFS account then the payment method might have changed from EFT to DRAWDOWN + if not cfs_account: + cfs_account: CfsAccountModel = CfsAccountModel.query.\ + filter(CfsAccountModel.account_id == payment_account.id).order_by(CfsAccountModel.id.desc()).first() + + # If CFS account is not ACTIVE or INACTIVE (for above case), raise error and continue + if cfs_account.status not in (CfsAccountStatus.ACTIVE.value, CfsAccountStatus.INACTIVE.value): + current_app.logger.info(f'CFS status for account {payment_account.auth_account_id} ' + f'is {payment_account.cfs_account_status} skipping.') + continue + + # Add all payment line items together + lines = [] + invoice_total = Decimal('0') + for invoice in account_invoices: + lines.extend(invoice.payment_line_items) + invoice_total += invoice.total + + invoice_number = account_invoices[-1].id + try: + # Get the first invoice id as the trx number for CFS + invoice_response = CFSService.create_account_invoice(transaction_number=invoice_number, + line_items=lines, + cfs_account=cfs_account) + except Exception as e: # NOQA # pylint: disable=broad-except + # There is a chance that the error is a timeout from CAS side, + # so to make sure we are not missing any data, make a GET call for the invoice we tried to create + # and use it if it got created. + current_app.logger.info(e) # INFO is intentional as sentry alerted only after the following try/catch + has_invoice_created: bool = False + try: + # add a 10 seconds delay here as safe bet, as CFS takes time to create the invoice + time.sleep(10) + invoice_number = generate_transaction_number(str(invoice_number)) + invoice_response = CFSService.get_invoice( + cfs_account=cfs_account, inv_number=invoice_number + ) + has_invoice_created = invoice_response.get('invoice_number', None) == invoice_number + invoice_total_matches = Decimal(invoice_response.get('total', '0')) == invoice_total + except Exception as exc: # NOQA # pylint: disable=broad-except,unused-variable + # Ignore this error, as it is irrelevant and error on outer level is relevant. + pass + # If no invoice is created raise an error for sentry + if not has_invoice_created: + capture_message(f'Error on creating EFT invoice: account id={payment_account.id}, ' + f'auth account : {payment_account.auth_account_id}, ERROR : {str(e)}', + level='error') + current_app.logger.error(e) + continue + if not invoice_total_matches: + capture_message(f'Error on creating EFT invoice: account id={payment_account.id}, ' + f'auth account : {payment_account.auth_account_id}, Invoice exists: ' + f' CAS total: {invoice_response.get("total", 0)}, PAY-BC total: {invoice_total}', + level='error') + current_app.logger.error(e) + continue + + mailer.publish_mailer_events(MessageType.EFT_INVOICE_CREATED, payment_account, { + 'invoice_total': float(invoice_total), + 'invoice_process_date': f'{datetime.now(tz=timezone.utc)}' + }) + + cls._save_invoice_reference_records(account_invoices, cfs_account, invoice_response) + @classmethod def _create_online_banking_invoices(cls): """Create online banking invoices to CFS system.""" diff --git a/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py b/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py index 4e48a25a6..2a04f3485 100644 --- a/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py +++ b/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py @@ -19,7 +19,7 @@ from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import db -from pay_api.utils.enums import InvoiceStatus, PaymentMethod +from pay_api.utils.enums import InvoiceStatus, MessageType, PaymentMethod from sentry_sdk import capture_message from sqlalchemy import Date, and_, cast, func @@ -76,7 +76,8 @@ def _notify_for_ob(cls): # pylint: disable=too-many-locals 'cfsAccountId': cfs_account.cfs_account, 'authAccountId': pay_account.auth_account_id, } - mailer.publish_mailer_events('ob.outstandingInvoice', pay_account, addition_params_to_mailer) + mailer.publish_mailer_events(MessageType.ONLINE_BANKING_OUTSTANDING_INVOICE, pay_account, + addition_params_to_mailer) except Exception as e: # NOQA # pylint: disable=broad-except capture_message(f'Error on notifying mailer OB Pending invoice: account id={pay_account.id}, ' f'auth account : {pay_account.auth_account_id}, ERROR : {str(e)}', level='error') diff --git a/jobs/payment-jobs/tests/jobs/test_cfs_create_invoice_task.py b/jobs/payment-jobs/tests/jobs/test_cfs_create_invoice_task.py index 49c35e296..2df2a00d5 100644 --- a/jobs/payment-jobs/tests/jobs/test_cfs_create_invoice_task.py +++ b/jobs/payment-jobs/tests/jobs/test_cfs_create_invoice_task.py @@ -16,7 +16,7 @@ Test-Suite to ensure that the CreateInvoiceTask is working as expected. """ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from unittest.mock import patch from pay_api.models import DistributionCode as DistributionCodeModel @@ -32,8 +32,8 @@ from tasks.cfs_create_invoice_task import CreateInvoiceTask from .factory import ( - factory_create_online_banking_account, factory_create_pad_account, factory_invoice, factory_payment_line_item, - factory_routing_slip_account) + factory_create_eft_account, factory_create_online_banking_account, factory_create_pad_account, factory_invoice, + factory_payment_line_item, factory_routing_slip_account) def test_create_invoice(session): @@ -272,3 +272,128 @@ def test_create_online_banking_transaction(session): assert inv_ref assert updated_invoice.invoice_status_code == InvoiceStatus.SETTLEMENT_SCHEDULED.value + + +def test_create_eft_invoice(session): + """Assert EFT invoice is created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.ACTIVE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=1) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + assert invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + CreateInvoiceTask.create_invoices() + + updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel. \ + find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + assert invoice_reference + assert updated_invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + +def test_create_eft_invoice_rerun(session): + """Assert EFT invoice is created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.ACTIVE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=1) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + invoice_response = {'invoice_number': '10021', 'pbc_ref_number': '10005', 'party_number': '11111', + 'party_name': 'invoice'} + assert invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + with patch.object(CFSService, 'create_account_invoice', return_value=invoice_response) as mock_cfs: + CreateInvoiceTask.create_invoices() + mock_cfs.assert_called() + + updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + inv_ref: InvoiceReferenceModel = InvoiceReferenceModel. \ + find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + assert inv_ref + assert updated_invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + with patch.object(CFSService, 'create_account_invoice', return_value=invoice_response) as mock_cfs: + CreateInvoiceTask.create_invoices() + mock_cfs.assert_not_called() + + +def test_create_eft_invoice_on_frozen_account(session): + """Assert EFT invoice is created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.FREEZE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=1) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + + assert invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + CreateInvoiceTask.create_invoices() + + updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + inv_ref: InvoiceReferenceModel = InvoiceReferenceModel. \ + find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + assert inv_ref is None + assert updated_invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + +def test_create_eft_invoices(session): + """Assert EFT invoices are created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.ACTIVE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=1) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + + # Create another invoice for this account + invoice2 = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + fee_schedule2 = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTADD') + line2 = factory_payment_line_item(invoice2.id, fee_schedule_id=fee_schedule2.fee_schedule_id) + line2.save() + + CreateInvoiceTask.create_invoices() + invoice2 = InvoiceModel.find_by_id(invoice2.id) + invoice = InvoiceModel.find_by_id(invoice.id) + assert invoice2.invoice_status_code == invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + +def test_create_eft_invoice_before_cutoff(session): + """Assert EFT invoices are created.""" + account = factory_create_eft_account(auth_account_id='1', status=CfsAccountStatus.ACTIVE.value) + previous_day = datetime.now(tz=timezone.utc) - timedelta(days=2) + # Create an invoice for this account + invoice = factory_invoice(payment_account=account, created_on=previous_day, total=10, + status_code=InvoiceStatus.APPROVED.value, payment_method_code=PaymentMethod.EFT.value) + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + line.save() + + assert invoice.invoice_status_code == InvoiceStatus.APPROVED.value + + CreateInvoiceTask.create_invoices() + + updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) + inv_ref: InvoiceReferenceModel = InvoiceReferenceModel. \ + find_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value) + + assert inv_ref is not None # As EFT will be summed up for all outstanding invoices + assert updated_invoice.invoice_status_code == InvoiceStatus.APPROVED.value From 97bf26115a3513a480a7288005dae50f5176f605 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 4 Apr 2024 11:29:40 -0700 Subject: [PATCH 46/87] Sync from main (#1460) * Add pay api gcp CD flow. (#1446) * Bump pillow from 10.2.0 to 10.3.0 in /report-api (#1458) Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.2.0 to 10.3.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/10.2.0...10.3.0) --- updated-dependencies: - dependency-name: pillow dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add in TZ conversion (#1459) --------- Signed-off-by: dependabot[bot] Co-authored-by: Patrick Wei <44277752+pwei1018@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pay-api-cd-gcp.yml | 30 +++++++++++++++++++ .../daily/vs_reconciliation_details.ipynb | 6 ++-- .../monthly/vs_reconciliation_summary.ipynb | 24 +++++++-------- report-api/requirements.txt | 2 +- 4 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/pay-api-cd-gcp.yml diff --git a/.github/workflows/pay-api-cd-gcp.yml b/.github/workflows/pay-api-cd-gcp.yml new file mode 100644 index 000000000..a5fe4c8dc --- /dev/null +++ b/.github/workflows/pay-api-cd-gcp.yml @@ -0,0 +1,30 @@ +name: Pay API CD + +on: + push: + branches: + - feature-queue-python-upgrade + paths: + - "pay-api/**" + workflow_dispatch: + inputs: + target: + description: "Deploy To" + required: true + type: choice + options: + - dev + - test + - sandbox + - prod + +jobs: + pay-api-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-api" + working_directory: "./pay-api" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file diff --git a/jobs/notebook-report/daily/vs_reconciliation_details.ipynb b/jobs/notebook-report/daily/vs_reconciliation_details.ipynb index 0796e728e..d46002348 100644 --- a/jobs/notebook-report/daily/vs_reconciliation_details.ipynb +++ b/jobs/notebook-report/daily/vs_reconciliation_details.ipynb @@ -131,7 +131,7 @@ "outputs": [], "source": [ "%%sql\n", - "select now() AT TIME ZONE 'PST' as current_date" + "select now() AT TIME ZONE 'america/vancouver' as current_date" ] }, { @@ -172,8 +172,8 @@ "WHERE i.corp_type_code = :partner_code\n", "AND i.invoice_status_code IN ('PAID', 'REFUNDED', 'CANCELLED', 'CREDITED')\n", "AND i.payment_method_code IN ('PAD','EJV', 'DRAWDOWN')\n", - "AND date(i.created_on) > date(current_date - 1 - interval '1 days')\n", - "AND date(i.created_on) <= date(current_date - 1)\n", + "AND date(i.created_on at time zone 'utc' at time zone 'america/vancouver') > date(current_date - 1 - interval '1 days')\n", + "AND date(i.created_on at time zone 'utc' at time zone 'america/vancouver') <= date(current_date - 1)\n", "ORDER BY 1;" ] }, diff --git a/jobs/notebook-report/monthly/vs_reconciliation_summary.ipynb b/jobs/notebook-report/monthly/vs_reconciliation_summary.ipynb index a78fadc05..5ec9c112d 100644 --- a/jobs/notebook-report/monthly/vs_reconciliation_summary.ipynb +++ b/jobs/notebook-report/monthly/vs_reconciliation_summary.ipynb @@ -138,7 +138,7 @@ "outputs": [], "source": [ "%%sql\n", - "select now() AT TIME ZONE 'PST' as current_date" + "select now() AT TIME ZONE 'america/vancouver' as current_date" ] }, { @@ -161,11 +161,11 @@ "outputs": [], "source": [ "%%sql monthly_reconciliation_summary <<\n", - "SELECT count(*) AS transaction_count,\n", + "SELECT\n", "sum(pli.total) AS subtotal,\n", "sum(pli.service_fees) AS service_fees,\n", "sum(pli.total + pli.service_fees) AS total,\n", - "(i.payment_date)::date,\n", + "(i.payment_date at time zone 'utc' at time zone 'america/vancouver')::date,\n", "pli.description,\n", "i.payment_method_code,\n", "i.corp_type_code\n", @@ -174,10 +174,10 @@ "WHERE i.corp_type_code = :partner_code\n", "AND i.invoice_status_code = 'PAID'\n", "AND i.payment_method_code IN ('PAD', 'EJV', 'DIRECT_PAY', 'DRAWDOWN')\n", - "AND date(i.payment_date) > date(current_date - 1 - interval '1 months')\n", - "AND date(i.payment_date) <= date(current_date - 1)\n", - "GROUP BY (i.payment_date)::date, i.payment_method_code, i.corp_type_code, pli.description\n", - "ORDER BY (i.payment_date)::date, pli.description, i.payment_method_code;" + "AND date(i.payment_date at time zone 'utc' at time zone 'america/vancouver') > date(current_date - 1 - interval '1 months')\n", + "AND date(i.payment_date at time zone 'utc' at time zone 'america/vancouver') <= date(current_date - 1)\n", + "GROUP BY (i.payment_date at time zone 'utc' at time zone 'america/vancouver')::date, i.payment_method_code, i.corp_type_code, pli.description\n", + "ORDER BY (i.payment_date at time zone 'utc' at time zone 'america/vancouver')::date, pli.description, i.payment_method_code" ] }, { @@ -226,7 +226,7 @@ "sum(pli.total) AS sub_total,\n", "sum(pli.service_fees) AS service_fees,\n", "sum(pli.total + pli.service_fees) AS total,\n", - "(i.disbursement_date)::date,\n", + "(i.disbursement_date at time zone 'utc' at time zone 'america/vancouver')::date,\n", "pli.description,\n", "i.payment_method_code,\n", "i.corp_type_code\n", @@ -236,10 +236,10 @@ "AND i.invoice_status_code = 'PAID'\n", "AND i.payment_method_code IN ('PAD', 'EJV', 'DIRECT_PAY')\n", "AND i.disbursement_status_code = 'COMPLETED'\n", - "AND date(disbursement_date) > date(current_date - 1 - interval '1 months')\n", - "AND date(disbursement_date) <= date(current_date - 1)\n", - "GROUP BY (disbursement_date)::date, payment_method_code, corp_type_code, pli.description\n", - "ORDER BY (disbursement_date)::date, pli.description, i.payment_method_code;" + "AND date(disbursement_date at time zone 'utc' at time zone 'america/vancouver') > date(current_date - 1 - interval '1 months')\n", + "AND date(disbursement_date at time zone 'utc' at time zone 'america/vancouver') <= date(current_date - 1)\n", + "GROUP BY (disbursement_date at time zone 'utc' at time zone 'america/vancouver')::date, payment_method_code, corp_type_code, pli.description\n", + "ORDER BY (disbursement_date at time zone 'utc' at time zone 'america/vancouver')::date, pli.description, i.payment_method_code;" ] }, { diff --git a/report-api/requirements.txt b/report-api/requirements.txt index 5f08e11f1..3956cf9ad 100644 --- a/report-api/requirements.txt +++ b/report-api/requirements.txt @@ -3,7 +3,7 @@ Flask-WeasyPrint==0.6 Flask==1.1.2 Jinja2==3.0.3 MarkupSafe==2.0.1 -Pillow==10.2.0 +Pillow==10.3.0 Werkzeug==1.0.1 aniso8601==9.0.1 attrs==22.2.0 From e6ef97975fd9acdc28178cc28c84cb8bcff41696 Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Mon, 8 Apr 2024 08:28:51 -0700 Subject: [PATCH 47/87] add verification logic in some .setters to avoid unnecessary db actions (#1462) --- .../src/pay_api/services/distribution_code.py | 33 +++++++++++-------- .../src/pay_api/services/eft_short_names.py | 20 ++++++----- .../src/pay_api/services/payment_account.py | 25 ++++++++------ 3 files changed, 47 insertions(+), 31 deletions(-) diff --git a/pay-api/src/pay_api/services/distribution_code.py b/pay-api/src/pay_api/services/distribution_code.py index 0b0760ff8..88aa2cb66 100644 --- a/pay-api/src/pay_api/services/distribution_code.py +++ b/pay-api/src/pay_api/services/distribution_code.py @@ -108,8 +108,9 @@ def service_fee_distribution_code_id(self): @service_fee_distribution_code_id.setter def service_fee_distribution_code_id(self, value: int): """Set the service_fee_distribution_code_id.""" - self._service_fee_distribution_code_id = value - self._dao.service_fee_distribution_code_id = value + if self._service_fee_distribution_code_id != value: + self._service_fee_distribution_code_id = value + self._dao.service_fee_distribution_code_id = value @property def disbursement_distribution_code_id(self): @@ -119,8 +120,9 @@ def disbursement_distribution_code_id(self): @disbursement_distribution_code_id.setter def disbursement_distribution_code_id(self, value: int): """Set the disbursement_distribution_code_id.""" - self._disbursement_distribution_code_id = value - self._dao.disbursement_distribution_code_id = value + if self._disbursement_distribution_code_id != value: + self._disbursement_distribution_code_id = value + self._dao.disbursement_distribution_code_id = value @property def end_date(self): @@ -152,8 +154,9 @@ def name(self): @name.setter def name(self, value: str): """Set the name.""" - self._name = value - self._dao.name = value + if self._name != value: + self._name = value + self._dao.name = value @property def service_fee_stob(self): @@ -247,8 +250,9 @@ def project_code(self): @project_code.setter def project_code(self, value: str): """Set the project_code.""" - self._project_code = value - self._dao.project_code = value + if self._project_code != value: + self._project_code = value + self._dao.project_code = value @property def service_fee_client(self): @@ -258,7 +262,8 @@ def service_fee_client(self): @service_fee_client.setter def service_fee_client(self, value: str): """Set the service_fee_client.""" - self._service_fee_client = value + if self._service_fee_client != value: + self._service_fee_client = value @property def service_fee_project_code(self): @@ -278,8 +283,9 @@ def stop_ejv(self): @stop_ejv.setter def stop_ejv(self, value: bool): """Set the stop_ejv.""" - self._stop_ejv = value - self._dao.stop_ejv = value + if self._stop_ejv != value: + self._stop_ejv = value + self._dao.stop_ejv = value @property def account_id(self): @@ -289,8 +295,9 @@ def account_id(self): @account_id.setter def account_id(self, value: int): """Set the account id.""" - self._account_id = value - self._dao.account_id = value + if self._account_id != value: + self._account_id = value + self._dao.account_id = value def save(self): """Save the distribution code information and commit.""" diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index 9d30e3677..f7512922f 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -106,8 +106,9 @@ def auth_account_id(self): @auth_account_id.setter def auth_account_id(self, value: str): """Set the auth_account_id.""" - self._auth_account_id = value - self._dao.auth_account_id = value + if self._auth_account_id != value: + self._auth_account_id = value + self._dao.auth_account_id = value @property def short_name(self): @@ -117,8 +118,9 @@ def short_name(self): @short_name.setter def short_name(self, value: str): """Set the short name.""" - self._short_name = value - self._dao.short_name = value + if self._short_name != value: + self._short_name = value + self._dao.short_name = value @property def created_on(self): @@ -139,8 +141,9 @@ def linked_by(self): @linked_by.setter def linked_by(self, value: str): """Set the linked by user name.""" - self._linked_by = value - self._dao.linked_by = value + if self._linked_by != value: + self._linked_by = value + self._dao.linked_by = value @property def linked_by_name(self): @@ -150,8 +153,9 @@ def linked_by_name(self): @linked_by_name.setter def linked_by_name(self, value: str): """Set the linked by name.""" - self._linked_by_name = value - self._dao.linked_by_name = value + if self._linked_by_name != value: + self._linked_by_name = value + self._dao.linked_by_name = value @property def linked_on(self): diff --git a/pay-api/src/pay_api/services/payment_account.py b/pay-api/src/pay_api/services/payment_account.py index d052ce4c7..0fe12aa1e 100644 --- a/pay-api/src/pay_api/services/payment_account.py +++ b/pay-api/src/pay_api/services/payment_account.py @@ -150,8 +150,9 @@ def auth_account_id(self): @auth_account_id.setter def auth_account_id(self, value: str): """Set the auth_account_id.""" - self._auth_account_id = value - self._dao.auth_account_id = value + if self._auth_account_id != value: + self._auth_account_id = value + self._dao.auth_account_id = value @property def name(self): @@ -161,8 +162,9 @@ def name(self): @name.setter def name(self, value: str): """Set the name.""" - self._name = value - self._dao.name = value + if self._name != value: + self._name = value + self._dao.name = value @property def payment_method(self): @@ -172,8 +174,9 @@ def payment_method(self): @payment_method.setter def payment_method(self, value: int): """Set the payment_method.""" - self._payment_method = value - self._dao.payment_method = value + if self._payment_method != value: + self._payment_method = value + self._dao.payment_method = value @property def cfs_account(self): @@ -320,8 +323,9 @@ def billable(self): @billable.setter def billable(self, value: bool): """Set the billable.""" - self._billable = value - self._dao.billable = value + if self._billable != value: + self._billable = value + self._dao.billable = value @property def eft_enable(self): @@ -331,8 +335,9 @@ def eft_enable(self): @eft_enable.setter def eft_enable(self, value: bool): """Set the eft_enable.""" - self._eft_enable = value - self._dao.eft_enable = value + if self._eft_enable != value: + self._eft_enable = value + self._dao.eft_enable = value def save(self): """Save the information to the DB.""" From 727d3385439146f9616ded4548c22a4867f42ff3 Mon Sep 17 00:00:00 2001 From: Rodrigo Barraza Date: Thu, 11 Apr 2024 09:18:16 -0700 Subject: [PATCH 48/87] 19723 - Exposing eft short names service (#1466) * Include EFTShortNames in service * as EftShortNamesService * Lint fix --- pay-api/src/pay_api/services/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pay-api/src/pay_api/services/__init__.py b/pay-api/src/pay_api/services/__init__.py index 92b737ff5..3e8dc9951 100755 --- a/pay-api/src/pay_api/services/__init__.py +++ b/pay-api/src/pay_api/services/__init__.py @@ -16,6 +16,7 @@ from .cfs_service import CFSService from .distribution_code import DistributionCode from .eft_service import EftService +from .eft_short_names import EFTShortnames as EFTShortNamesService from .eft_gl_transfer import EFTGlTransfer from .fee_schedule import FeeSchedule from .hashing import HashingService From e020b2d2b4de23736e8ef616dcb4042831d09546 Mon Sep 17 00:00:00 2001 From: Rodrigo Barraza Date: Mon, 15 Apr 2024 08:45:48 -0700 Subject: [PATCH 49/87] 19723 - Linking EFT Job (#1464) * Linking EFTs * Minor cleanup * Tests * Remove print --- jobs/payment-jobs/invoke_jobs.py | 3 + jobs/payment-jobs/poetry.lock | 16 +- .../tasks/electronic_funds_transfer_task.py | 187 ++++++++++++++++++ jobs/payment-jobs/tests/jobs/factory.py | 53 ++++- .../test_electronic_funds_transfer_task.py | 71 +++++++ 5 files changed, 315 insertions(+), 15 deletions(-) create mode 100644 jobs/payment-jobs/tasks/electronic_funds_transfer_task.py create mode 100644 jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index cc89471b0..7d03dbdd3 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -26,6 +26,7 @@ from services import oracle_db from tasks.eft_transfer_task import EftTransferTask from tasks.routing_slip_task import RoutingSlipTask +from tasks.electronic_funds_transfer_task import ElectronicFundsTransferTask from tasks.statement_due_task import StatementDueTask from utils.logger import setup_logging @@ -131,6 +132,8 @@ def run(job_name, argument=None): RoutingSlipTask.process_correction() RoutingSlipTask.adjust_routing_slips() application.logger.info(f'<<<< Completed Routing Slip tasks >>>>') + elif job_name == 'EFT': + ElectronicFundsTransferTask.link_electronic_funds_transfer() elif job_name == 'EJV_PAYMENT': EjvPaymentTask.create_ejv_file() application.logger.info(f'<<<< Completed running EJV payment >>>>') diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock index ed192f29e..88cecd800 100644 --- a/jobs/payment-jobs/poetry.lock +++ b/jobs/payment-jobs/poetry.lock @@ -660,13 +660,13 @@ tests = ["coverage", "coveralls", "dill", "mock", "nose"] [[package]] name = "faker" -version = "24.3.0" +version = "24.8.0" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-24.3.0-py3-none-any.whl", hash = "sha256:9978025e765ba79f8bf6154c9630a9c2b7f9c9b0f175d4ad5e04b19a82a8d8d6"}, - {file = "Faker-24.3.0.tar.gz", hash = "sha256:5fb5aa9749d09971e04a41281ae3ceda9414f683d4810a694f8a8eebb8f9edec"}, + {file = "Faker-24.8.0-py3-none-any.whl", hash = "sha256:2f70a7817b4147d67c544192e169c5653060fce8aef758db0ea8823d89caac94"}, + {file = "Faker-24.8.0.tar.gz", hash = "sha256:1a46466b22c6bf5925448f725f90c6e0d8bf085819906520ddaa15aec58a6df5"}, ] [package.dependencies] @@ -1693,7 +1693,7 @@ werkzeug = "3.0.1" type = "git" url = "https://github.com/bcgov/sbc-pay.git" reference = "feature-queue-python-upgrade" -resolved_reference = "f34b20fd5d6c6710523f5935cc315f0ef733310f" +resolved_reference = "727d3385439146f9616ded4548c22a4867f42ff3" subdirectory = "pay-api" [[package]] @@ -1712,13 +1712,13 @@ flake8 = ">=5.0.0" [[package]] name = "pg8000" -version = "1.30.5" +version = "1.31.1" description = "PostgreSQL interface library" optional = false python-versions = ">=3.8" files = [ - {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, - {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, + {file = "pg8000-1.31.1-py3-none-any.whl", hash = "sha256:69aac9dba4114c9c8d0408232d54eaf7d06d271df7765caeed39960e057800e4"}, + {file = "pg8000-1.31.1.tar.gz", hash = "sha256:b11130d4c615dd3062ea8fed8143064a7978b7fe6d44f14b72261d43c8e27087"}, ] [package.dependencies] @@ -2492,7 +2492,7 @@ develop = false type = "git" url = "https://github.com/bcgov/lear.git" reference = "feature-legal-name" -resolved_reference = "1d171cb28fc5044cbe40014ea89a3e8eb20a4266" +resolved_reference = "2d4c389743ff307be74af1861d6debda8bc8d194" subdirectory = "python/common/sql-versioning" [[package]] diff --git a/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py b/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py new file mode 100644 index 000000000..d8137b464 --- /dev/null +++ b/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py @@ -0,0 +1,187 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Task for linking electronic funds transfers.""" + +from datetime import datetime +from typing import List + +from flask import current_app +from pay_api.models import CfsAccount as CfsAccountModel +from pay_api.models import EFTShortnames as EFTShortnameModel +from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import Invoice as InvoiceModel +from pay_api.models import InvoiceReference as InvoiceReferenceModel +from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models import Payment as PaymentModel +from pay_api.models import db +from pay_api.services.cfs_service import CFSService +from pay_api.services import EFTShortNamesService +from pay_api.services.receipt import Receipt +from pay_api.utils.enums import CfsAccountStatus, EFTShortnameState, InvoiceReferenceStatus, InvoiceStatus +from pay_api.utils.util import generate_receipt_number +from sentry_sdk import capture_message + + +class ElectronicFundsTransferTask: # pylint:disable=too-few-public-methods + """Task to link electronic funds transfers.""" + + @classmethod + def link_electronic_funds_transfers(cls): + """Create invoice in CFS. + + Steps: + 1. Find all pending EFT Short Names in a LINKED state. + 2. Receipts created in CAS representing the EFT. + 3. Apply the receipts to the invoices. + 4. Notify mailer + """ + eft_short_names = cls._get_eft_short_names_by_state(EFTShortnameState.LINKED.value) + for eft_short_name in eft_short_names: + try: + current_app.logger.debug(f'Linking Electronic Funds Transfer: {eft_short_name.id}') + payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id( + eft_short_name.auth_account_id) + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) + eft_credit: EFTCreditModel = EFTCreditModel.find_by_payment_account_id(payment_account.id) + + payment = db.session.query(PaymentModel) \ + .join(PaymentAccountModel, PaymentAccountModel.id == PaymentModel.payment_account_id) \ + .join(EFTCreditModel, EFTCreditModel.payment_account_id == PaymentAccountModel.id) \ + .join(EFTShortnameModel, EFTShortnameModel.id == EFTCreditModel.short_name_id) \ + .filter(EFTShortnameModel.id == eft_short_name.id).first() + + receipt_number = generate_receipt_number(payment.id) + CFSService.create_cfs_receipt( + cfs_account=cfs_account, + rcpt_number=receipt_number, + rcpt_date=payment.payment_date.strftime('%Y-%m-%d'), + amount=payment.invoice_amount, + payment_method=payment_account.payment_method, + access_token=CFSService.get_fas_token().json().get('access_token')) + + # apply receipt to cfs_account + total_invoice_amount = cls._apply_electronic_funds_transfers_to_pending_invoices( + eft_short_name, payment) + current_app.logger.debug(f'Total Invoice Amount : {total_invoice_amount}') + + eft_credit.remaining_amount = eft_credit.amount - total_invoice_amount + + eft_short_name.save() + + except Exception as e: # NOQA # pylint: disable=broad-except + capture_message( + f'Error on Electronics Funds Transfer ID:={eft_short_name.id}, ' + f'electronic funds transfer : {eft_short_name.id}, ERROR : {str(e)}', level='error') + current_app.logger.error(e) + continue + + @classmethod + def _get_eft_short_names_by_state(cls, state: EFTShortnameState) -> List[EFTShortnameModel]: + """Get electronic funds transfer by state.""" + query = db.session.query(EFTShortnameModel) \ + .join(PaymentAccountModel, PaymentAccountModel.auth_account_id == EFTShortnameModel.auth_account_id) \ + .join(CfsAccountModel, CfsAccountModel.account_id == PaymentAccountModel.id) \ + .filter(CfsAccountModel.status == CfsAccountStatus.ACTIVE.value) + + if state == EFTShortnameState.UNLINKED.value: + query = query.filter(EFTShortnameModel.auth_account_id.is_(None)) + if state == EFTShortnameState.LINKED.value: + query = query.filter(EFTShortnameModel.auth_account_id.isnot(None)) + + return query.all() + + @classmethod + def _apply_electronic_funds_transfers_to_pending_invoices(cls, + eft_short_name: EFTShortnameModel, + payment: PaymentModel) -> float: + """Apply the electronic funds transfers again.""" + current_app.logger.info( + f'Applying electronic funds transfer to pending invoices: {eft_short_name.id}') + + payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id( + eft_short_name.auth_account_id) + + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) + + invoices: List[InvoiceModel] = EFTShortNamesService.get_invoices_owing( + eft_short_name.auth_account_id) + + current_app.logger.info(f'Found {len(invoices)} to apply receipt') + applied_amount = 0 + for invoice in invoices: + invoice_reference: InvoiceReferenceModel = InvoiceReferenceModel.find_by_invoice_id_and_status( + invoice.id, InvoiceReferenceStatus.ACTIVE.value + ) + + # apply invoice to the CFS_ACCOUNT + cls.apply_electronic_funds_transfer_to_invoice( + payment_account, cfs_account, eft_short_name, invoice, invoice_reference.invoice_number, payment + ) + + # IF invoice balance is zero, then update records. + if CFSService.get_invoice(cfs_account=cfs_account, inv_number=invoice_reference.invoice_number) \ + .get('amount_due') == 0: + applied_amount += invoice.total + invoice_reference.status_code = InvoiceReferenceStatus.COMPLETED.value + invoice.invoice_status_code = InvoiceStatus.PAID.value + invoice.payment_date = datetime.now() + + return applied_amount + + @classmethod + def apply_electronic_funds_transfer_to_invoice(cls, # pylint: disable = too-many-arguments, too-many-locals + payment_account: PaymentAccountModel, + cfs_account: CfsAccountModel, + eft_short_name: EFTShortnameModel, + invoice: InvoiceModel, + invoice_number: str, + payment: PaymentModel) -> bool: + """Apply electronic funds transfers (receipts in CFS) to invoice.""" + has_errors = False + # an invoice has to be applied to multiple receipts (incl. all linked RS); apply till the balance is zero + try: + receipt_number = generate_receipt_number(payment.id) + current_app.logger.debug(f'Apply receipt {receipt_number} on invoice {invoice_number} ' + f'for electronic funds transfer {eft_short_name.id}') + + # If balance of receipt is zero, continue to next receipt. + receipt_balance_before_apply = float( + CFSService.get_receipt(cfs_account, receipt_number).get('unapplied_amount') + ) + current_app.logger.debug(f'Current balance on {receipt_number} = {receipt_balance_before_apply}') + if receipt_balance_before_apply == 0: + current_app.logger.debug(f'Applying receipt {receipt_number} to {invoice_number}') + receipt_response = CFSService.apply_receipt(cfs_account, receipt_number, invoice_number) + + receipt = Receipt() + receipt.receipt_number = receipt_response.json().get('receipt_number', None) + receipt_amount = receipt_balance_before_apply - float(receipt_response.json().get('unapplied_amount')) + receipt.receipt_amount = receipt_amount + receipt.invoice_id = invoice.id + receipt.receipt_date = datetime.now() + receipt.flush() + + invoice_from_cfs = CFSService.get_invoice(cfs_account, invoice_number) + if invoice_from_cfs.get('amount_due') == 0: + has_errors = True + return has_errors + + except Exception as e: # NOQA # pylint: disable=broad-except + capture_message( + f'Error on creating electronic funds transfer invoice: account id={payment_account.id}, ' + f'electronic funds transfer : {eft_short_name.id}, ERROR : {str(e)}', level='error') + current_app.logger.error(e) + has_errors = True + + return has_errors diff --git a/jobs/payment-jobs/tests/jobs/factory.py b/jobs/payment-jobs/tests/jobs/factory.py index 6bf6fab04..b916f811a 100644 --- a/jobs/payment-jobs/tests/jobs/factory.py +++ b/jobs/payment-jobs/tests/jobs/factory.py @@ -20,12 +20,12 @@ from datetime import datetime, timedelta from pay_api.models import ( - CfsAccount, DistributionCode, DistributionCodeLink, EFTShortnames, Invoice, InvoiceReference, Payment, - PaymentAccount, PaymentLineItem, Receipt, Refund, RefundsPartial, RoutingSlip, StatementRecipients, - StatementSettings) + CfsAccount, DistributionCode, DistributionCodeLink, EFTCredit, EFTFile, EFTShortnames, EFTTransaction, Invoice, + InvoiceReference, Payment, PaymentAccount, PaymentLineItem, Receipt, Refund, RefundsPartial, RoutingSlip, + StatementRecipients, StatementSettings) from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, PaymentStatus, - PaymentSystem, RoutingSlipStatus) + CfsAccountStatus, EFTProcessStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, + PaymentStatus, PaymentSystem, RoutingSlipStatus) def factory_premium_payment_account(bcol_user_id='PB25020', bcol_account_id='1234567890', auth_account_id='1234'): @@ -65,7 +65,9 @@ def factory_payment( payment_system_code: str = 'PAYBC', payment_method_code: str = 'CC', payment_status_code: str = PaymentStatus.CREATED.value, payment_date: datetime = datetime.now(), - invoice_number: str = None + invoice_number: str = None, + payment_account_id: int = None, + invoice_amount: float = None, ): """Return Factory.""" return Payment( @@ -73,7 +75,9 @@ def factory_payment( payment_method_code=payment_method_code, payment_status_code=payment_status_code, payment_date=payment_date, - invoice_number=invoice_number + invoice_number=invoice_number, + payment_account_id=payment_account_id, + invoice_amount=invoice_amount ).save() @@ -230,6 +234,41 @@ def factory_create_eft_shortname(auth_account_id: str, short_name: str): return short_name +def factory_create_eft_credit(amount=100, remaining_amount=0, eft_file_id=1, short_name_id=1, payment_account_id=1, + eft_transaction_id=1): + """Return Factory.""" + eft_credit = EFTCredit( + amount=amount, + remaining_amount=remaining_amount, + eft_file_id=eft_file_id, + short_name_id=short_name_id, + payment_account_id=payment_account_id, + eft_transaction_id=eft_transaction_id + ).save() + return eft_credit + + +def factory_create_eft_file(file_ref='test.txt', status_code=EFTProcessStatus.COMPLETED.value): + """Return Factory.""" + eft_file = EFTFile( + file_ref=file_ref, + status_code=status_code + ).save() + return eft_file + + +def factory_create_eft_transaction(file_id=1, line_number=1, line_type='T', + status_code=EFTProcessStatus.COMPLETED.value): + """Return Factory.""" + eft_transaction = EFTTransaction( + file_id=file_id, + line_number=line_number, + line_type=line_type, + status_code=status_code, + ).save() + return eft_transaction + + def factory_create_account(auth_account_id: str = '1234', payment_method_code: str = PaymentMethod.DIRECT_PAY.value, status: str = CfsAccountStatus.PENDING.value, statement_notification_enabled: bool = True): """Return payment account model.""" diff --git a/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py b/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py new file mode 100644 index 000000000..de524fdfa --- /dev/null +++ b/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py @@ -0,0 +1,71 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests to assure the CreateAccountTask. + +Test-Suite to ensure that the CreateAccountTask for electronic funds transfer is working as expected. +""" +from datetime import datetime +from unittest.mock import patch + +from pay_api.models import CfsAccount as CfsAccountModel +from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models import EFTShortnames as EFTShortnameModel +from pay_api.utils.enums import CfsAccountStatus, PaymentMethod +from pay_api.services import CFSService +from tasks.electronic_funds_transfer_task import ElectronicFundsTransferTask + + +from .factory import ( + factory_create_eft_account, factory_create_eft_credit, factory_create_eft_file, factory_create_eft_shortname, + factory_create_eft_transaction, factory_invoice, factory_invoice_reference, factory_payment) + + +def test_link_electronic_funds_transfer(session): + """Test link electronic funds transfer.""" + auth_account_id = '1234' + short_name = 'TEST1' + + payment_account = factory_create_eft_account(auth_account_id=auth_account_id, status=CfsAccountStatus.ACTIVE.value) + eft_file = factory_create_eft_file() + factory_create_eft_transaction(file_id=eft_file.id) + eft_short_name = factory_create_eft_shortname(auth_account_id=auth_account_id, short_name=short_name) + invoice = factory_invoice(payment_account=payment_account, payment_method_code=PaymentMethod.EFT.value) + factory_invoice_reference(invoice_id=invoice.id) + factory_payment(payment_account_id=payment_account.id, payment_method_code=PaymentMethod.EFT.value, + invoice_amount=351.50) + factory_create_eft_credit( + amount=100, remaining_amount=0, eft_file_id=eft_file.id, short_name_id=eft_short_name.id, + payment_account_id=payment_account.id) + + eft_short_name = EFTShortnameModel.find_by_short_name(short_name) + eft_short_name.linked_by = 'test' + eft_short_name.linked_by_name = 'test' + eft_short_name.linked_on = datetime.now() + eft_short_name.save() + + payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id( + eft_short_name.auth_account_id) + + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id( + payment_account.id) + + with patch('pay_api.services.CFSService.create_cfs_receipt') as mock_create_cfs: + with patch.object(CFSService, 'get_receipt') as mock_get_receipt: + ElectronicFundsTransferTask.link_electronic_funds_transfers() + mock_create_cfs.assert_called() + mock_get_receipt.assert_called() + + cfs_account: CfsAccountModel = CfsAccountModel.find_by_id(cfs_account.id) + assert cfs_account.status == CfsAccountStatus.ACTIVE.value From 5336ec22c30eace011b45902add928ec595d2641 Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Mon, 15 Apr 2024 13:24:00 -0700 Subject: [PATCH 50/87] 20457 - SBC-PAY Common Queue (#1467) * Pub/Sub Integration * fix unit test * unit test fix * queue name change * lint fix * lint fix * unit test fix * pay queue unit test fix --- pay-api/src/pay_api/__init__.py | 2 + pay-api/src/pay_api/config.py | 2 +- pay-api/src/pay_api/services/__init__.py | 1 + .../pay_api/services/base_payment_system.py | 4 +- .../pay_api/services/gcp_queue/__init__.py | 37 +++++++++ .../pay_api/services/gcp_queue/gcp_queue.py | 72 +++++++---------- .../src/pay_api/services/gcp_queue/logging.py | 67 ++++++++++++++++ .../pay_api/services/gcp_queue_publisher.py | 47 +++-------- .../pay_api/services/payment_transaction.py | 1 + pay-api/tests/conftest.py | 2 +- pay-api/tests/unit/services/test_gcp_queue.py | 79 +++++++++++++++++++ .../unit/services/test_payment_transaction.py | 2 +- pay-queue/poetry.lock | 37 ++++++--- pay-queue/pyproject.toml | 2 +- pay-queue/src/pay_queue/services/__init__.py | 2 +- pay-queue/tests/conftest.py | 11 +++ 16 files changed, 270 insertions(+), 98 deletions(-) create mode 100644 pay-api/src/pay_api/services/gcp_queue/__init__.py rename pay-queue/src/pay_queue/external/pubsub.py => pay-api/src/pay_api/services/gcp_queue/gcp_queue.py (77%) create mode 100644 pay-api/src/pay_api/services/gcp_queue/logging.py create mode 100644 pay-api/tests/unit/services/test_gcp_queue.py diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index 0432ed0bd..611fdcba2 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -31,6 +31,7 @@ from pay_api.resources import endpoints from pay_api.services.flags import flags from pay_api.models import db, ma +from pay_api.services.gcp_queue import queue from pay_api.utils.auth import jwt from pay_api.utils.cache import cache from pay_api.utils.logging import setup_logging @@ -48,6 +49,7 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): flags.init_app(app) db.init_app(app) + queue.init_app(app) Migrate(app, db) app.logger.info('Running migration upgrade.') with app.app_context(): diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 8f7b201b1..408484a69 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -135,7 +135,7 @@ class _Config: # pylint: disable=too-few-public-methods # GCP PubSub AUDIENCE = os.getenv('AUDIENCE', None) GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) - PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) + PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher') ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) EVENT_LISTENER_TOPIC = os.getenv('EVENT_LISTENER_TOPIC', None) NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', None) diff --git a/pay-api/src/pay_api/services/__init__.py b/pay-api/src/pay_api/services/__init__.py index 3e8dc9951..b56e840dd 100755 --- a/pay-api/src/pay_api/services/__init__.py +++ b/pay-api/src/pay_api/services/__init__.py @@ -32,3 +32,4 @@ from .statement_recipients import StatementRecipients from .statement_settings import StatementSettings from .flags import Flags +from .gcp_queue import gcp_queue diff --git a/pay-api/src/pay_api/services/base_payment_system.py b/pay-api/src/pay_api/services/base_payment_system.py index 61bcee943..c588fb095 100644 --- a/pay-api/src/pay_api/services/base_payment_system.py +++ b/pay-api/src/pay_api/services/base_payment_system.py @@ -30,13 +30,13 @@ from pay_api.models import PaymentTransaction as PaymentTransactionModel from pay_api.models import Receipt as ReceiptModel from pay_api.models.refunds_partial import RefundPartialLine -from pay_api.services.cfs_service import CFSService from pay_api.services import gcp_queue_publisher +from pay_api.services.gcp_queue_publisher import QueueMessage +from pay_api.services.cfs_service import CFSService from pay_api.services.invoice import Invoice from pay_api.services.invoice_reference import InvoiceReference from pay_api.services.payment import Payment from pay_api.services.payment_account import PaymentAccount -from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.utils.enums import ( CorpType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, QueueSources, TransactionStatus) diff --git a/pay-api/src/pay_api/services/gcp_queue/__init__.py b/pay-api/src/pay_api/services/gcp_queue/__init__.py new file mode 100644 index 000000000..aeb71bc83 --- /dev/null +++ b/pay-api/src/pay_api/services/gcp_queue/__init__.py @@ -0,0 +1,37 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the BSD 3 Clause License, (the "License"); +# you may not use this file except in compliance with the License. +# The template for the license can be found here +# https://opensource.org/license/bsd-3-clause/ +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +"""This module contains all the services used.""" +from .gcp_queue import GcpQueue + +queue = GcpQueue() diff --git a/pay-queue/src/pay_queue/external/pubsub.py b/pay-api/src/pay_api/services/gcp_queue/gcp_queue.py similarity index 77% rename from pay-queue/src/pay_queue/external/pubsub.py rename to pay-api/src/pay_api/services/gcp_queue/gcp_queue.py index c116e1afd..34c0c7ce0 100644 --- a/pay-queue/src/pay_queue/external/pubsub.py +++ b/pay-api/src/pay_api/services/gcp_queue/gcp_queue.py @@ -1,9 +1,6 @@ -# pylint: skip-file -# flake8: noqa -# This will get moved to an external library, which is linted by black (different than our rules) +# NOTE: This module is planned to be replaced with a version from LEAR's library in the future. # Copyright © 2023 Province of British Columbia -# -# Licensed under the BSD 3 Clause License, (the 'License'); +# Licensed under the BSD 3 Clause License, (the "License"); # you may not use this file except in compliance with the License. # The template for the license can be found here # https://opensource.org/license/bsd-3-clause/ @@ -48,20 +45,15 @@ from google.auth import jwt from google.cloud import pubsub_v1 from simple_cloudevent import ( - CloudEventVersionException, - InvalidCloudEventError, - SimpleCloudEvent, - from_queue_message, - to_queue_message, -) + CloudEventVersionException, InvalidCloudEventError, SimpleCloudEvent, from_queue_message, to_queue_message) from werkzeug.local import LocalProxy class GcpQueue: - """Provides Queue type services""" + """Provides Queue type services.""" def __init__(self, app: Flask = None): - """Initializes the GCP Queue class""" + """Initialize the GCP Queue class.""" self.audience = None self.credentials_pub = None self.gcp_auth_key = None @@ -73,8 +65,7 @@ def __init__(self, app: Flask = None): self.init_app(app) def init_app(self, app: Flask): - """Initializes the application""" - + """Initialize the application.""" self.gcp_auth_key = app.config.get('GCP_AUTH_KEY') if self.gcp_auth_key: try: @@ -87,42 +78,37 @@ def init_app(self, app: Flask): 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher', ) - self.service_account_info = json.loads( - base64.b64decode(self.gcp_auth_key).decode('utf-8')) - credentials = jwt.Credentials.from_service_account_info( - self.service_account_info, audience=audience) - self.credentials_pub = credentials.with_claims( - audience=publisher_audience) + self.service_account_info = json.loads(base64.b64decode(self.gcp_auth_key).decode('utf-8')) + credentials = jwt.Credentials.from_service_account_info(self.service_account_info, audience=audience) + self.credentials_pub = credentials.with_claims(audience=publisher_audience) except Exception as error: # noqa: B902 - raise Exception('Unable to create a connection', - error) from error # pylint: disable=W0719 + raise Exception('Unable to create a connection', error) from error # pylint: disable=W0719 @property def publisher(self): - """Returns the publisher""" - + """Returns the publisher.""" if not self._publisher and self.credentials_pub: - self._publisher = pubsub_v1.PublisherClient( - credentials=self.credentials_pub) + self._publisher = pubsub_v1.PublisherClient(credentials=self.credentials_pub) + else: + self._publisher = pubsub_v1.PublisherClient() return self._publisher @staticmethod def is_valid_envelope(msg: dict): - """Checks if the envelope is valid""" - + """Check if the envelope is valid.""" if ( - msg.get('subscription') - and (message := msg.get('message')) - and isinstance(message, dict) - and message.get('data') + msg.get('subscription') and + (message := msg.get('message')) and + isinstance(message, dict) and + message.get('data') ): return True + return False @staticmethod def get_envelope(request: LocalProxy) -> Optional[dict]: - """Returns the envelope""" - + """Return the envelope.""" with suppress(Exception): if (envelope := request.get_json()) and GcpQueue.is_valid_envelope(envelope): return envelope @@ -155,13 +141,13 @@ def get_simple_cloud_event(request: LocalProxy, return_raw: bool = False) -> typ return None if ( - (message := envelope.get('message')) - and (raw_data := message.get('data')) - and (str_data := base64.b64decode(raw_data)) + (message := envelope.get('message')) and + (raw_data := message.get('data')) and + (str_data := base64.b64decode(raw_data)) ): try: return from_queue_message(str_data) - except ( + except ( # pylint: disable=broad-exception-caught CloudEventVersionException, InvalidCloudEventError, ValueError, @@ -181,16 +167,14 @@ def publish(self, topic: str, payload: bytes): return future.result() except (CancelledError, TimeoutError) as error: - raise Exception('Unable to post to queue', - error) from error # pylint: disable=W0719 + raise Exception('Unable to post to queue', error) from error # pylint: disable=W0719 @staticmethod def to_queue_message(ce: SimpleCloudEvent): - """Return a byte string of the CloudEvent in JSON format""" - + """Return a byte string of the CloudEvent in JSON format.""" return to_queue_message(ce) @staticmethod def from_queue_message(data: dict): - """Convert a queue message back to a simple CloudEvent""" + """Convert a queue message back to a simple CloudEvent.""" return from_queue_message(data) diff --git a/pay-api/src/pay_api/services/gcp_queue/logging.py b/pay-api/src/pay_api/services/gcp_queue/logging.py new file mode 100644 index 000000000..938857455 --- /dev/null +++ b/pay-api/src/pay_api/services/gcp_queue/logging.py @@ -0,0 +1,67 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the BSD 3 Clause License, (the "License"); +# you may not use this file except in compliance with the License. +# The template for the license can be found here +# https://opensource.org/license/bsd-3-clause/ +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +"""Structured logging based on emitting to knative.""" +import inspect +import json +import os + +from werkzeug.local import LocalProxy + + +def structured_log(request: LocalProxy, severity: str = 'NOTICE', message: str = None): + """Print structured log message.""" + frm = inspect.stack()[1] + mod = inspect.getmodule(frm[0]) + + # Build structured log messages as an object. + global_log_fields = {} + + if project := os.environ.get('GOOGLE_CLOUD_PROJECT'): + # Add log correlation to nest all log messages. + trace_header = request.headers.get('X-Cloud-Trace-Context') + + if trace_header and project: + trace = trace_header.split('/') + global_log_fields['logging.googleapis.com/trace'] = f'projects/{project}/traces/{trace[0]}' + + # Complete a structured log entry. + entry = { + 'severity': severity, + 'message': message, + # Log viewer accesses 'component' as jsonPayload.component'. + 'component': f'{mod.__name__}.{frm.function}', + **global_log_fields, + } + + print(json.dumps(entry)) diff --git a/pay-api/src/pay_api/services/gcp_queue_publisher.py b/pay-api/src/pay_api/services/gcp_queue_publisher.py index 1d894ea24..598d1a655 100644 --- a/pay-api/src/pay_api/services/gcp_queue_publisher.py +++ b/pay-api/src/pay_api/services/gcp_queue_publisher.py @@ -1,16 +1,11 @@ """This module provides Queue type services.""" -import base64 from dataclasses import dataclass -import json import uuid -from concurrent.futures import CancelledError -from concurrent.futures import TimeoutError # pylint: disable=W0622 from datetime import datetime, timezone - from flask import current_app -from google.auth import jwt -from google.cloud import pubsub_v1 -from simple_cloudevent import SimpleCloudEvent, to_queue_message +from simple_cloudevent import SimpleCloudEvent + +from pay_api.services.gcp_queue import GcpQueue @dataclass @@ -24,12 +19,13 @@ class QueueMessage: def publish_to_queue(queue_message: QueueMessage): - """Publish to GCP PubSub Queue.""" + """Publish to GCP PubSub Queue using queue.""" if queue_message.topic is None: current_app.logger.info('Skipping queue message topic not set.') return - queue_message_bytes = to_queue_message(SimpleCloudEvent( + # Create a SimpleCloudEvent from the QueueMessage + cloud_event = SimpleCloudEvent( id=str(uuid.uuid4()), source=f'sbc-pay-{queue_message.source}', # Intentionally blank, this field has been moved to topic. @@ -37,31 +33,8 @@ def publish_to_queue(queue_message: QueueMessage): time=datetime.now(tz=timezone.utc).isoformat(), type=queue_message.message_type, data=queue_message.payload - )) - - _send_to_queue(queue_message.topic, queue_message_bytes) - - -def _send_to_queue(topic_name: str, payload: bytes): - """Send payload to the queue.""" - gcp_auth_key = current_app.config.get('GCP_AUTH_KEY') - audience = current_app.config.get('AUDIENCE') - publisher_audience = current_app.config.get('PUBLISHER_AUDIENCE') - try: - if gcp_auth_key: - service_account_info = json.loads(base64.b64decode(gcp_auth_key).decode('utf-8')) - credentials = jwt.Credentials.from_service_account_info( - service_account_info, audience=audience - ) - credentials_pub = credentials.with_claims(audience=publisher_audience) - publisher = pubsub_v1.PublisherClient(credentials=credentials_pub) - else: - publisher = pubsub_v1.PublisherClient() - except Exception as error: # noqa: B902 - raise Exception('Unable to create a connection', error) from error # pylint: disable=W0719 + ) - try: - future = publisher.publish(topic_name, payload) - return future.result() - except (CancelledError, TimeoutError) as error: - raise Exception('Unable to post to queue', error) from error # pylint: disable=W0719 + # Initialize queue and publish + gcp_queue = GcpQueue() + gcp_queue.publish(queue_message.topic, gcp_queue.to_queue_message(cloud_event)) diff --git a/pay-api/src/pay_api/services/payment_transaction.py b/pay-api/src/pay_api/services/payment_transaction.py index 7c800c6f1..d93b02079 100644 --- a/pay-api/src/pay_api/services/payment_transaction.py +++ b/pay-api/src/pay_api/services/payment_transaction.py @@ -516,6 +516,7 @@ def publish_status(transaction_dao: PaymentTransactionModel, invoice: Invoice): topic=get_topic_for_corp_type(invoice.corp_type_code) ) ) + except Exception as e: # NOQA pylint: disable=broad-except current_app.logger.error(e) current_app.logger.warning( diff --git a/pay-api/tests/conftest.py b/pay-api/tests/conftest.py index 33d553869..0e56f8990 100755 --- a/pay-api/tests/conftest.py +++ b/pay-api/tests/conftest.py @@ -37,7 +37,7 @@ def app(): @pytest.fixture(autouse=True) def mock_queue_publish(monkeypatch): """Mock queue publish.""" - # TODO: so it can be used like this from gcp_queue_publisher import publish_to_queue + # TODO: so it can be used like this from gcp_queue_publisher import publish_to_queue monkeypatch.setattr('pay_api.services.gcp_queue_publisher.publish_to_queue', lambda *args, **kwargs: None) diff --git a/pay-api/tests/unit/services/test_gcp_queue.py b/pay-api/tests/unit/services/test_gcp_queue.py new file mode 100644 index 000000000..6b293f1b5 --- /dev/null +++ b/pay-api/tests/unit/services/test_gcp_queue.py @@ -0,0 +1,79 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests to assure the GCP service layer. + +Test-Suite to ensure that the GCP Queue Service layer is working as expected. +""" +from unittest.mock import ANY, MagicMock, patch + +import pytest +from flask import Flask + +from pay_api.services.gcp_queue.gcp_queue import GcpQueue +from pay_api.services.gcp_queue_publisher import QueueMessage, publish_to_queue + + +@pytest.fixture(autouse=True) +def setup(): + """Initialize app with test env for testing.""" + global app + app = Flask(__name__) + app.env = 'testing' + + +@pytest.fixture(autouse=True) +def mock_publisher_client(): + """Mock the PublisherClient used in GcpQueue.""" + with patch('google.cloud.pubsub_v1.PublisherClient') as publisher: + yield publisher.return_value + + +@pytest.fixture(autouse=True) +def mock_credentials(): + """Mock Credentials.""" + with patch('google.auth.jwt.Credentials') as mock: + mock.from_service_account_info.return_value = MagicMock() + yield mock + + +def test_publish_to_queue_success(): + """Test publishing to GCP PubSub Queue successfully.""" + with patch.object(GcpQueue, 'publish') as mock_publisher: + with app.app_context(): + queue_message = QueueMessage( + source='test-source', + message_type='test-message-type', + payload={'key': 'value'}, + topic='projects/project-id/topics/topic' + ) + + publish_to_queue(queue_message) + mock_publisher.assert_called_once_with('projects/project-id/topics/topic', ANY) + + +def test_publish_to_queue_no_topic(): + """Test that publish_to_queue does not publish if no topic is set.""" + with patch.object(GcpQueue, 'publish') as mock_publisher: + with patch.object(Flask, 'logger') as logger: + with app.app_context(): + queue_message = QueueMessage( + source='test-source', + message_type='test-message-type', + payload={'key': 'value'}, + topic=None + ) + publish_to_queue(queue_message) + mock_publisher.publish.assert_not_called() + logger.info.assert_called_once_with('Skipping queue message topic not set.') diff --git a/pay-api/tests/unit/services/test_payment_transaction.py b/pay-api/tests/unit/services/test_payment_transaction.py index 8abc3b3d5..52e9c3f22 100644 --- a/pay-api/tests/unit/services/test_payment_transaction.py +++ b/pay-api/tests/unit/services/test_payment_transaction.py @@ -482,7 +482,7 @@ def get_receipt(cls, payment_account, pay_response_url: str, monkeypatch.setattr('pay_api.services.direct_pay_service.DirectPayService.get_receipt', get_receipt) - with patch('pay_api.services.payment_transaction.gcp_queue_publisher.publish_to_queue', + with patch('pay_api.services.gcp_queue_publisher.publish_to_queue', side_effect=ConnectionError('mocked error')): transaction = PaymentTransactionService.update_transaction(transaction.id, pay_response_url='?key=value') diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index 8bc2c6d99..c9e095cb8 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -1637,6 +1637,7 @@ semver = "3.0.2" sentry-sdk = "1.41.0" simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} six = "1.16.0" +sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} sqlalchemy = "2.0.28" sqlalchemy-utils = "0.41.1" threadloop = "1.0.2" @@ -1649,8 +1650,8 @@ werkzeug = "3.0.1" [package.source] type = "git" url = "https://github.com/Jxio/sbc-pay.git" -reference = "19875" -resolved_reference = "62ceadd6e60f53cd8cf3781b5e2f6b99c968c6bf" +reference = "20457" +resolved_reference = "da7c2f0d798ad46e8bcd6f010ef1d0a7a66d8be0" subdirectory = "pay-api" [[package]] @@ -1669,13 +1670,13 @@ flake8 = ">=5.0.0" [[package]] name = "pg8000" -version = "1.30.5" +version = "1.31.1" description = "PostgreSQL interface library" optional = false python-versions = ">=3.8" files = [ - {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, - {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, + {file = "pg8000-1.31.1-py3-none-any.whl", hash = "sha256:69aac9dba4114c9c8d0408232d54eaf7d06d271df7765caeed39960e057800e4"}, + {file = "pg8000-1.31.1.tar.gz", hash = "sha256:b11130d4c615dd3062ea8fed8143064a7978b7fe6d44f14b72261d43c8e27087"}, ] [package.dependencies] @@ -2149,17 +2150,17 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.12.0" +version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, - {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, ] [package.dependencies] -pytest = ">=5.0" +pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] @@ -2411,6 +2412,22 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] +[[package]] +name = "sql-versioning" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "feature-legal-name" +resolved_reference = "8ccb5e1bfdda45cca72ec65f38394ff064d256b1" +subdirectory = "python/common/sql-versioning" + [[package]] name = "sqlalchemy" version = "2.0.28" @@ -2647,4 +2664,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "b74265a904a8f0ea46d5e829d3643326ea5f204a805c8486c1782bf97d643c6f" +content-hash = "f09623e21148a2625eb0fda3d9ae9a8e645d7fa142f649894b625363a9e0b3d4" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml index 0ad74480b..abc9acb73 100644 --- a/pay-queue/pyproject.toml +++ b/pay-queue/pyproject.toml @@ -22,7 +22,7 @@ protobuf = "4.25.3" launchdarkly-server-sdk = "^9.2.2" cachecontrol = "^0.14.0" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/Jxio/sbc-pay.git", rev = "19875", subdirectory = "pay-api"} +pay-api = {git = "https://github.com/Jxio/sbc-pay.git", rev = "20457", subdirectory = "pay-api"} flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} pg8000 = "^1.30.5" diff --git a/pay-queue/src/pay_queue/services/__init__.py b/pay-queue/src/pay_queue/services/__init__.py index 63a1cf400..c86353543 100644 --- a/pay-queue/src/pay_queue/services/__init__.py +++ b/pay-queue/src/pay_queue/services/__init__.py @@ -33,7 +33,7 @@ # POSSIBILITY OF SUCH DAMAGE. """This module provides Queue type services.""" -from pay_queue.external.pubsub import GcpQueue +from pay_api.services.gcp_queue import GcpQueue from .identifier_updater import update_temporary_identifier diff --git a/pay-queue/tests/conftest.py b/pay-queue/tests/conftest.py index 66508ee24..c691641c5 100644 --- a/pay-queue/tests/conftest.py +++ b/pay-queue/tests/conftest.py @@ -19,6 +19,7 @@ from google.api_core.exceptions import NotFound from google.cloud import pubsub from pay_api import db as _db +from pay_api.services.gcp_queue import GcpQueue from sqlalchemy import event, text from sqlalchemy_utils import create_database, database_exists, drop_database @@ -29,9 +30,19 @@ def app(): """Return a session-wide application configured in TEST mode.""" _app = create_app('testing') + _app.config['GCP_AUTH_KEY'] = 'xxxxx' + _app.config['AUDIENCE'] = 'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber' + _app.config['PUBLISHER_AUDIENCE'] = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher' return _app +@pytest.fixture(scope='function', autouse=True) +def gcp_queue(app, mocker): + """Mock GcpQueue to avoid initializing the external connections.""" + mocker.patch.object(GcpQueue, 'init_app') + return GcpQueue(app) + + @pytest.fixture(scope='session', autouse=True) def db(app): # pylint: disable=redefined-outer-name, invalid-name """Return a session-wide initialised database.""" From ba20cecf7e65065fa22dbfedb0f0bb5c1ee7ec94 Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Fri, 19 Apr 2024 10:28:32 -0700 Subject: [PATCH 51/87] 19496 refactor eft shortnames to support multiple account linking and states (#1488) --- ...67cf1bd9e_19496_eft_short_names_updates.py | 123 +++++++ pay-api/src/pay_api/models/__init__.py | 1 + pay-api/src/pay_api/models/eft_credit.py | 8 - .../pay_api/models/eft_short_name_links.py | 103 ++++++ pay-api/src/pay_api/models/eft_short_names.py | 28 +- .../pay_api/resources/v1/eft_short_names.py | 70 ++-- .../src/pay_api/services/eft_short_names.py | 330 ++++++++---------- pay-api/src/pay_api/utils/enums.py | 6 +- pay-api/src/pay_api/version.py | 2 +- pay-api/tests/unit/api/test_cors_preflight.py | 7 +- .../tests/unit/api/test_eft_short_names.py | 164 ++++++--- .../tests/unit/api/test_eft_transactions.py | 10 +- .../models/test_eft_credit_invoice_link.py | 3 +- pay-api/tests/unit/models/test_eft_credits.py | 3 +- .../tests/unit/models/test_eft_gl_transfer.py | 3 +- .../unit/models/test_eft_short_name_links.py | 73 ++++ .../tests/unit/models/test_eft_short_names.py | 20 +- pay-api/tests/utilities/base_test.py | 27 +- 18 files changed, 651 insertions(+), 330 deletions(-) create mode 100644 pay-api/migrations/versions/2024_04_12_29867cf1bd9e_19496_eft_short_names_updates.py create mode 100644 pay-api/src/pay_api/models/eft_short_name_links.py create mode 100644 pay-api/tests/unit/models/test_eft_short_name_links.py diff --git a/pay-api/migrations/versions/2024_04_12_29867cf1bd9e_19496_eft_short_names_updates.py b/pay-api/migrations/versions/2024_04_12_29867cf1bd9e_19496_eft_short_names_updates.py new file mode 100644 index 000000000..bba99e3d7 --- /dev/null +++ b/pay-api/migrations/versions/2024_04_12_29867cf1bd9e_19496_eft_short_names_updates.py @@ -0,0 +1,123 @@ +"""19496-eft-shortname-updates + +Revision ID: 29867cf1bd9e +Revises: 52ed2340a43c +Create Date: 2024-04-12 10:21:53.863572 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy import text +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '29867cf1bd9e' +down_revision = '52ed2340a43c' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + + # Table to support multiple accounts linked to a short name with different statuses + op.create_table('eft_short_name_links', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('eft_short_name_id', sa.Integer(), nullable=False), + sa.Column('auth_account_id', sa.String(length=50), nullable=False), + sa.Column('created_on', sa.DateTime(), nullable=False), + sa.Column('status_code', sa.String(length=25), nullable=False), + sa.Column('updated_by', sa.String(length=100), nullable=True), + sa.Column('updated_by_name', sa.String(length=100), nullable=True), + sa.Column('updated_on', sa.DateTime(), nullable=True), + sa.Column('version', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['eft_short_name_id'], ['eft_short_names.id'], ), + sa.PrimaryKeyConstraint('id'), + sqlite_autoincrement=True + ) + with op.batch_alter_table('eft_short_name_links', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_eft_short_name_links_auth_account_id'), ['auth_account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_name_links_eft_short_name_id'), ['eft_short_name_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_name_links_status_code'), ['status_code'], unique=False) + + op.create_table('eft_short_name_links_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('eft_short_name_id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('auth_account_id', sa.String(length=50), autoincrement=False, nullable=False), + sa.Column('created_on', sa.DateTime(), autoincrement=False, nullable=False), + sa.Column('status_code', sa.String(length=25), autoincrement=False, nullable=False), + sa.Column('updated_by', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_by_name', sa.String(length=100), autoincrement=False, nullable=True), + sa.Column('updated_on', sa.DateTime(), autoincrement=False, nullable=True), + sa.Column('version', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('changed', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['eft_short_name_id'], ['eft_short_names.id'], ), + sa.PrimaryKeyConstraint('id', 'version'), + sqlite_autoincrement=True + ) + + with op.batch_alter_table('eft_short_name_links_history', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_eft_short_name_links_history_auth_account_id'), ['auth_account_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_name_links_history_eft_short_name_id'), ['eft_short_name_id'], unique=False) + batch_op.create_index(batch_op.f('ix_eft_short_name_links_history_status_code'), ['status_code'], unique=False) + + # Migrate linking information from existing short name table + op.execute(text(f"INSERT INTO eft_short_name_links (eft_short_name_id, auth_account_id, created_on, status_code, " + f"updated_by, updated_by_name, updated_on, version) " + f"select id, auth_account_id, created_on, 'LINKED', linked_by, linked_by_name, linked_on, 1 " + f"from eft_short_names where auth_account_id is not null")) + + with op.batch_alter_table('eft_short_names', schema=None) as batch_op: + batch_op.drop_index('ix_eft_short_names_auth_account_id') + batch_op.drop_column('auth_account_id') + batch_op.drop_column('linked_by_name') + batch_op.drop_column('linked_by') + batch_op.drop_column('linked_on') + + with op.batch_alter_table('eft_short_names_history', schema=None) as batch_op: + batch_op.drop_index('ix_eft_short_names_history_auth_account_id') + batch_op.drop_column('auth_account_id') + batch_op.drop_column('linked_by_name') + batch_op.drop_column('linked_by') + batch_op.drop_column('linked_on') + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('eft_short_names_history', schema=None) as batch_op: + batch_op.add_column(sa.Column('linked_on', postgresql.TIMESTAMP(), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('linked_by', sa.VARCHAR(length=100), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('linked_by_name', sa.VARCHAR(length=100), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('auth_account_id', sa.VARCHAR(length=50), autoincrement=False, nullable=True)) + batch_op.create_index('ix_eft_short_names_history_auth_account_id', ['auth_account_id'], unique=False) + + with op.batch_alter_table('eft_short_names', schema=None) as batch_op: + batch_op.add_column(sa.Column('linked_on', postgresql.TIMESTAMP(), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('linked_by', sa.VARCHAR(length=100), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('linked_by_name', sa.VARCHAR(length=100), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('auth_account_id', sa.VARCHAR(length=50), autoincrement=False, nullable=True)) + batch_op.create_index('ix_eft_short_names_auth_account_id', ['auth_account_id'], unique=False) + + # Restore data from eft short name link table + op.execute(text(f"UPDATE eft_short_names esn " + f"SET auth_account_id = (select auth_account_id from eft_short_name_links esnl where esnl.eft_short_name_id = esn.id)," + f"linked_on = (select esnl.updated_on from eft_short_name_links esnl where esnl.eft_short_name_id = esn.id)," + f"linked_by = (select esnl.updated_by from eft_short_name_links esnl where esnl.eft_short_name_id = esn.id)," + f"linked_by_name = (select esnl.updated_by_name from eft_short_name_links esnl where esnl.eft_short_name_id = esn.id)")) + + with op.batch_alter_table('eft_short_name_links_history', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_history_status_code')) + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_history_eft_short_name_id')) + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_history_auth_account_id')) + + op.drop_table('eft_short_name_links_history') + with op.batch_alter_table('eft_short_name_links', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_status_code')) + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_eft_short_name_id')) + batch_op.drop_index(batch_op.f('ix_eft_short_name_links_auth_account_id')) + + op.drop_table('eft_short_name_links') + + # ### end Alembic commands ### diff --git a/pay-api/src/pay_api/models/__init__.py b/pay-api/src/pay_api/models/__init__.py index 9e119110f..4d2e1d861 100755 --- a/pay-api/src/pay_api/models/__init__.py +++ b/pay-api/src/pay_api/models/__init__.py @@ -33,6 +33,7 @@ from .eft_gl_transfers import EFTGLTransfer from .eft_process_status_code import EFTProcessStatusCode from .eft_short_names import EFTShortnames, EFTShortnameSchema +from .eft_short_name_links import EFTShortnameLinks, EFTShortnameLinkSchema from .eft_transaction import EFTTransaction, EFTTransactionSchema from .ejv_file import EjvFile from .ejv_header import EjvHeader diff --git a/pay-api/src/pay_api/models/eft_credit.py b/pay-api/src/pay_api/models/eft_credit.py index e16aaab8c..5a8927d12 100644 --- a/pay-api/src/pay_api/models/eft_credit.py +++ b/pay-api/src/pay_api/models/eft_credit.py @@ -61,11 +61,3 @@ class EFTCredit(BaseModel): # pylint:disable=too-many-instance-attributes def find_by_payment_account_id(cls, payment_account_id: int): """Find EFT Credit by payment account id.""" return cls.query.filter_by(payment_account_id=payment_account_id).all() - - @classmethod - def update_account_by_short_name_id(cls, short_name_id: int, payment_account_id: int): - """Update all payment account ids for short name.""" - db.session.query(EFTCredit) \ - .filter(EFTCredit.short_name_id == short_name_id) \ - .update({EFTCredit.payment_account_id: payment_account_id}, synchronize_session='fetch') - db.session.commit() diff --git a/pay-api/src/pay_api/models/eft_short_name_links.py b/pay-api/src/pay_api/models/eft_short_name_links.py new file mode 100644 index 000000000..5d2cb17db --- /dev/null +++ b/pay-api/src/pay_api/models/eft_short_name_links.py @@ -0,0 +1,103 @@ +# Copyright © 2024 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Model to handle EFT short name to BCROS account mapping links.""" +from datetime import datetime +from _decimal import Decimal +from attrs import define + + +from sql_versioning import Versioned +from sqlalchemy import ForeignKey + +from .base_model import BaseModel +from .db import db +from ..utils.util import cents_to_decimal + + +class EFTShortnameLinks(Versioned, BaseModel): # pylint: disable=too-many-instance-attributes + """This class manages the EFT short name links to auth account mapping.""" + + __tablename__ = 'eft_short_name_links' + # this mapper is used so that new and old versions of the service can be run simultaneously, + # making rolling upgrades easier + # This is used by SQLAlchemy to explicitly define which fields we're interested + # so it doesn't freak out and say it can't map the structure if other fields are present. + # This could occur from a failed deploy or during an upgrade. + # The other option is to tell SQLAlchemy to ignore differences, but that is ambiguous + # and can interfere with Alembic upgrades. + # + # NOTE: please keep mapper names in alpha-order, easier to track that way + # Exception, id is always first, _fields first + __mapper_args__ = { + 'include_properties': [ + 'id', + 'auth_account_id', + 'created_on', + 'eft_short_name_id', + 'status_code', + 'updated_by', + 'updated_by_name', + 'updated_on' + ] + } + + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + eft_short_name_id = db.Column(db.Integer, ForeignKey('eft_short_names.id'), nullable=False, index=True) + auth_account_id = db.Column('auth_account_id', db.String(50), nullable=False, index=True) + created_on = db.Column('created_on', db.DateTime, nullable=False, default=datetime.now) + status_code = db.Column('status_code', db.String(25), nullable=False, index=True) + updated_by = db.Column('updated_by', db.String(100), nullable=True) + updated_by_name = db.Column('updated_by_name', db.String(100), nullable=True) + updated_on = db.Column('updated_on', db.DateTime, nullable=True) + + @classmethod + def find_by_short_name_id(cls, short_name_id: id): + """Find by eft short name.""" + return cls.query.filter_by(eft_short_name_id=short_name_id).all() + + +@define +class EFTShortnameLinkSchema: # pylint: disable=too-few-public-methods + """Main schema used to serialize the EFT Short name link.""" + + id: int + short_name_id: str + status_code: str + account_id: str + account_name: str + account_branch: str + latest_statement_id: str + amount_owing: Decimal + updated_by: str + updated_by_name: str + updated_on: datetime + + @classmethod + def from_row(cls, row: EFTShortnameLinks): + """From row is used so we don't tightly couple to our database class. + + https://www.attrs.org/en/stable/init.html + """ + return cls(id=row.id, + short_name_id=row.eft_short_name_id, + status_code=row.status_code, + account_id=row.auth_account_id, + account_name=getattr(row, 'account_name', None), + account_branch=getattr(row, 'account_branch', None), + latest_statement_id=getattr(row, 'statement_id', None), + amount_owing=cents_to_decimal(getattr(row, 'amount_owing', None)), + updated_by=row.updated_by, + updated_by_name=row.updated_by_name, + updated_on=row.updated_on + ) diff --git a/pay-api/src/pay_api/models/eft_short_names.py b/pay-api/src/pay_api/models/eft_short_names.py index bec08a98c..027aad346 100644 --- a/pay-api/src/pay_api/models/eft_short_names.py +++ b/pay-api/src/pay_api/models/eft_short_names.py @@ -25,7 +25,7 @@ class EFTShortnames(Versioned, BaseModel): # pylint: disable=too-many-instance-attributes - """This class manages the EFT short name to auth account mapping.""" + """This class manages the EFT short names.""" __tablename__ = 'eft_short_names' # this mapper is used so that new and old versions of the service can be run simultaneously, @@ -41,22 +41,14 @@ class EFTShortnames(Versioned, BaseModel): # pylint: disable=too-many-instance- __mapper_args__ = { 'include_properties': [ 'id', - 'auth_account_id', 'created_on', - 'short_name', - 'linked_by', - 'linked_by_name', - 'linked_on' + 'short_name' ] } id = db.Column(db.Integer, primary_key=True, autoincrement=True) - auth_account_id = db.Column('auth_account_id', db.String(50), nullable=True, index=True) created_on = db.Column('created_on', db.DateTime, nullable=False, default=datetime.now) short_name = db.Column('short_name', db.String, nullable=False, index=True) - linked_by = db.Column('linked_by', db.String(100), nullable=True) - linked_by_name = db.Column('linked_by_name', db.String(100), nullable=True) - linked_on = db.Column('linked_on', db.DateTime, nullable=True) @classmethod def find_by_short_name(cls, short_name: str): @@ -70,6 +62,7 @@ class EFTShortnameSchema: # pylint: disable=too-few-public-methods id: int short_name: str + status_code: str account_id: str account_name: str account_branch: str @@ -78,9 +71,9 @@ class EFTShortnameSchema: # pylint: disable=too-few-public-methods transaction_date: datetime deposit_date: datetime deposit_amount: Decimal - linked_by: str - linked_by_name: str - linked_on: datetime + updated_by: str + updated_by_name: str + updated_on: datetime @classmethod def from_row(cls, row: EFTShortnames): @@ -90,7 +83,8 @@ def from_row(cls, row: EFTShortnames): """ return cls(id=row.id, short_name=row.short_name, - account_id=row.auth_account_id, + status_code=getattr(row, 'status_code', None), + account_id=getattr(row, 'auth_account_id', None), account_name=getattr(row, 'account_name', None), account_branch=getattr(row, 'account_branch', None), created_on=row.created_on, @@ -98,7 +92,7 @@ def from_row(cls, row: EFTShortnames): transaction_date=getattr(row, 'transaction_date', None), deposit_date=getattr(row, 'deposit_date', None), deposit_amount=cents_to_decimal(getattr(row, 'deposit_amount', None)), - linked_by=row.linked_by, - linked_by_name=row.linked_by_name, - linked_on=row.linked_on + updated_by=getattr(row, 'updated_by'), + updated_by_name=getattr(row, 'updated_by_name'), + updated_on=getattr(row, 'updated_on') ) diff --git a/pay-api/src/pay_api/resources/v1/eft_short_names.py b/pay-api/src/pay_api/resources/v1/eft_short_names.py index 2209d6f3a..5e2fbc9f3 100644 --- a/pay-api/src/pay_api/resources/v1/eft_short_names.py +++ b/pay-api/src/pay_api/resources/v1/eft_short_names.py @@ -40,7 +40,7 @@ def get_eft_shortnames(): """Get all eft short name records.""" current_app.logger.info('get_eft_shortnames') + return jsonify(response), status @bp.route('/', methods=['GET', 'OPTIONS']) -@cross_origin(origins='*', methods=['GET', 'PATCH']) +@cross_origin(origins='*', methods=['GET']) @_jwt.requires_auth @_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) def get_eft_shortname(short_name_id: int): @@ -90,41 +90,61 @@ def get_eft_shortname(short_name_id: int): return jsonify(response), status -@bp.route('/', methods=['PATCH']) -@cross_origin(origins='*') +@bp.route('//transactions', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_jwt.requires_auth @_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) -def patch_eft_shortname(short_name_id: int): - """Update EFT short name mapping.""" - current_app.logger.info('get_eft_shortname_transactions') + return jsonify(response), status + + +@bp.route('//links', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) +def get_eft_shortname_links(short_name_id: int): + """Get EFT short name account links.""" + current_app.logger.info('patch_eft_shortname') + current_app.logger.debug('>get_eft_shortname_links') return jsonify(response), status -@bp.route('//transactions', methods=['GET', 'OPTIONS']) -@cross_origin(origins='*', methods=['GET']) -@_jwt.requires_auth +@bp.route('//links', methods=['POST']) +@cross_origin(origins='*') @_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) -def get_eft_shortname_transactions(short_name_id: int): - """Get EFT short name transactions.""" - current_app.logger.info('get_eft_shortname_transactions') + current_app.logger.debug('>post_eft_shortname_link') return jsonify(response), status diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index f7512922f..6644ab260 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -17,23 +17,25 @@ from dataclasses import dataclass from datetime import date, datetime from operator import and_ -from typing import Any, Dict, List, Optional +from typing import List, Optional from _decimal import Decimal from flask import current_app from sqlalchemy import case, func +from sqlalchemy.sql.expression import exists from pay_api.exceptions import BusinessException from pay_api.factory.payment_system_factory import PaymentSystemFactory -from pay_api.models import EFTCredit as EFTCreditModel from pay_api.models import EFTShortnames as EFTShortnameModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel +from pay_api.models import EFTShortnameLinkSchema from pay_api.models import EFTShortnameSchema from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import db from pay_api.utils.converter import Converter -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameState, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod from pay_api.utils.errors import Error from pay_api.utils.user_context import user_context @@ -53,7 +55,7 @@ class EFTShortnamesSearch: # pylint: disable=too-many-instance-attributes deposit_end_date: Optional[date] = None deposit_amount: Optional[Decimal] = None short_name: Optional[str] = None - state: Optional[str] = None + state: Optional[List[str]] = None page: Optional[int] = 1 limit: Optional[int] = 10 @@ -61,172 +63,55 @@ class EFTShortnamesSearch: # pylint: disable=too-many-instance-attributes class EFTShortnames: # pylint: disable=too-many-instance-attributes """Service to manage EFT short name model operations.""" - def __init__(self): - """Initialize service.""" - self.__dao = None - self._id: Optional[int] = None - self._auth_account_id: Optional[str] = None - self._short_name: Optional[str] = None - self._linked_by: Optional[str] = None - self._linked_by_name: Optional[str] = None - self._linked_on: Optional[datetime] = None - - @property - def _dao(self): - if not self.__dao: - self.__dao = EFTShortnameModel() - return self.__dao - - @_dao.setter - def _dao(self, value: EFTShortnameModel): - self.__dao = value - self.id: int = self._dao.id - self.auth_account_id: str = self._dao.auth_account_id - self.short_name: str = self._dao.short_name - self.linked_by: str = self._dao.linked_by - self.linked_by_name: str = self._dao.linked_by_name - self.linked_on: datetime = self._dao.linked_on - - @property - def id(self): - """Return the _id.""" - return self._id - - @id.setter - def id(self, value: int): - """Set the id.""" - self._id = value - self._dao.id = value - - @property - def auth_account_id(self): - """Return the auth_account_id.""" - return self._auth_account_id - - @auth_account_id.setter - def auth_account_id(self, value: str): - """Set the auth_account_id.""" - if self._auth_account_id != value: - self._auth_account_id = value - self._dao.auth_account_id = value - - @property - def short_name(self): - """Return the short name.""" - return self._short_name - - @short_name.setter - def short_name(self, value: str): - """Set the short name.""" - if self._short_name != value: - self._short_name = value - self._dao.short_name = value - - @property - def created_on(self): - """Return the created_on date.""" - return self._created_on - - @created_on.setter - def created_on(self, value: datetime): - """Set the created on date.""" - self._created_on = value - self._dao.created_on = value - - @property - def linked_by(self): - """Return the linked by user name.""" - return self._linked_by - - @linked_by.setter - def linked_by(self, value: str): - """Set the linked by user name.""" - if self._linked_by != value: - self._linked_by = value - self._dao.linked_by = value - - @property - def linked_by_name(self): - """Return the linked by name.""" - return self._linked_by - - @linked_by_name.setter - def linked_by_name(self, value: str): - """Set the linked by name.""" - if self._linked_by_name != value: - self._linked_by_name = value - self._dao.linked_by_name = value - - @property - def linked_on(self): - """Return the linked on date.""" - return self._linked_on - - @linked_on.setter - def linked_on(self, value: str): - """Set the linked on date.""" - self._linked_on = value - self._dao.linked_on = value - - def save(self): - """Save the information to the DB.""" - return self._dao.save() - - def flush(self): - """Flush the information to the DB.""" - return self._dao.flush() - - @classmethod - def _save(cls, short_name_request: Dict[str, any], short_name: EFTShortnameModel): - """Update and save eft short name model.""" - short_name.short_name = short_name_request.get('shortName') - short_name.auth_account_id = short_name_request.get('accountId', None) - short_name.flush() - short_name.save() - - @classmethod - def update(cls, short_name_id: str, short_name_request: Dict[str, Any]) -> EFTShortnames: - """Create or update payment account record.""" - current_app.logger.debug('update short name mapping') - return cls.find_by_short_name_id(short_name.id) - @classmethod @user_context - def patch(cls, short_name_id: int, auth_account_id: str, **kwargs) -> EFTShortnames: - """Patch eft short name auth account mapping.""" - current_app.logger.debug(' EFTShortnameLinksModel: + """Create EFT short name auth account link.""" + current_app.logger.debug('create_shortname_link') + return cls.find_link_by_id(eft_short_name_link.id) - # Process any invoices owing for short name mapping - cls.process_owing_invoices(short_name_id) + @classmethod + def get_shortname_links(cls, short_name_id: int) -> List[EFTShortnameLinksModel]: + """Get EFT short name account links.""" + current_app.logger.debug('patch short name mapping') - return cls.find_by_short_name_id(short_name.id) + current_app.logger.debug('>get_shortname_links') + return { + 'items': link_list + } @staticmethod def process_owing_invoices(short_name_id: int) -> EFTShortnames: @@ -260,7 +145,7 @@ def get_invoices_owing(auth_account_id: str) -> [InvoiceModel]: @classmethod def find_by_short_name_id(cls, short_name_id: int) -> EFTShortnames: - """Find payment account by corp number, corp type and payment system code.""" + """Find EFT short name by short name id.""" current_app.logger.debug(' EFTShortnames: current_app.logger.debug('>find_by_short_name_id') return result + @classmethod + def find_by_auth_account_id(cls, auth_account_id: str) -> List[EFTShortnames]: + """Find EFT shortname by auth account id.""" + current_app.logger.debug('find_by_auth_account_id') + return result + + @classmethod + def find_by_auth_account_id_state(cls, auth_account_id: str, state: List[str]) -> List[EFTShortnames]: + """Find EFT shortname by auth account id that are linked.""" + current_app.logger.debug('find_by_auth_account_id_state') + return result + + @classmethod + def find_link_by_id(cls, link_id: int) -> List[EFTShortnames]: + """Find EFT shortname link by id.""" + current_app.logger.debug('find_link_by_id') + return result + @classmethod def search(cls, search_criteria: EFTShortnamesSearch): """Search eft short name records.""" @@ -325,31 +248,38 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = # Case statement is to check for and remove the branch name from the name, so they can be filtered on separately # The branch name was added to facilitate a better short name search experience and the existing # name is preserved as it was with '-' concatenated with the branch name for reporting purposes - query = db.session.query(EFTShortnameModel.id, - EFTShortnameModel.short_name, - EFTShortnameModel.auth_account_id, - EFTShortnameModel.created_on, - EFTShortnameModel.linked_by, - EFTShortnameModel.linked_by_name, - EFTShortnameModel.linked_on, - case( - (PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), - func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') - ), - else_=PaymentAccountModel.name - ).label('account_name'), - PaymentAccountModel.branch_name.label('account_branch')) + query = (db.session.query(EFTShortnameModel.id, + EFTShortnameModel.short_name, + EFTShortnameModel.created_on, + EFTShortnameLinksModel.status_code, + EFTShortnameLinksModel.auth_account_id, + EFTShortnameLinksModel.updated_by, + EFTShortnameLinksModel.updated_by_name, + EFTShortnameLinksModel.updated_on, + case( + (EFTShortnameLinksModel.auth_account_id.is_(None), + EFTShortnameStatus.UNLINKED.value + ), + else_=EFTShortnameLinksModel.status_code + ).label('status_code')) + .outerjoin(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortnameModel.id)) # Join payment information if this is NOT the count query if not is_count: sub_query = cls.get_ordered_transaction_query().subquery() - query = query.add_columns(sub_query.c.id.label('transaction_id'), - sub_query.c.deposit_date.label('deposit_date'), - sub_query.c.transaction_date.label('transaction_date'), - sub_query.c.deposit_amount_cents.label('deposit_amount')) \ + query = query.add_columns( + case( + (PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), + func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') + ), else_=PaymentAccountModel.name).label('account_name'), + PaymentAccountModel.branch_name.label('account_branch'), + sub_query.c.id.label('transaction_id'), + sub_query.c.deposit_date.label('deposit_date'), + sub_query.c.transaction_date.label('transaction_date'), + sub_query.c.deposit_amount_cents.label('deposit_amount')) \ .outerjoin(sub_query, and_(sub_query.c.short_name_id == EFTShortnameModel.id, sub_query.c.rn == 1)) \ .outerjoin(PaymentAccountModel, - PaymentAccountModel.auth_account_id == EFTShortnameModel.auth_account_id) + PaymentAccountModel.auth_account_id == EFTShortnameLinksModel.auth_account_id) # Sub query filters for EFT dates query = query.filter_conditional_date_range(start_date=search_criteria.transaction_start_date, @@ -363,40 +293,54 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = # Sub query filters query = query.filter_conditionally(search_criteria.deposit_amount, sub_query.c.deposit_amount_cents) query = query.filter_conditionally(search_criteria.account_id, - EFTShortnameModel.auth_account_id, is_like=True) + EFTShortnameLinksModel.auth_account_id, is_like=True) # Payment account filters query = query.filter_conditionally(search_criteria.account_name, PaymentAccountModel.name, is_like=True) query = query.filter_conditionally(search_criteria.account_branch, PaymentAccountModel.branch_name, is_like=True) - # Filter by short name state - if search_criteria.state == EFTShortnameState.UNLINKED.value: - query = query.filter(EFTShortnameModel.auth_account_id.is_(None)) - elif search_criteria.state == EFTShortnameState.LINKED.value: - query = query.filter(EFTShortnameModel.auth_account_id.isnot(None)) + query = cls.get_link_state_filters(search_criteria, query) # Filter by a list of auth account ids - full match if search_criteria.account_id_list: - query = query.filter(EFTShortnameModel.auth_account_id.in_(search_criteria.account_id_list)) + query = query.filter(EFTShortnameLinksModel.auth_account_id.in_(search_criteria.account_id_list)) # Short name filters query = query.filter_conditionally(search_criteria.id, EFTShortnameModel.id) query = query.filter_conditionally(search_criteria.short_name, EFTShortnameModel.short_name, is_like=True) - query = cls.get_order_by(search_criteria, query, sub_query) + + if not is_count: + query = cls.get_order_by(search_criteria, query, sub_query) if search_criteria.state else query return query + @classmethod + def get_link_state_filters(cls, search_criteria, query): + """Build filters for link states.""" + if search_criteria.state: + if EFTShortnameStatus.UNLINKED.value in search_criteria.state: + # There can be multiple links to a short name, look for any links that don't have an UNLINKED status + # if they don't exist return them. + query = query.filter( + ~exists() + .where(EFTShortnameLinksModel.status_code != EFTShortnameStatus.UNLINKED.value) + .where(EFTShortnameLinksModel.eft_short_name_id == EFTShortnameModel.id) + .correlate(EFTShortnameModel) + ) + if EFTShortnameStatus.LINKED.value in search_criteria.state: + query = query.filter( + EFTShortnameLinksModel.status_code.in_([EFTShortnameStatus.PENDING.value, + EFTShortnameStatus.LINKED.value]) + ) + return query + @classmethod def get_order_by(cls, search_criteria, query, sub_query): """Get the order by for search query.""" - if search_criteria.state == EFTShortnameState.LINKED.value: - return query.order_by(EFTShortnameModel.linked_on.desc()) + if EFTShortnameStatus.LINKED.value in search_criteria.state: + return query.order_by(EFTShortnameLinksModel.updated_on.desc()) - if search_criteria.state == EFTShortnameState.UNLINKED.value and sub_query is not None: + if EFTShortnameStatus.UNLINKED.value in search_criteria.state and sub_query is not None: return query.order_by(sub_query.c.transaction_date.desc()) return query - - def asdict(self): - """Return the EFT Short name as a python dict.""" - return Converter().unstructure(EFTShortnameSchema.from_row(self._dao)) diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index 4eedf65b3..bf40d070e 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -331,11 +331,13 @@ class EFTGlTransferType(Enum): TRANSFER = 'TRANSFER' -class EFTShortnameState(Enum): - """EFT Short name search states.""" +class EFTShortnameStatus(Enum): + """EFT Short name statuses.""" + INACTIVE = 'INACTIVE' LINKED = 'LINKED' UNLINKED = 'UNLINKED' + PENDING = 'PENDING' class MessageType(Enum): diff --git a/pay-api/src/pay_api/version.py b/pay-api/src/pay_api/version.py index d4105527d..a0ad3cd84 100644 --- a/pay-api/src/pay_api/version.py +++ b/pay-api/src/pay_api/version.py @@ -22,4 +22,4 @@ Development release segment: .devN """ -__version__ = '1.20.9' # pylint: disable=invalid-name +__version__ = '1.20.10' # pylint: disable=invalid-name diff --git a/pay-api/tests/unit/api/test_cors_preflight.py b/pay-api/tests/unit/api/test_cors_preflight.py index 9f61d4563..a4b574161 100644 --- a/pay-api/tests/unit/api/test_cors_preflight.py +++ b/pay-api/tests/unit/api/test_cors_preflight.py @@ -227,7 +227,12 @@ def test_preflight_eft_shortnames(app, client, jwt, session): rv = client.options('/api/v1/eft-shortnames/1', headers={'Access-Control-Request-Method': 'GET'}) assert rv.status_code == 200 - assert_access_control_headers(rv, '*', 'GET, PATCH') + assert_access_control_headers(rv, '*', 'GET') + + rv = client.options('/api/v1/eft-shortnames/1/links', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') rv = client.options('/api/v1/eft-shortnames/1/transactions', headers={'Access-Control-Request-Method': 'GET'}) diff --git a/pay-api/tests/unit/api/test_eft_short_names.py b/pay-api/tests/unit/api/test_eft_short_names.py index 2864ce225..245d31873 100755 --- a/pay-api/tests/unit/api/test_eft_short_names.py +++ b/pay-api/tests/unit/api/test_eft_short_names.py @@ -19,8 +19,8 @@ import json from datetime import datetime -import pytest +import pytest from flask import current_app from pay_api.models import EFTCredit as EFTCreditModel @@ -29,13 +29,15 @@ from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Payment as PaymentModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, InvoiceStatus, PaymentMethod, PaymentStatus, Role +from pay_api.utils.enums import ( + EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod, PaymentStatus, Role) from tests.utilities.base_test import ( - factory_eft_file, factory_eft_shortname, factory_invoice, factory_payment_account, get_claims, token_header) + factory_eft_file, factory_eft_shortname, factory_eft_shortname_link, factory_invoice, factory_payment_account, + get_claims, token_header) -def test_patch_eft_short_name(session, client, jwt, app): - """Assert that an EFT short name account id can be patched.""" +def test_create_eft_short_name_link(session, client, jwt, app): + """Assert that an EFT short name link can be created.""" token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value], username='IDIR/JSMITH'), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} @@ -43,51 +45,102 @@ def test_patch_eft_short_name(session, client, jwt, app): auth_account_id='1234').save() short_name = factory_eft_shortname(short_name='TESTSHORTNAME').save() - rv = client.patch(f'/api/v1/eft-shortnames/{short_name.id}', - data=json.dumps({'accountId': '1234'}), - headers=headers) - shortname_dict = rv.json + rv = client.post(f'/api/v1/eft-shortnames/{short_name.id}/links', + data=json.dumps({'accountId': '1234'}), + headers=headers) + link_dict = rv.json assert rv.status_code == 200 - assert shortname_dict is not None - assert shortname_dict['id'] is not None - assert shortname_dict['shortName'] == 'TESTSHORTNAME' - assert shortname_dict['accountId'] == '1234' - assert shortname_dict['linkedBy'] == 'IDIR/JSMITH' + assert link_dict is not None + assert link_dict['id'] is not None + assert link_dict['shortNameId'] == short_name.id + assert link_dict['statusCode'] == EFTShortnameStatus.PENDING.value + assert link_dict['accountId'] == '1234' + assert link_dict['updatedBy'] == 'IDIR/JSMITH' date_format = '%Y-%m-%dT%H:%M:%S.%f' - assert datetime.strptime(shortname_dict['linkedOn'], date_format).date() == datetime.now().date() + assert datetime.strptime(link_dict['updatedOn'], date_format).date() == datetime.now().date() -def test_patch_eft_short_name_validation(session, client, jwt, app): - """Assert that invalid request is returned for existing short name.""" - token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) +def test_create_eft_short_name_link_validation(session, client, jwt, app): + """Assert that invalid request is returned for existing short name link.""" + token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value], + username='IDIR/JSMITH'), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} - short_name = factory_eft_shortname(short_name='TESTSHORTNAME', auth_account_id='1234').save() + short_name = factory_eft_shortname(short_name='TESTSHORTNAME').save() + factory_eft_shortname_link( + short_name_id=short_name.id, + auth_account_id='1234', + updated_by='IDIR/JSMITH' + ).save() # Assert requires an auth account id for mapping - rv = client.patch(f'/api/v1/eft-shortnames/{short_name.id}', - data=json.dumps({}), - headers=headers) + rv = client.post(f'/api/v1/eft-shortnames/{short_name.id}/links', + data=json.dumps({}), + headers=headers) - shortname_dict = rv.json + link_dict = rv.json assert rv.status_code == 400 - assert shortname_dict['type'] == 'EFT_SHORT_NAME_ACCOUNT_ID_REQUIRED' + assert link_dict['type'] == 'EFT_SHORT_NAME_ACCOUNT_ID_REQUIRED' - # Assert cannot update short name with an existing mapped account id - rv = client.patch(f'/api/v1/eft-shortnames/{short_name.id}', - data=json.dumps({'accountId': '2222'}), - headers=headers) + # Assert cannot create link to an existing mapped account id + rv = client.post(f'/api/v1/eft-shortnames/{short_name.id}/links', + data=json.dumps({'accountId': '1234'}), + headers=headers) - shortname_dict = rv.json + link_dict = rv.json assert rv.status_code == 400 - assert shortname_dict['type'] == 'EFT_SHORT_NAME_ALREADY_MAPPED' + assert link_dict['type'] == 'EFT_SHORT_NAME_ALREADY_MAPPED' + + +def test_get_eft_short_name_links(session, client, jwt, app): + """Assert that short name links can be retrieved.""" + token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value], + username='IDIR/JSMITH'), token_header) + headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + short_name = factory_eft_shortname(short_name='TESTSHORTNAME').save() + + # Assert an empty result set is properly returned + rv = client.get(f'/api/v1/eft-shortnames/{short_name.id}/links', + headers=headers) + + link_dict = rv.json + assert rv.status_code == 200 + assert link_dict is not None + assert link_dict['items'] is not None + assert len(link_dict['items']) == 0 + + # Create a short name link + rv = client.post(f'/api/v1/eft-shortnames/{short_name.id}/links', + data=json.dumps({'accountId': '1234'}), + headers=headers) + + link_dict = rv.json + assert rv.status_code == 200 + + # Assert link is returned in the result + rv = client.get(f'/api/v1/eft-shortnames/{short_name.id}/links', + headers=headers) + + link_list_dict = rv.json + assert rv.status_code == 200 + assert link_list_dict is not None + assert link_list_dict['items'] is not None + assert len(link_list_dict['items']) == 1 + link = link_list_dict['items'][0] + assert link['accountId'] == '1234' + assert link['id'] == link_dict['id'] + assert link['shortNameId'] == short_name.id + assert link['statusCode'] == EFTShortnameStatus.PENDING.value + assert link['updatedBy'] == 'IDIR/JSMITH' -def assert_short_name(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel): + +def assert_short_name(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel, + expected_status: str): """Assert short name result.""" date_format = '%Y-%m-%dT%H:%M:%S' assert result_dict['shortName'] == short_name.short_name - assert result_dict['accountId'] == short_name.auth_account_id + assert result_dict['statusCode'] == expected_status assert result_dict['depositAmount'] == transaction.deposit_amount_cents / 100 assert datetime.strptime(result_dict['depositDate'], date_format) == transaction.deposit_date assert result_dict['transactionId'] == transaction.id @@ -116,7 +169,12 @@ def test_search_eft_short_names(session, client, jwt, app): eft_file: EFTFileModel = factory_eft_file() short_name_1 = factory_eft_shortname(short_name='TESTSHORTNAME1').save() - short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2', auth_account_id='1234').save() + short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2').save() + factory_eft_shortname_link( + short_name_id=short_name_2.id, + auth_account_id='1234', + updated_by='IDIR/JSMITH' + ).save() # short_name_1 transactions to test getting first payment s1_transaction1: EFTTransactionModel = EFTTransactionModel( @@ -181,7 +239,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 1 assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME1' - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) # Assert search returns linked short names with payment account name that has a branch rv = client.get('/api/v1/eft-shortnames?state=LINKED', headers=headers) @@ -198,7 +256,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2' assert result_dict['items'][0]['accountName'] == 'ABC' assert result_dict['items'][0]['accountBranch'] == '123' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Assert search account name rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountName=BC', headers=headers) @@ -213,7 +271,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 1 assert result_dict['items'][0]['accountName'] == 'ABC' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Assert search account branch rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountBranch=2', headers=headers) @@ -229,7 +287,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert len(result_dict['items']) == 1 assert result_dict['items'][0]['accountName'] == 'ABC' assert result_dict['items'][0]['accountBranch'] == '123' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Update payment account to not have a branch name payment_account.name = 'ABC' @@ -251,7 +309,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2' assert result_dict['items'][0]['accountName'] == 'ABC' assert result_dict['items'][0]['accountBranch'] is None - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Assert search account name rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountName=BC', headers=headers) @@ -266,7 +324,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 1 assert result_dict['items'][0]['accountName'] == 'ABC' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Assert search query by no state will return all records rv = client.get('/api/v1/eft-shortnames', headers=headers) @@ -280,8 +338,8 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 2 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) - assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) + assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Assert search pagination - page 1 works rv = client.get('/api/v1/eft-shortnames?page=1&limit=1', headers=headers) @@ -295,7 +353,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 1 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) # Assert search pagination - page 2 works rv = client.get('/api/v1/eft-shortnames?page=2&limit=1', headers=headers) @@ -309,7 +367,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 1 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Assert search text brings back both short names rv = client.get('/api/v1/eft-shortnames?shortName=SHORT', headers=headers) @@ -323,8 +381,8 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 2 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) - assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) + assert_short_name(result_dict['items'][1], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) # Assert search text brings back one short name rv = client.get('/api/v1/eft-shortnames?shortName=name1', headers=headers) @@ -338,7 +396,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) # Assert search transaction date rv = client.get('/api/v1/eft-shortnames?transactionStartDate=2024-01-04&transactionEndDate=2024-01-14', @@ -353,7 +411,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) # Assert search transaction date rv = client.get('/api/v1/eft-shortnames?transactionStartDate=2024-01-04&transactionEndDate=2024-01-15', @@ -368,8 +426,8 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 2 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) - assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) + assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Assert search transaction date rv = client.get('/api/v1/eft-shortnames?depositStartDate=2024-01-16&depositEndDate=2024-01-16', headers=headers) @@ -383,7 +441,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Assert search deposit amount rv = client.get('/api/v1/eft-shortnames?depositAmount=101.50', headers=headers) @@ -397,7 +455,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1) + assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) # Assert search account id rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountId=1234', headers=headers) @@ -411,7 +469,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) # Assert search account id list rv = client.get('/api/v1/eft-shortnames?accountIdList=1,1234', headers=headers) @@ -428,7 +486,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2' assert result_dict['items'][0]['accountName'] == 'ABC' assert result_dict['items'][0]['accountBranch'] is None - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1) + assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) @pytest.mark.skip(reason='This needs to be re-thought, the create cfs invoice job should be handling receipt creation' diff --git a/pay-api/tests/unit/api/test_eft_transactions.py b/pay-api/tests/unit/api/test_eft_transactions.py index ec201fc97..1748a76ab 100755 --- a/pay-api/tests/unit/api/test_eft_transactions.py +++ b/pay-api/tests/unit/api/test_eft_transactions.py @@ -25,7 +25,8 @@ from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, PaymentMethod, Role from tests.utilities.base_test import ( - factory_eft_file, factory_eft_shortname, factory_payment_account, get_claims, token_header) + factory_eft_file, factory_eft_shortname, factory_eft_shortname_link, factory_payment_account, get_claims, + token_header) def assert_transaction(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel): @@ -48,7 +49,12 @@ def test_search_short_name_transactions(session, client, jwt, app): auth_account_id='1234').save() eft_file: EFTFileModel = factory_eft_file() short_name_1 = factory_eft_shortname(short_name='TESTSHORTNAME1').save() - short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2', auth_account_id='1234').save() + short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2').save() + factory_eft_shortname_link( + short_name_id=short_name_2.id, + auth_account_id='1234', + updated_by='IDIR/JSMITH' + ).save() # short_name_1 transactions s1_transaction1: EFTTransactionModel = EFTTransactionModel( diff --git a/pay-api/tests/unit/models/test_eft_credit_invoice_link.py b/pay-api/tests/unit/models/test_eft_credit_invoice_link.py index 5931edd14..bbb2e9ac2 100644 --- a/pay-api/tests/unit/models/test_eft_credit_invoice_link.py +++ b/pay-api/tests/unit/models/test_eft_credit_invoice_link.py @@ -18,7 +18,7 @@ """ from pay_api.models import EFTCredit, EFTCreditInvoiceLink, EFTFile, EFTShortnames, EFTTransaction -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus from tests.utilities.base_test import factory_invoice, factory_payment_account @@ -34,6 +34,7 @@ def test_eft_credit_invoice_link(session): eft_short_name = EFTShortnames() eft_short_name.auth_account_id = payment_account.auth_account_id + eft_short_name.status_code = EFTShortnameStatus.LINKED.value eft_short_name.short_name = 'TESTSHORTNAME' eft_short_name.save() diff --git a/pay-api/tests/unit/models/test_eft_credits.py b/pay-api/tests/unit/models/test_eft_credits.py index d63171920..614c230b7 100644 --- a/pay-api/tests/unit/models/test_eft_credits.py +++ b/pay-api/tests/unit/models/test_eft_credits.py @@ -20,7 +20,7 @@ from typing import List from pay_api.models import EFTCredit, EFTFile, EFTShortnames, EFTTransaction -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus from tests.utilities.base_test import factory_payment_account @@ -33,6 +33,7 @@ def test_eft_credits(session): eft_short_name = EFTShortnames() eft_short_name.auth_account_id = payment_account.auth_account_id + eft_short_name.status_code = EFTShortnameStatus.LINKED.value eft_short_name.short_name = 'TESTSHORTNAME' eft_short_name.save() diff --git a/pay-api/tests/unit/models/test_eft_gl_transfer.py b/pay-api/tests/unit/models/test_eft_gl_transfer.py index 83cec3d6e..f4d29b040 100644 --- a/pay-api/tests/unit/models/test_eft_gl_transfer.py +++ b/pay-api/tests/unit/models/test_eft_gl_transfer.py @@ -20,7 +20,7 @@ from pay_api.models.eft_gl_transfers import EFTGLTransfer as EFTGLTransferModel from pay_api.models.eft_short_names import EFTShortnames as EFTShortnamesModel -from pay_api.utils.enums import EFTGlTransferType +from pay_api.utils.enums import EFTGlTransferType, EFTShortnameStatus from tests.utilities.base_test import factory_invoice, factory_payment, factory_payment_account @@ -41,6 +41,7 @@ def create_short_name_data(): """Create shortname seed data for test.""" eft_short_name = EFTShortnamesModel() eft_short_name.short_name = 'ABC' + eft_short_name.status_code = EFTShortnameStatus.LINKED.value eft_short_name.save() return eft_short_name diff --git a/pay-api/tests/unit/models/test_eft_short_name_links.py b/pay-api/tests/unit/models/test_eft_short_name_links.py new file mode 100644 index 000000000..8cfd3e7ce --- /dev/null +++ b/pay-api/tests/unit/models/test_eft_short_name_links.py @@ -0,0 +1,73 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests to assure the EFT Short names model. + +Test-Suite to ensure that the EFT Short names model is working as expected. +""" +from datetime import datetime + +from pay_api.models.eft_short_names import EFTShortnames as EFTShortnamesModel +from pay_api.models.eft_short_name_links import EFTShortnameLinks as EFTShortnameLinksModel +from pay_api.utils.enums import EFTShortnameStatus + + +def create_short_name_data(): + """Create shortname seed data for test.""" + eft_short_name = EFTShortnamesModel() + eft_short_name.short_name = 'ABC' + eft_short_name.save() + + return eft_short_name + + +def test_eft_short_name_defaults(session): + """Assert eft short name link defaults are stored.""" + eft_short_name = create_short_name_data() + eft_short_name_link = EFTShortnameLinksModel() + eft_short_name_link.eft_short_name_id = eft_short_name.id + eft_short_name_link.status_code = EFTShortnameStatus.PENDING.value + eft_short_name_link.auth_account_id = '1234' + eft_short_name_link.save() + + assert eft_short_name_link.id is not None + assert eft_short_name_link.eft_short_name_id == eft_short_name.id + assert eft_short_name_link.created_on.date() == datetime.now().date() + assert eft_short_name_link.auth_account_id == '1234' + assert eft_short_name_link.status_code == EFTShortnameStatus.PENDING.value + assert eft_short_name_link.updated_by is None + assert eft_short_name_link.updated_by_name is None + assert eft_short_name_link.updated_on is None + + +def test_eft_short_name_all_attributes(session): + """Assert eft short name link defaults are stored.""" + eft_short_name = create_short_name_data() + eft_short_name_link = EFTShortnameLinksModel() + eft_short_name_link.eft_short_name_id = eft_short_name.id + eft_short_name_link.status_code = EFTShortnameStatus.PENDING.value + eft_short_name_link.auth_account_id = '1234' + eft_short_name_link.updated_by_name = 'name' + eft_short_name_link.updated_by = 'userid' + eft_short_name_link.updated_on = datetime.now() + eft_short_name_link.save() + + assert eft_short_name_link.id is not None + assert eft_short_name_link.eft_short_name_id == eft_short_name.id + assert eft_short_name_link.created_on.date() == datetime.now().date() + assert eft_short_name_link.auth_account_id == '1234' + assert eft_short_name_link.status_code == EFTShortnameStatus.PENDING.value + assert eft_short_name_link.updated_by == 'userid' + assert eft_short_name_link.updated_by_name == 'name' + assert eft_short_name_link.updated_on.date() == datetime.now().date() diff --git a/pay-api/tests/unit/models/test_eft_short_names.py b/pay-api/tests/unit/models/test_eft_short_names.py index 6d160fa35..6c0ea03d7 100644 --- a/pay-api/tests/unit/models/test_eft_short_names.py +++ b/pay-api/tests/unit/models/test_eft_short_names.py @@ -21,8 +21,8 @@ from pay_api.models.eft_short_names import EFTShortnames as EFTShortnamesModel -def test_eft_short_name_defaults(session): - """Assert eft short names defaults are stored.""" +def test_eft_short_name_model(session): + """Assert eft short names are stored.""" eft_short_name = EFTShortnamesModel() eft_short_name.short_name = 'ABC' eft_short_name.save() @@ -30,19 +30,3 @@ def test_eft_short_name_defaults(session): assert eft_short_name.id is not None assert eft_short_name.short_name == 'ABC' assert eft_short_name.created_on.date() == datetime.now().date() - assert eft_short_name.auth_account_id is None - - -def test_eft_short_names_all_attributes(session): - """Assert all eft short names attributes are stored.""" - eft_short_name = EFTShortnamesModel() - eft_short_name.short_name = 'ABC' - eft_short_name.auth_account_id = '1234' - eft_short_name.save() - - assert eft_short_name.id is not None - - eft_short_name = EFTShortnamesModel.find_by_id(eft_short_name.id) - assert eft_short_name.short_name == 'ABC' - assert eft_short_name.auth_account_id == '1234' - assert eft_short_name.created_on.date() == datetime.now().date() diff --git a/pay-api/tests/utilities/base_test.py b/pay-api/tests/utilities/base_test.py index d9cd157d6..0c322bce6 100644 --- a/pay-api/tests/utilities/base_test.py +++ b/pay-api/tests/utilities/base_test.py @@ -25,13 +25,13 @@ from faker import Faker from pay_api.models import ( - CfsAccount, Comment, DistributionCode, DistributionCodeLink, EFTFile, EFTShortnames, Invoice, InvoiceReference, - NonSufficientFundsModel, Payment, PaymentAccount, PaymentLineItem, PaymentTransaction, Receipt, RoutingSlip, - Statement, StatementInvoices, StatementSettings) + CfsAccount, Comment, DistributionCode, DistributionCodeLink, EFTFile, EFTShortnameLinks, EFTShortnames, Invoice, + InvoiceReference, NonSufficientFundsModel, Payment, PaymentAccount, PaymentLineItem, PaymentTransaction, Receipt, + RoutingSlip, Statement, StatementInvoices, StatementSettings) from pay_api.utils.constants import DT_SHORT_FORMAT from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, PaymentStatus, - PaymentSystem, Role, RoutingSlipStatus) + CfsAccountStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, + PaymentStatus, PaymentSystem, Role, RoutingSlipStatus) token_header = { @@ -891,9 +891,22 @@ def factory_eft_file(file_ref: str = 'test_ref.txt'): return EFTFile(file_ref=file_ref).save() -def factory_eft_shortname(short_name: str, auth_account_id: str = None): +def factory_eft_shortname(short_name: str): """Return an EFT short name model.""" - return EFTShortnames(short_name=short_name, auth_account_id=auth_account_id) + return EFTShortnames(short_name=short_name) + + +def factory_eft_shortname_link(short_name_id: int, auth_account_id: str = '1234', + updated_by: str = None, updated_on: datetime = datetime.now()): + """Return an EFT short name link model.""" + return EFTShortnameLinks( + eft_short_name_id=short_name_id, + auth_account_id=auth_account_id, + status_code=EFTShortnameStatus.PENDING.value, + updated_by=updated_by, + updated_by_name=updated_by, + updated_on=updated_on + ) def factory_non_sufficient_funds(invoice_id: int, invoice_number: str, description: str = None): From 13188060572f57fe3782265bdbad7e18d51f7264 Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Fri, 19 Apr 2024 15:32:00 -0700 Subject: [PATCH 52/87] 19496 -EFT TDI17 Processing / Multi Account linking (#1489) * 19496 - EFT TDI17 processing update, Multi account linking fixes * update queue poetry.lock * linting --- jobs/payment-jobs/invoke_jobs.py | 4 - jobs/payment-jobs/poetry.lock | 4 +- jobs/payment-jobs/tasks/eft_transfer_task.py | 292 ------------------ .../tasks/electronic_funds_transfer_task.py | 40 ++- jobs/payment-jobs/tests/jobs/factory.py | 26 +- .../tests/jobs/test_eft_transfer_task.py | 162 ---------- .../test_electronic_funds_transfer_task.py | 20 +- pay-queue/poetry.lock | 30 +- pay-queue/pyproject.toml | 2 +- .../services/eft/eft_reconciliation.py | 51 +-- .../src/pay_queue/services/eft/eft_record.py | 22 +- pay-queue/src/pay_queue/version.py | 2 +- .../integration/test_eft_reconciliation.py | 82 +++-- pay-queue/tests/unit/test_eft_file_parser.py | 5 + pay-queue/tests/utilities/factory_utils.py | 3 +- 15 files changed, 200 insertions(+), 545 deletions(-) delete mode 100644 jobs/payment-jobs/tasks/eft_transfer_task.py delete mode 100644 jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index 7d03dbdd3..55fef4ffe 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -24,7 +24,6 @@ import config from services import oracle_db -from tasks.eft_transfer_task import EftTransferTask from tasks.routing_slip_task import RoutingSlipTask from tasks.electronic_funds_transfer_task import ElectronicFundsTransferTask from tasks.statement_due_task import StatementDueTask @@ -146,9 +145,6 @@ def run(job_name, argument=None): elif job_name == 'BCOL_REFUND_CONFIRMATION': BcolRefundConfirmationTask.update_bcol_refund_invoices() application.logger.info(f'<<<< Completed running BCOL Refund Confirmation Job >>>>') - elif job_name == 'EFT_TRANSFER': - EftTransferTask.create_ejv_file() - application.logger.info(f'<<<< Completed Creating EFT Transfer File for transfer to internal GLs>>>>') else: application.logger.debug('No valid args passed. Exiting job without running any ***************') diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock index 88cecd800..ff1195ca7 100644 --- a/jobs/payment-jobs/poetry.lock +++ b/jobs/payment-jobs/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "alembic" @@ -1693,7 +1693,7 @@ werkzeug = "3.0.1" type = "git" url = "https://github.com/bcgov/sbc-pay.git" reference = "feature-queue-python-upgrade" -resolved_reference = "727d3385439146f9616ded4548c22a4867f42ff3" +resolved_reference = "ba20cecf7e65065fa22dbfedb0f0bb5c1ee7ec94" subdirectory = "pay-api" [[package]] diff --git a/jobs/payment-jobs/tasks/eft_transfer_task.py b/jobs/payment-jobs/tasks/eft_transfer_task.py deleted file mode 100644 index c43bd7412..000000000 --- a/jobs/payment-jobs/tasks/eft_transfer_task.py +++ /dev/null @@ -1,292 +0,0 @@ -# Copyright © 2023 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Task to create EFT Transfer Journal Voucher.""" - -import time -from datetime import datetime -from typing import List - -from flask import current_app -from pay_api.models import DistributionCode as DistributionCodeModel -from pay_api.models import EFTGLTransfer as EFTGLTransferModel -from pay_api.models import EFTShortnames as EFTShortnameModel -from pay_api.models import EjvFile as EjvFileModel -from pay_api.models import EjvHeader as EjvHeaderModel -from pay_api.models import EjvLink as EjvLinkModel -from pay_api.models import Invoice as InvoiceModel -from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.models import PaymentLineItem as PaymentLineItemModel -from pay_api.models import db -from pay_api.services.flags import flags -from pay_api.utils.enums import ( - DisbursementStatus, EFTGlTransferType, EjvFileType, EJVLinkType, InvoiceStatus, PaymentMethod) -from sqlalchemy import exists, func - -from tasks.common.cgi_ejv import CgiEjv - - -class EftTransferTask(CgiEjv): - """Task to create EJV Files.""" - - @classmethod - def create_ejv_file(cls): - """Create JV files and upload to CGI. - - Steps: - 1. Find all invoices from invoice table for EFT Transfer. - 2. Group by fee schedule and create JV Header and JV Details. - 3. Upload the file to minio for future reference. - 4. Upload to sftp for processing. First upload JV file and then a TRG file. - 5. Update the statuses and create records to for the batch. - """ - eft_enabled = flags.is_on('enable-eft-payment-method', default=False) - if eft_enabled: - cls._create_ejv_file_for_eft_transfer() - - @staticmethod - def get_invoices_for_transfer(payment_account_id: int): - """Return invoices for EFT Holdings transfer.""" - # Return all EFT Paid invoices that don't already have an EFT GL Transfer record - invoices: List[InvoiceModel] = db.session.query(InvoiceModel) \ - .filter(InvoiceModel.invoice_status_code == InvoiceStatus.PAID.value) \ - .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ - .filter(InvoiceModel.payment_account_id == payment_account_id) \ - .filter(~exists().where((EFTGLTransferModel.invoice_id == InvoiceModel.id) & - (EFTGLTransferModel.transfer_type == EFTGlTransferType.TRANSFER.value))).all() - return invoices - - @staticmethod - def get_invoices_for_refund_reversal(payment_account_id: int): - """Return invoices for EFT reversal.""" - refund_inv_statuses = (InvoiceStatus.REFUNDED.value, InvoiceStatus.REFUND_REQUESTED.value, - InvoiceStatus.CREDITED.value) - # Future may need to re-evaluate when EFT Short name unlinking use cases are defined - invoices: List[InvoiceModel] = db.session.query(InvoiceModel) \ - .filter(InvoiceModel.invoice_status_code.in_(refund_inv_statuses)) \ - .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ - .filter(InvoiceModel.payment_account_id == payment_account_id) \ - .filter(InvoiceModel.disbursement_status_code == DisbursementStatus.COMPLETED.value) \ - .filter(~exists().where((EFTGLTransferModel.invoice_id == InvoiceModel.id) & - (EFTGLTransferModel.transfer_type == EFTGlTransferType.REVERSAL.value))).all() - current_app.logger.info(invoices) - return invoices - - @staticmethod - def get_account_ids() -> List[int]: - """Return account IDs for EFT payments.""" - query = db.session.query(func.DISTINCT(InvoiceModel.payment_account_id)) \ - .filter(InvoiceModel.invoice_status_code == InvoiceStatus.PAID.value) \ - .filter(InvoiceModel.payment_method_code == PaymentMethod.EFT.value) \ - .filter(~exists().where((EFTGLTransferModel.invoice_id == InvoiceModel.id) & - (EFTGLTransferModel.transfer_type == EFTGlTransferType.TRANSFER.value))) - return db.session.scalars(query).all() - - @staticmethod - def create_eft_gl_transfer(eft_holding_gl: str, line_distribution_gl: str, transfer_type: str, - line_item: PaymentLineItemModel, payment_account: PaymentAccountModel): - """Create EFT GL Transfer record.""" - short_name_id = db.session.query(EFTShortnameModel.id) \ - .filter(EFTShortnameModel.auth_account_id == payment_account.auth_account_id).one()[0] - source_gl = eft_holding_gl if transfer_type == EFTGlTransferType.TRANSFER.value else line_distribution_gl - target_gl = line_distribution_gl if transfer_type == EFTGlTransferType.TRANSFER.value else eft_holding_gl - now = datetime.now() - return EFTGLTransferModel( - invoice_id=line_item.invoice_id, - is_processed=True, - processed_on=now, - short_name_id=short_name_id, - source_gl=source_gl.strip(), - target_gl=target_gl.strip(), - transfer_amount=line_item.total, - transfer_type=transfer_type, - transfer_date=now - ) - - @classmethod - def _process_eft_transfer_invoices(cls, invoices: List[InvoiceModel], transfer_type: str, - eft_gl_transfers: dict = None) -> List[EFTGLTransferModel]: - """Create EFT GL Transfer for invoice line items.""" - eft_holding_gl = current_app.config.get('EFT_HOLDING_GL') - eft_gl_transfers = eft_gl_transfers or {} - - for invoice in invoices: - payment_account = PaymentAccountModel.find_by_id(invoice.payment_account_id) - for line_item in invoice.payment_line_items: - distribution_code: DistributionCodeModel = \ - DistributionCodeModel.find_by_id(line_item.fee_distribution_id) - - # Create line distribution transfer - line_distribution_code: DistributionCodeModel = DistributionCodeModel.find_by_id( - distribution_code.distribution_code_id - ) - - line_distribution = cls.get_distribution_string(line_distribution_code) - - line_gl_transfer = cls.create_eft_gl_transfer( - eft_holding_gl=eft_holding_gl, - line_distribution_gl=line_distribution, - transfer_type=transfer_type, - line_item=line_item, - payment_account=payment_account - ) - - eft_gl_transfers.setdefault(invoice.payment_account_id, []) - eft_gl_transfers[invoice.payment_account_id].append(line_gl_transfer) - db.session.add(line_gl_transfer) - - # Check for service fee, if there is one create a transfer record - if distribution_code.service_fee_distribution_code_id: - service_fee_distribution_code: DistributionCodeModel = DistributionCodeModel.find_by_id( - distribution_code.service_fee_distribution_code_id - ) - - service_fee_distribution = cls.get_distribution_string(service_fee_distribution_code) - - service_fee_gl_transfer = cls.create_eft_gl_transfer( - eft_holding_gl=eft_holding_gl, - line_distribution_gl=service_fee_distribution, - transfer_type=transfer_type, - line_item=line_item, - payment_account=payment_account - ) - service_fee_gl_transfer.transfer_amount = line_item.service_fees - eft_gl_transfers[invoice.payment_account_id].append(service_fee_gl_transfer) - db.session.add(service_fee_gl_transfer) - - return eft_gl_transfers - - @staticmethod - def process_invoice_ejv_links(invoices: List[InvoiceModel], ejv_header_model_id: int): - """Create EJV Invoice Links.""" - current_app.logger.info('Creating ejv invoice link records and setting invoice status.') - sequence = 1 - for inv in invoices: - current_app.logger.debug(f'Creating EJV Invoice Link for invoice id: {inv.id}') - # Create Ejv file link and flush - ejv_invoice_link = EjvLinkModel(link_id=inv.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header_model_id, - disbursement_status_code=DisbursementStatus.UPLOADED.value, - sequence=sequence) - db.session.add(ejv_invoice_link) - sequence += 1 - - @classmethod - def _create_ejv_file_for_eft_transfer(cls): # pylint:disable=too-many-locals, too-many-statements - """Create EJV file for the EFT Transfer and upload.""" - ejv_content: str = '' - batch_total: float = 0 - control_total: int = 0 - today = datetime.now() - transfer_desc = current_app.config.get('EFT_TRANSFER_DESC'). \ - format(today.strftime('%B').upper(), f'{today.day:0>2}')[:100] - transfer_desc = f'{transfer_desc:<100}' - - # Create a ejv file model record. - ejv_file_model: EjvFileModel = EjvFileModel( - file_type=EjvFileType.TRANSFER.value, - file_ref=cls.get_file_name(), - disbursement_status_code=DisbursementStatus.UPLOADED.value - ).flush() - batch_number = cls.get_batch_number(ejv_file_model.id) - batch_type = 'GA' - - account_ids = cls.get_account_ids() - - # JV Batch Header - batch_header: str = cls.get_batch_header(batch_number, batch_type) - - effective_date: str = cls.get_effective_date() - for account_id in account_ids: - account_jv: str = '' - payment_invoices = cls.get_invoices_for_transfer(account_id) - refund_invoices = cls.get_invoices_for_refund_reversal(account_id) - transfers = cls._process_eft_transfer_invoices(payment_invoices, EFTGlTransferType.TRANSFER.value) - cls._process_eft_transfer_invoices(refund_invoices, EFTGlTransferType.REVERSAL.value, transfers) - invoices = payment_invoices + refund_invoices - - ejv_header_model: EjvFileModel = EjvHeaderModel( - payment_account_id=account_id, - disbursement_status_code=DisbursementStatus.UPLOADED.value, - ejv_file_id=ejv_file_model.id - ).flush() - journal_name: str = cls.get_journal_name(ejv_header_model.id) - - line_number: int = 0 - total: float = 0 - - current_app.logger.info(f'Processing EFT Transfers for account_id: {account_id}.') - account_transfers: List[EFTGLTransferModel] = transfers[account_id] - - for eft_transfer in account_transfers: - invoice_number = f'#{eft_transfer.invoice_id}' - description = transfer_desc[:-len(invoice_number)] + invoice_number - description = f'{description[:100]:<100}' - - if eft_transfer.transfer_amount > 0: - total += eft_transfer.transfer_amount - flow_through = f'{eft_transfer.invoice_id:<110}' - - line_number += 1 - control_total += 1 - - # Debit from source gl - source_gl = f'{eft_transfer.source_gl}{cls.EMPTY:<16}' - target_gl = f'{eft_transfer.target_gl}{cls.EMPTY:<16}' - - account_jv = account_jv + cls.get_jv_line(batch_type, source_gl, description, - effective_date, flow_through, journal_name, - eft_transfer.transfer_amount, - line_number, 'D') - # Credit to target gl - account_jv = account_jv + cls.get_jv_line(batch_type, target_gl, description, - effective_date, flow_through, journal_name, - eft_transfer.transfer_amount, - line_number, 'C') - line_number += 1 - control_total += 1 - - batch_total += total - - # Skip if we have no total from the transfers. - if total > 0: - # A JV header for each account. - control_total += 1 - account_jv = cls.get_jv_header(batch_type, cls.get_journal_batch_name(batch_number), - journal_name, total) + account_jv - ejv_content = ejv_content + account_jv - - # Create ejv invoice link records and set invoice status - cls.process_invoice_ejv_links(invoices, ejv_header_model.id) - - db.session.flush() - - if not ejv_content: - db.session.rollback() - return - - # JV Batch Trailer - batch_trailer: str = cls.get_batch_trailer(batch_number, batch_total, batch_type, control_total) - ejv_content = f'{batch_header}{ejv_content}{batch_trailer}' - - # Create a file add this content. - file_path_with_name, trg_file_path = cls.create_inbox_and_trg_files(ejv_content) - - # Upload file and trg to FTP - current_app.logger.info('Uploading EFT Transfer file to ftp.') - cls.upload(ejv_content, cls.get_file_name(), file_path_with_name, trg_file_path) - - db.session.commit() - - # Add a sleep to prevent collision on file name. - time.sleep(1) diff --git a/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py b/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py index d8137b464..ce1983a88 100644 --- a/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py +++ b/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py @@ -12,13 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. """Task for linking electronic funds transfers.""" - +from dataclasses import dataclass from datetime import datetime from typing import List from flask import current_app from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import EFTShortnames as EFTShortnameModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTCredit as EFTCreditModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel @@ -28,11 +29,19 @@ from pay_api.services.cfs_service import CFSService from pay_api.services import EFTShortNamesService from pay_api.services.receipt import Receipt -from pay_api.utils.enums import CfsAccountStatus, EFTShortnameState, InvoiceReferenceStatus, InvoiceStatus +from pay_api.utils.enums import CfsAccountStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus from pay_api.utils.util import generate_receipt_number from sentry_sdk import capture_message +@dataclass +class EFTShortnameInfo: + """Consolidated EFT Short name information for processing.""" + + id: int + auth_account_id: str + + class ElectronicFundsTransferTask: # pylint:disable=too-few-public-methods """Task to link electronic funds transfers.""" @@ -46,7 +55,7 @@ def link_electronic_funds_transfers(cls): 3. Apply the receipts to the invoices. 4. Notify mailer """ - eft_short_names = cls._get_eft_short_names_by_state(EFTShortnameState.LINKED.value) + eft_short_names: List[EFTShortnameInfo] = cls._get_eft_short_names_by_status(EFTShortnameStatus.LINKED.value) for eft_short_name in eft_short_names: try: current_app.logger.debug(f'Linking Electronic Funds Transfer: {eft_short_name.id}') @@ -87,19 +96,28 @@ def link_electronic_funds_transfers(cls): continue @classmethod - def _get_eft_short_names_by_state(cls, state: EFTShortnameState) -> List[EFTShortnameModel]: + def _get_eft_short_names_by_status(cls, status: str) -> List[EFTShortnameModel]: """Get electronic funds transfer by state.""" - query = db.session.query(EFTShortnameModel) \ - .join(PaymentAccountModel, PaymentAccountModel.auth_account_id == EFTShortnameModel.auth_account_id) \ + query = db.session.query(EFTShortnameModel.id.label('short_name_id'), EFTShortnameLinksModel.auth_account_id) \ + .join(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortnameModel.id) \ + .join(PaymentAccountModel, PaymentAccountModel.auth_account_id == EFTShortnameLinksModel.auth_account_id) \ .join(CfsAccountModel, CfsAccountModel.account_id == PaymentAccountModel.id) \ .filter(CfsAccountModel.status == CfsAccountStatus.ACTIVE.value) - if state == EFTShortnameState.UNLINKED.value: - query = query.filter(EFTShortnameModel.auth_account_id.is_(None)) - if state == EFTShortnameState.LINKED.value: - query = query.filter(EFTShortnameModel.auth_account_id.isnot(None)) + if status == EFTShortnameStatus.UNLINKED.value: + query = query.filter(EFTShortnameLinksModel.id.is_(None)) + if status == EFTShortnameStatus.LINKED.value: + query = query.filter(EFTShortnameLinksModel.status_code == status) + + result = query.all() + short_name_results = [] + + # Short name can have multiple linked accounts, prepare list of dataclasses with the associated + # auth_account_ids for the outer processing loops + for short_name_id, auth_account_id in result: + short_name_results.append(EFTShortnameInfo(id=short_name_id, auth_account_id=auth_account_id)) - return query.all() + return short_name_results @classmethod def _apply_electronic_funds_transfers_to_pending_invoices(cls, diff --git a/jobs/payment-jobs/tests/jobs/factory.py b/jobs/payment-jobs/tests/jobs/factory.py index b916f811a..e199cf1cc 100644 --- a/jobs/payment-jobs/tests/jobs/factory.py +++ b/jobs/payment-jobs/tests/jobs/factory.py @@ -20,12 +20,12 @@ from datetime import datetime, timedelta from pay_api.models import ( - CfsAccount, DistributionCode, DistributionCodeLink, EFTCredit, EFTFile, EFTShortnames, EFTTransaction, Invoice, - InvoiceReference, Payment, PaymentAccount, PaymentLineItem, Receipt, Refund, RefundsPartial, RoutingSlip, - StatementRecipients, StatementSettings) + CfsAccount, DistributionCode, DistributionCodeLink, EFTCredit, EFTFile, EFTShortnameLinks, EFTShortnames, + EFTTransaction, Invoice, InvoiceReference, Payment, PaymentAccount, PaymentLineItem, Receipt, Refund, + RefundsPartial, RoutingSlip, StatementRecipients, StatementSettings) from pay_api.utils.enums import ( - CfsAccountStatus, EFTProcessStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, - PaymentStatus, PaymentSystem, RoutingSlipStatus) + CfsAccountStatus, EFTProcessStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, + PaymentMethod, PaymentStatus, PaymentSystem, RoutingSlipStatus) def factory_premium_payment_account(bcol_user_id='PB25020', bcol_account_id='1234567890', auth_account_id='1234'): @@ -225,15 +225,27 @@ def factory_create_eft_account(auth_account_id='1234', status=CfsAccountStatus.P return account -def factory_create_eft_shortname(auth_account_id: str, short_name: str): +def factory_create_eft_shortname(short_name: str): """Return Factory.""" short_name = EFTShortnames( - auth_account_id=auth_account_id, short_name=short_name ).save() return short_name +def factory_eft_shortname_link(short_name_id: int, auth_account_id: str = '1234', + updated_by: str = None, updated_on: datetime = datetime.now()): + """Return an EFT short name link model.""" + return EFTShortnameLinks( + eft_short_name_id=short_name_id, + auth_account_id=auth_account_id, + status_code=EFTShortnameStatus.LINKED.value, + updated_by=updated_by, + updated_by_name=updated_by, + updated_on=updated_on + ) + + def factory_create_eft_credit(amount=100, remaining_amount=0, eft_file_id=1, short_name_id=1, payment_account_id=1, eft_transaction_id=1): """Return Factory.""" diff --git a/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py b/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py deleted file mode 100644 index 27aaf3256..000000000 --- a/jobs/payment-jobs/tests/jobs/test_eft_transfer_task.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright © 2023 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests to assure the EFT CGI Transfer Job. - -Test-Suite to ensure that the EFT Transfer task is working as expected. -""" -from datetime import datetime -from typing import List - -from pay_api.models import DistributionCode, EFTGLTransfer, EjvFile, EjvHeader, EjvLink, FeeSchedule, Invoice, db -from pay_api.utils.enums import DisbursementStatus, EFTGlTransferType, EjvFileType, InvoiceStatus, PaymentMethod - -from tasks.eft_transfer_task import EftTransferTask - -from .factory import ( - factory_create_eft_account, factory_create_eft_shortname, factory_distribution, factory_invoice, - factory_payment_line_item) - - -def test_eft_transfer(app, session, monkeypatch): - """Test EFT Holdings GL Transfer for EFT invoices. - - Steps: - 1) Create GL codes to match GA batch type. - 2) Create account to short name mappings - 3) Create paid invoices for EFT. - 4) Run the job and assert results. - """ - monkeypatch.setattr('pysftp.Connection.put', lambda *args, **kwargs: None) - - corp_type = 'BEN' - filing_type = 'BCINC' - - # Find fee schedule which have service fees. - fee_schedule: FeeSchedule = FeeSchedule.find_by_filing_type_and_corp_type(corp_type, filing_type) - # Create a service fee distribution code - service_fee_dist_code = factory_distribution(name='service fee', client='112', reps_centre='99999', - service_line='99999', - stob='9999', project_code='9999999') - service_fee_dist_code.save() - - dist_code: DistributionCode = DistributionCode.find_by_active_for_fee_schedule(fee_schedule.fee_schedule_id) - # Update fee dist code to match the requirement. - dist_code.client = '112' - dist_code.responsibility_centre = '11111' - dist_code.service_line = '22222' - dist_code.stob = '3333' - dist_code.project_code = '4444444' - dist_code.service_fee_distribution_code_id = service_fee_dist_code.distribution_code_id - dist_code.save() - - app.config['EFT_HOLDING_GL'] = '1128888888888888888000000000000000' - eft_holding_gl = app.config['EFT_HOLDING_GL'] - distribution_gl = EftTransferTask.get_distribution_string(dist_code).strip() - service_fee_gl = EftTransferTask.get_distribution_string(service_fee_dist_code).strip() - - # GA - eft_account_1 = factory_create_eft_account(auth_account_id='1') - eft_shortname_1 = factory_create_eft_shortname(auth_account_id='1', short_name='SHORTNAME1') - eft_account_2 = factory_create_eft_account(auth_account_id='2') - eft_shortname_2 = factory_create_eft_shortname(auth_account_id='2', short_name='SHORTNAME2') - - eft_accounts = [eft_account_1, eft_account_2] - invoices: List[Invoice] = [] - for account in eft_accounts: - inv = factory_invoice(payment_account=account, corp_type_code=corp_type, total=101.5, - status_code=InvoiceStatus.PAID.value, payment_method_code=PaymentMethod.EFT.value) - factory_payment_line_item(invoice_id=inv.id, - fee_schedule_id=fee_schedule.fee_schedule_id, - filing_fees=100, - total=100, - service_fees=1.5, - fee_dist_id=dist_code.distribution_code_id) - invoices.append(inv) - - EftTransferTask.create_ejv_file() - - # Lookup invoice and assert disbursement status - for invoice in invoices: - ejv_inv_link: EjvLink = db.session.query(EjvLink) \ - .filter(EjvLink.link_id == invoice.id).first() - assert ejv_inv_link - - ejv_header = db.session.query(EjvHeader).filter(EjvHeader.id == ejv_inv_link.ejv_header_id).first() - assert ejv_header.disbursement_status_code == DisbursementStatus.UPLOADED.value - assert ejv_header - - ejv_file: EjvFile = EjvFile.find_by_id(ejv_header.ejv_file_id) - assert ejv_file - assert ejv_file.disbursement_status_code == DisbursementStatus.UPLOADED.value - assert ejv_file.file_type == EjvFileType.TRANSFER.value - - eft_transfers: List[EFTGLTransfer] = db.session.query(EFTGLTransfer).all() - - now = datetime.now().date() - - assert eft_transfers - assert len(eft_transfers) == 4 - - # Assert first short name line item distribution - assert eft_transfers[0].id is not None - assert eft_transfers[0].short_name_id == eft_shortname_1.id - assert eft_transfers[0].invoice_id == invoices[0].id - assert eft_transfers[0].transfer_amount == invoices[0].payment_line_items[0].total - assert eft_transfers[0].transfer_type == EFTGlTransferType.TRANSFER.value - assert eft_transfers[0].transfer_date.date() == now - assert eft_transfers[0].is_processed - assert eft_transfers[0].processed_on.date() == now - assert eft_transfers[0].created_on.date() == now - assert eft_transfers[0].source_gl == eft_holding_gl - assert eft_transfers[0].target_gl == distribution_gl - - # Assert first short name service fee distribution - assert eft_transfers[1].id is not None - assert eft_transfers[1].short_name_id == eft_shortname_1.id - assert eft_transfers[1].invoice_id == invoices[0].id - assert eft_transfers[1].transfer_type == EFTGlTransferType.TRANSFER.value - assert eft_transfers[1].transfer_amount == invoices[0].payment_line_items[0].service_fees - assert eft_transfers[1].transfer_date.date() == now - assert eft_transfers[1].is_processed - assert eft_transfers[1].processed_on.date() == now - assert eft_transfers[1].created_on.date() == now - assert eft_transfers[1].source_gl == eft_holding_gl - assert eft_transfers[1].target_gl == service_fee_gl - - # Assert second short name line item distribution - assert eft_transfers[2].id is not None - assert eft_transfers[2].short_name_id == eft_shortname_2.id - assert eft_transfers[2].invoice_id == invoices[1].id - assert eft_transfers[2].transfer_type == EFTGlTransferType.TRANSFER.value - assert eft_transfers[2].transfer_amount == invoices[1].payment_line_items[0].total - assert eft_transfers[2].transfer_date.date() == now - assert eft_transfers[2].is_processed - assert eft_transfers[2].processed_on.date() == now - assert eft_transfers[2].created_on.date() == now - assert eft_transfers[2].source_gl == eft_holding_gl - assert eft_transfers[2].target_gl == distribution_gl - - # Assert second short name service fee distribution - assert eft_transfers[3].id is not None - assert eft_transfers[3].short_name_id == eft_shortname_2.id - assert eft_transfers[3].invoice_id == invoices[1].id - assert eft_transfers[3].transfer_type == EFTGlTransferType.TRANSFER.value - assert eft_transfers[3].transfer_amount == invoices[1].payment_line_items[0].service_fees - assert eft_transfers[3].transfer_date.date() == now - assert eft_transfers[3].is_processed - assert eft_transfers[3].processed_on.date() == now - assert eft_transfers[3].created_on.date() == now - assert eft_transfers[3].source_gl == eft_holding_gl - assert eft_transfers[3].target_gl == service_fee_gl diff --git a/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py b/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py index de524fdfa..ee3d183d3 100644 --- a/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py +++ b/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py @@ -21,6 +21,7 @@ from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTShortnames as EFTShortnameModel from pay_api.utils.enums import CfsAccountStatus, PaymentMethod from pay_api.services import CFSService @@ -29,7 +30,8 @@ from .factory import ( factory_create_eft_account, factory_create_eft_credit, factory_create_eft_file, factory_create_eft_shortname, - factory_create_eft_transaction, factory_invoice, factory_invoice_reference, factory_payment) + factory_create_eft_transaction, factory_eft_shortname_link, factory_invoice, factory_invoice_reference, + factory_payment) def test_link_electronic_funds_transfer(session): @@ -40,7 +42,12 @@ def test_link_electronic_funds_transfer(session): payment_account = factory_create_eft_account(auth_account_id=auth_account_id, status=CfsAccountStatus.ACTIVE.value) eft_file = factory_create_eft_file() factory_create_eft_transaction(file_id=eft_file.id) - eft_short_name = factory_create_eft_shortname(auth_account_id=auth_account_id, short_name=short_name) + eft_short_name = factory_create_eft_shortname(short_name=short_name) + eft_short_name_link = factory_eft_shortname_link( + short_name_id=eft_short_name.id, + auth_account_id=auth_account_id, + updated_by='test' + ).save() invoice = factory_invoice(payment_account=payment_account, payment_method_code=PaymentMethod.EFT.value) factory_invoice_reference(invoice_id=invoice.id) factory_payment(payment_account_id=payment_account.id, payment_method_code=PaymentMethod.EFT.value, @@ -50,13 +57,14 @@ def test_link_electronic_funds_transfer(session): payment_account_id=payment_account.id) eft_short_name = EFTShortnameModel.find_by_short_name(short_name) - eft_short_name.linked_by = 'test' - eft_short_name.linked_by_name = 'test' - eft_short_name.linked_on = datetime.now() + eft_short_name_link = EFTShortnameLinksModel.find_by_short_name_id(eft_short_name.id)[0] + eft_short_name_link.updated_by = 'test' + eft_short_name_link.updated_by_name = 'test' + eft_short_name_link.updated_on = datetime.now() eft_short_name.save() payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id( - eft_short_name.auth_account_id) + eft_short_name_link.auth_account_id) cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id( payment_account.id) diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index c9e095cb8..d053b1e55 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "alembic" @@ -1649,9 +1649,9 @@ werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/Jxio/sbc-pay.git" -reference = "20457" -resolved_reference = "da7c2f0d798ad46e8bcd6f010ef1d0a7a66d8be0" +url = "https://github.com/bcgov/sbc-pay.git" +reference = "feature-queue-python-upgrade" +resolved_reference = "ba20cecf7e65065fa22dbfedb0f0bb5c1ee7ec94" subdirectory = "pay-api" [[package]] @@ -2285,13 +2285,13 @@ subdirectory = "python" [[package]] name = "scramp" -version = "1.4.4" +version = "1.4.5" description = "An implementation of the SCRAM protocol." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, - {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, + {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, + {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, ] [package.dependencies] @@ -2358,18 +2358,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "69.2.0" +version = "69.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, + {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, + {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -2425,7 +2425,7 @@ develop = false type = "git" url = "https://github.com/bcgov/lear.git" reference = "feature-legal-name" -resolved_reference = "8ccb5e1bfdda45cca72ec65f38394ff064d256b1" +resolved_reference = "e7d04a5b6a6b7fd278a4a445f8b6303583f68aec" subdirectory = "python/common/sql-versioning" [[package]] @@ -2664,4 +2664,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "f09623e21148a2625eb0fda3d9ae9a8e645d7fa142f649894b625363a9e0b3d4" +content-hash = "f7e978de8d7c39e508ff106d26b6a9fedac7896203b705955ed5b3a5d481c775" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml index abc9acb73..299044c9d 100644 --- a/pay-queue/pyproject.toml +++ b/pay-queue/pyproject.toml @@ -22,7 +22,7 @@ protobuf = "4.25.3" launchdarkly-server-sdk = "^9.2.2" cachecontrol = "^0.14.0" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/Jxio/sbc-pay.git", rev = "20457", subdirectory = "pay-api"} +pay-api = {git = "https://github.com/bcgov/sbc-pay.git", branch = "feature-queue-python-upgrade", subdirectory = "pay-api"} flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} pg8000 = "^1.30.5" diff --git a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py index d63e23e29..dd0774b1b 100644 --- a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py +++ b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py @@ -28,7 +28,7 @@ from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.services.eft_service import EftService as EFTService from pay_api.services.eft_short_names import EFTShortnames -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod from sentry_sdk import capture_message from pay_queue.minio import get_object @@ -103,6 +103,10 @@ def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-many-loc has_eft_transaction_errors = False + # Include only transactions that are eft or has an error - ignore non EFT + eft_transactions = [transaction for transaction in eft_transactions + if transaction.has_errors() or transaction.is_eft] + # Parse EFT Transactions for eft_transaction in eft_transactions: if eft_transaction.has_errors(): # Flag any instance of an error - will indicate file is partially processed @@ -189,14 +193,6 @@ def _process_eft_credits(shortname_balance, eft_file_id): for shortname in shortname_balance.keys(): try: eft_shortname = _get_shortname(shortname) - payment_account_id = None - - # Get payment account if short name is mapped to an auth account - if eft_shortname.auth_account_id is not None: - payment_account: PaymentAccountModel = PaymentAccountModel.\ - find_by_auth_account_id(eft_shortname.auth_account_id) - payment_account_id = payment_account.id - eft_transactions = shortname_balance[shortname]['transactions'] for eft_transaction in eft_transactions: @@ -216,7 +212,6 @@ def _process_eft_credits(shortname_balance, eft_file_id): continue eft_credit_model.eft_file_id = eft_file_id - eft_credit_model.payment_account_id = payment_account_id eft_credit_model.short_name_id = eft_shortname.id eft_credit_model.amount = deposit_amount eft_credit_model.remaining_amount = deposit_amount @@ -236,6 +231,16 @@ def _process_eft_payments(shortname_balance: Dict, eft_file: EFTFileModel) -> bo for shortname in shortname_balance.keys(): # Retrieve or Create shortname mapping eft_shortname_model = _get_shortname(shortname) + shortname_links = EFTShortnames.get_shortname_links(eft_shortname_model.id) + + # Skip short names with no links or multiple links (manual processing by staff) + if not shortname_links['items'] or len(shortname_links['items']) > 1: + continue + + eft_short_link = shortname_links['items'][0] + # Skip if the link is in pending status - this will be officially linked via a scheduled job + if eft_short_link['status_code'] == EFTShortnameStatus.PENDING.value: + continue # No balance to apply - move to next shortname if shortname_balance[shortname]['balance'] <= 0: @@ -243,20 +248,18 @@ def _process_eft_payments(shortname_balance: Dict, eft_file: EFTFileModel) -> bo shortname, eft_file.file_ref) continue - # check if short name is mapped to an auth account - if eft_shortname_model is not None and eft_shortname_model.auth_account_id is not None: - # We have a mapping and can continue processing - try: - auth_account_id = eft_shortname_model.auth_account_id - # Find invoices to be paid - invoices: List[InvoiceModel] = EFTShortnames.get_invoices_owing(auth_account_id) - for invoice in invoices: - _pay_invoice(invoice=invoice, shortname_balance=shortname_balance[shortname]) - - except Exception as e: # NOQA pylint: disable=broad-exception-caught - has_eft_transaction_errors = True - current_app.logger.error(e) - capture_message('EFT Failed to apply balance to invoice.', level='error') + # We have a mapping and can continue processing + try: + auth_account_id = eft_short_link['account_id'] + # Find invoices to be paid + invoices: List[InvoiceModel] = EFTShortnames.get_invoices_owing(auth_account_id) + for invoice in invoices: + _pay_invoice(invoice=invoice, shortname_balance=shortname_balance[shortname]) + + except Exception as e: # NOQA pylint: disable=broad-exception-caught + has_eft_transaction_errors = True + current_app.logger.error(e) + capture_message('EFT Failed to apply balance to invoice.', level='error') return has_eft_transaction_errors diff --git a/pay-queue/src/pay_queue/services/eft/eft_record.py b/pay-queue/src/pay_queue/services/eft/eft_record.py index 4c002754e..89c483f80 100644 --- a/pay-queue/src/pay_queue/services/eft/eft_record.py +++ b/pay-queue/src/pay_queue/services/eft/eft_record.py @@ -24,6 +24,9 @@ class EFTRecord(EFTBase): """Defines the structure of the transaction record of a received EFT file.""" + PAD_DESCRIPTION_PATTERN = 'MISC PAYMENT BCONLINE' + EFT_DESCRIPTION_PATTERN = 'MISC PAYMENT' + ministry_code: str program_code: str location_id: str @@ -39,6 +42,7 @@ class EFTRecord(EFTBase): jv_type: str # I = inter, J = intra; mandatory if JV batch specified jv_number: str # mandatory if JV batch specified transaction_date: datetime # optional + is_eft: bool = False # Used to help with filtering transactions as not all transactions will necessary be EFT def __init__(self, content: str, index: int): """Return an EFT Transaction record.""" @@ -66,7 +70,11 @@ def _process(self): self.ministry_code = self.extract_value(1, 3) self.program_code = self.extract_value(3, 7) - self.deposit_datetime = self.parse_datetime(self.extract_value(7, 15) + self.extract_value(20, 24), + + deposit_time = self.extract_value(20, 24) + deposit_time = '0000' if len(deposit_time) == 0 else deposit_time # default to 0000 if time not provided + + self.deposit_datetime = self.parse_datetime(self.extract_value(7, 15) + deposit_time, EFTError.INVALID_DEPOSIT_DATETIME) self.location_id = self.extract_value(15, 20) self.transaction_sequence = self.extract_value(24, 27) @@ -75,6 +83,7 @@ def _process(self): self.transaction_description = self.extract_value(27, 67) if len(self.transaction_description) == 0: self.add_error(EFTParseError(EFTError.ACCOUNT_SHORTNAME_REQUIRED)) + self.parse_transaction_description() self.deposit_amount = self.parse_decimal(self.extract_value(67, 80), EFTError.INVALID_DEPOSIT_AMOUNT) self.currency = self.get_currency(self.extract_value(80, 82)) @@ -89,3 +98,14 @@ def _process(self): transaction_date = self.extract_value(131, 139) self.transaction_date = None if len(transaction_date) == 0 \ else self.parse_date(transaction_date, EFTError.INVALID_TRANSACTION_DATE) + + def parse_transaction_description(self): + """Determine if the transaction is an EFT and parse it.""" + if not self.transaction_description: + return + + # Check if this a PAD or EFT Transaction - ignore non EFT Transactions + if self.transaction_description.startswith(self.EFT_DESCRIPTION_PATTERN) \ + and not self.transaction_description.startswith(self.PAD_DESCRIPTION_PATTERN): + self.is_eft = True + self.transaction_description = self.transaction_description[len(self.EFT_DESCRIPTION_PATTERN):].strip() diff --git a/pay-queue/src/pay_queue/version.py b/pay-queue/src/pay_queue/version.py index 19ea931f0..3b723bf04 100644 --- a/pay-queue/src/pay_queue/version.py +++ b/pay-queue/src/pay_queue/version.py @@ -22,4 +22,4 @@ Development release segment: .devN """ -__version__ = '2.0.0' # pylint: disable=invalid-name +__version__ = '2.0.1' # pylint: disable=invalid-name diff --git a/pay-queue/tests/integration/test_eft_reconciliation.py b/pay-queue/tests/integration/test_eft_reconciliation.py index 5c7e9d081..114ae89e2 100644 --- a/pay-queue/tests/integration/test_eft_reconciliation.py +++ b/pay-queue/tests/integration/test_eft_reconciliation.py @@ -23,11 +23,12 @@ from pay_api.models import EFTCredit as EFTCreditModel from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel from pay_api.models import EFTFile as EFTFileModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTShortnames as EFTShortnameModel from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, MessageType, PaymentMethod +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, MessageType, PaymentMethod from pay_queue.services.eft.eft_enums import EFTConstants from tests.integration.factory import factory_create_eft_account, factory_invoice @@ -250,9 +251,13 @@ def test_eft_tdi17_basic_process(client): assert eft_shortnames is not None assert len(eft_shortnames) == 2 - assert eft_shortnames[0].auth_account_id is None + + short_name_link_1 = EFTShortnameLinksModel.find_by_short_name_id(eft_shortnames[0].id) + short_name_link_2 = EFTShortnameLinksModel.find_by_short_name_id(eft_shortnames[1].id) + + assert not short_name_link_1 assert eft_shortnames[0].short_name == 'ABC123' - assert eft_shortnames[1].auth_account_id is None + assert not short_name_link_2 assert eft_shortnames[1].short_name == 'DEF456' eft_credits: List[EFTCreditModel] = db.session.query(EFTCreditModel).order_by(EFTCreditModel.created_on.asc()).all() @@ -325,12 +330,15 @@ def test_eft_tdi17_process(client): assert eft_transactions[2].short_name_id is not None eft_shortnames = db.session.query(EFTShortnameModel).all() + short_name_link_1: EFTShortnameLinksModel = EFTShortnameLinksModel.find_by_short_name_id(eft_shortnames[0].id)[0] + short_name_link_2: EFTShortnameLinksModel = EFTShortnameLinksModel.find_by_short_name_id(eft_shortnames[1].id) assert eft_shortnames is not None assert len(eft_shortnames) == 2 - assert eft_shortnames[0].auth_account_id == eft_shortname.auth_account_id + assert short_name_link_1 + assert short_name_link_1.auth_account_id == payment_account.auth_account_id assert eft_shortnames[0].short_name == 'TESTSHORTNAME' - assert eft_shortnames[1].auth_account_id is None + assert not short_name_link_2 assert eft_shortnames[1].short_name == 'ABC123' # NOTE THIS NEEDS TO BE RE-WRITTEN INSIDE OF THE JOB. @@ -416,7 +424,7 @@ def test_eft_tdi17_rerun(client): transaction_1 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='001', - transaction_description='TESTSHORTNAME', deposit_amount='13500', + transaction_description='MISC PAYMENT TESTSHORTNAME', deposit_amount='13500', currency='', exchange_adj_amount='0', deposit_amount_cad='FAIL', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') @@ -456,7 +464,7 @@ def test_eft_tdi17_rerun(client): transaction_1 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='001', - transaction_description='TESTSHORTNAME', deposit_amount='13500', + transaction_description='MISC PAYMENT TESTSHORTNAME', deposit_amount='13500', currency='', exchange_adj_amount='0', deposit_amount_cad='13500', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') @@ -545,8 +553,15 @@ def test_eft_tdi17_rerun(client): def create_test_data(): """Create test seed data.""" payment_account: PaymentAccountModel = factory_create_eft_account() - eft_shortname: EFTShortnameModel = EFTShortnameModel(short_name='TESTSHORTNAME', - auth_account_id=payment_account.auth_account_id).save() + eft_shortname: EFTShortnameModel = EFTShortnameModel(short_name='TESTSHORTNAME').save() + EFTShortnameLinksModel( + eft_short_name_id=eft_shortname.id, + auth_account_id=payment_account.auth_account_id, + status_code=EFTShortnameStatus.LINKED.value, + updated_by='IDIR/JSMITH', + updated_by_name='IDIR/JSMITH', + updated_on=datetime.now() + ).save() invoice: InvoiceModel = factory_invoice(payment_account=payment_account, total=100, service_fees=10.0, payment_method_code=PaymentMethod.EFT.value) @@ -565,21 +580,41 @@ def generate_basic_tdi17_file(file_name: str): transaction_1 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='001', - transaction_description='ABC123', deposit_amount='13500', + transaction_description='MISC PAYMENT ABC123', deposit_amount='13500', currency='', exchange_adj_amount='0', deposit_amount_cad='13500', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') transaction_2 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', - program_code='0146', deposit_date='20230810', deposit_time='0000', + program_code='0146', deposit_date='20230810', deposit_time='', location_id='85004', transaction_sequence='002', - transaction_description='DEF456', + transaction_description='MISC PAYMENT DEF456', deposit_amount='525000', currency='', exchange_adj_amount='0', deposit_amount_cad='525000', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') - create_and_upload_eft_file(file_name, [header, transaction_1, transaction_2, trailer]) + transaction_3 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', + program_code='0146', deposit_date='20230810', deposit_time='0000', + location_id='85004', transaction_sequence='003', + transaction_description='SHOULDIGNORE', + deposit_amount='525000', currency='', exchange_adj_amount='0', + deposit_amount_cad='525000', destination_bank_number='0003', + batch_number='002400986', jv_type='I', jv_number='002425669', + transaction_date='') + + transaction_4 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', + program_code='0146', deposit_date='20230810', deposit_time='0000', + location_id='85004', transaction_sequence='004', + transaction_description='MISC PAYMENT BCONLINE SHOULDIGNORE', + deposit_amount='525000', currency='', exchange_adj_amount='0', + deposit_amount_cad='525000', destination_bank_number='0003', + batch_number='002400986', jv_type='I', jv_number='002425669', + transaction_date='') + + create_and_upload_eft_file(file_name, [header, + transaction_1, transaction_2, transaction_3, transaction_4, + trailer]) def generate_tdi17_file(file_name: str): @@ -593,15 +628,15 @@ def generate_tdi17_file(file_name: str): transaction_1 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='001', - transaction_description='TESTSHORTNAME', deposit_amount='10000', + transaction_description='MISC PAYMENT TESTSHORTNAME', deposit_amount='10000', currency='', exchange_adj_amount='0', deposit_amount_cad='10000', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') transaction_2 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', - program_code='0146', deposit_date='20230810', deposit_time='0000', + program_code='0146', deposit_date='20230810', deposit_time='', location_id='85004', transaction_sequence='002', - transaction_description='TESTSHORTNAME', + transaction_description='MISC PAYMENT TESTSHORTNAME', deposit_amount='5050', currency='', exchange_adj_amount='0', deposit_amount_cad='5050', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', @@ -610,9 +645,20 @@ def generate_tdi17_file(file_name: str): transaction_3 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', program_code='0146', deposit_date='20230810', deposit_time='0000', location_id='85004', transaction_sequence='003', - transaction_description='ABC123', deposit_amount='35150', + transaction_description='MISC PAYMENT ABC123', deposit_amount='35150', currency='', exchange_adj_amount='0', deposit_amount_cad='35150', destination_bank_number='0003', batch_number='002400986', jv_type='I', jv_number='002425669', transaction_date='') - create_and_upload_eft_file(file_name, [header, transaction_1, transaction_2, transaction_3, trailer]) + transaction_4 = factory_eft_record(record_type=EFTConstants.TRANSACTION_RECORD_TYPE.value, ministry_code='AT', + program_code='0146', deposit_date='20230810', deposit_time='0000', + location_id='85004', transaction_sequence='004', + transaction_description='MISC PAYMENT BCONLINE SHOULDIGNORE', + deposit_amount='525000', currency='', exchange_adj_amount='0', + deposit_amount_cad='525000', destination_bank_number='0003', + batch_number='002400986', jv_type='I', jv_number='002425669', + transaction_date='') + + create_and_upload_eft_file(file_name, [header, + transaction_1, transaction_2, transaction_3, transaction_4, + trailer]) diff --git a/pay-queue/tests/unit/test_eft_file_parser.py b/pay-queue/tests/unit/test_eft_file_parser.py index fd667674f..73d27124e 100644 --- a/pay-queue/tests/unit/test_eft_file_parser.py +++ b/pay-queue/tests/unit/test_eft_file_parser.py @@ -555,6 +555,7 @@ def test_eft_parse_file(): assert eft_records[0].jv_type == 'I' assert eft_records[0].jv_number == '002425669' assert eft_records[0].transaction_date is None + assert not eft_records[0].is_eft assert eft_records[1].index == 2 assert eft_records[1].record_type == '2' @@ -573,6 +574,7 @@ def test_eft_parse_file(): assert eft_records[1].jv_type == 'I' assert eft_records[1].jv_number == '002425669' assert eft_records[1].transaction_date is None + assert not eft_records[1].is_eft assert eft_records[2].index == 3 assert eft_records[2].record_type == '2' @@ -591,6 +593,7 @@ def test_eft_parse_file(): assert eft_records[2].jv_type == 'I' assert eft_records[2].jv_number == '002425669' assert eft_records[2].transaction_date is None + assert not eft_records[2].is_eft assert eft_records[3].index == 4 assert eft_records[3].record_type == '2' @@ -609,6 +612,7 @@ def test_eft_parse_file(): assert eft_records[3].jv_type == 'I' assert eft_records[3].jv_number == '002425669' assert eft_records[3].transaction_date is None + assert not eft_records[3].is_eft assert eft_records[4].index == 5 assert eft_records[4].record_type == '2' @@ -627,3 +631,4 @@ def test_eft_parse_file(): assert eft_records[4].jv_type == 'I' assert eft_records[4].jv_number == '002425836' assert eft_records[4].transaction_date is None + assert not eft_records[4].is_eft diff --git a/pay-queue/tests/utilities/factory_utils.py b/pay-queue/tests/utilities/factory_utils.py index d8047880a..f493b3b88 100644 --- a/pay-queue/tests/utilities/factory_utils.py +++ b/pay-queue/tests/utilities/factory_utils.py @@ -47,7 +47,8 @@ def factory_eft_record(record_type: str, ministry_code: str, program_code: str, exchange_adj_amount = transform_money_string(exchange_adj_amount) deposit_amount_cad = transform_money_string(deposit_amount_cad) - result = f'{record_type}{ministry_code}{program_code}{deposit_date}{location_id}{deposit_time}' \ + result = f'{record_type}{ministry_code}{program_code}{deposit_date}{location_id}' \ + f'{right_pad_space(deposit_time, 4)}' \ f'{transaction_sequence}{right_pad_space(transaction_description, 40)}' \ f'{left_pad_zero(deposit_amount, 13)}{right_pad_space(currency, 2)}' \ f'{left_pad_zero(exchange_adj_amount, 13)}{left_pad_zero(deposit_amount_cad, 13)}' \ From 031104546edc3c471f0d00bdbbf9c2025d468557 Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Tue, 23 Apr 2024 08:49:38 -0700 Subject: [PATCH 53/87] 20454 - Pay-queue JWT Verification (#1491) --- pay-queue/devops/vaults.gcp.env | 5 +++- pay-queue/src/pay_queue/config.py | 3 +++ pay-queue/src/pay_queue/external/gcp_auth.py | 26 +++++++++++--------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/pay-queue/devops/vaults.gcp.env b/pay-queue/devops/vaults.gcp.env index 08cac8d26..5bfb8a754 100644 --- a/pay-queue/devops/vaults.gcp.env +++ b/pay-queue/devops/vaults.gcp.env @@ -16,10 +16,13 @@ EFT_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/eft/EFT_INVOICE_PREF AUDIENCE="op://gcp-queue/$APP_ENV/base/AUDIENCE" GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/base/GCP_AUTH_KEY" PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" +PAY_SUB_AUDIENCE="op://gcp-queue/$APP_ENV/base/PAY_SUB_AUDIENCE" +VERIFY_PUBSUB_EMAIL="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_EMAIL" +VERIFY_PUBSUB_VIA_JWT="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_VIA_JWT" ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" LEGISLATIVE_TIMEZONE="op://relationship/$APP_ENV/pay-api/LEGISLATIVE_TIMEZONE" ACCOUNT_SECRET_KEY="op://relationship/$APP_ENV/pay-api/ACCOUNT_SECRET_KEY" DISABLE_EJV_ERROR_EMAIL="True" -DISABLE_PAD_SUCCESS_EMAIL="False" \ No newline at end of file +DISABLE_PAD_SUCCESS_EMAIL="False" diff --git a/pay-queue/src/pay_queue/config.py b/pay-queue/src/pay_queue/config.py index 9acc00a49..2b2a07ab4 100644 --- a/pay-queue/src/pay_queue/config.py +++ b/pay-queue/src/pay_queue/config.py @@ -107,6 +107,9 @@ class _Config(): # pylint: disable=too-few-public-methods GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) + PAY_SUB_AUDIENCE = os.getenv('PAY_SUB_AUDIENCE', None) + VERIFY_PUBSUB_EMAIL = os.getenv('VERIFY_PUBSUB_EMAIL', None) + VERIFY_PUBSUB_VIA_JWT = os.getenv('VERIFY_PUBSUB_VIA_JWT', None) class DevConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/pay-queue/src/pay_queue/external/gcp_auth.py b/pay-queue/src/pay_queue/external/gcp_auth.py index 39121ede2..81ac57ba5 100644 --- a/pay-queue/src/pay_queue/external/gcp_auth.py +++ b/pay-queue/src/pay_queue/external/gcp_auth.py @@ -13,19 +13,21 @@ def verify_jwt(session): - """Verify token is valid.""" - msg = '' + """Check token is valid with the correct audience and email claims for configured email address.""" try: - # Get the Cloud Pub/Sub-generated JWT in the "Authorization" header. - id_token.verify_oauth2_token( - request.headers.get('Authorization').split()[1], + jwt_token = request.headers.get('Authorization', '').split()[1] + claims = id_token.verify_oauth2_token( + jwt_token, Request(session=session), audience=current_app.config.get('PAY_SUB_AUDIENCE') ) - except Exception as e: # TODO fix - msg = f'Invalid token: {e}\n' - finally: - return msg + # Check if the email is verified and matches the configured email + required_email = current_app.config.get('VERIFY_PUBSUB_EMAIL') + if not claims.get('email_verified') or claims.get('email') != required_email: + return 'Email not verified or does not match', 401 + except Exception as e: + return f'Invalid token: {e}', 400 + return None def ensure_authorized_queue_user(f): @@ -33,8 +35,8 @@ def ensure_authorized_queue_user(f): @functools.wraps(f) def decorated_function(*args, **kwargs): # Use CacheControl to avoid re-fetching certificates for every request. - if message := verify_jwt(CacheControl(Session())): - print(message) - abort(HTTPStatus.UNAUTHORIZED) + if current_app.config.get('VERIFY_PUBSUB_VIA_JWT', True): + if message := verify_jwt(CacheControl(Session())): + abort(HTTPStatus.UNAUTHORIZED) return f(*args, **kwargs) return decorated_function From a091d6a1a80533adbc6ced03e4c0d6f61dd6ad8e Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Tue, 23 Apr 2024 15:10:52 -0700 Subject: [PATCH 54/87] 20454 - Fix a jwt condition check issue (#1492) * just for test * pubsub jwt verification * remove extra code * add back DISABLE_PAD_SUCCESS_EMAIL, it deleted by incidence --- pay-queue/poetry.lock | 139 ++++++++++--------- pay-queue/src/pay_queue/external/gcp_auth.py | 3 +- 2 files changed, 72 insertions(+), 70 deletions(-) diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index d053b1e55..28dfadd78 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "alembic" @@ -408,63 +408,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.4" +version = "7.5.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, - {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, - {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, - {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, - {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, - {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, - {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, - {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, - {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, - {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, - {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, - {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, - {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, - {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, + {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, + {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, + {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, + {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, + {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, + {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, + {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, + {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, + {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, + {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, + {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, + {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, + {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, + {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, ] [package.extras] @@ -738,7 +738,7 @@ Flask = ">=0.9" [[package]] name = "flask_jwt_oidc" -version = "0.3.0" +version = "0.5.0" description = "Flask JWT OIDC" optional = false python-versions = "*" @@ -755,7 +755,7 @@ six = "*" type = "git" url = "https://github.com/thorwolpert/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" +resolved_reference = "fc60d430a69e0d59e1d659d01aff78151ba498d0" [[package]] name = "flask-marshmallow" @@ -856,13 +856,13 @@ sqlalchemy = ">=2.0.16" [[package]] name = "freezegun" -version = "1.4.0" +version = "1.5.0" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, - {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, + {file = "freezegun-1.5.0-py3-none-any.whl", hash = "sha256:ec3f4ba030e34eb6cf7e1e257308aee2c60c3d038ff35996d7475760c9ff3719"}, + {file = "freezegun-1.5.0.tar.gz", hash = "sha256:200a64359b363aa3653d8aac289584078386c7c3da77339d257e46a01fb5c77c"}, ] [package.dependencies] @@ -1651,7 +1651,7 @@ werkzeug = "3.0.1" type = "git" url = "https://github.com/bcgov/sbc-pay.git" reference = "feature-queue-python-upgrade" -resolved_reference = "ba20cecf7e65065fa22dbfedb0f0bb5c1ee7ec94" +resolved_reference = "031104546edc3c471f0d00bdbbf9c2025d468557" subdirectory = "pay-api" [[package]] @@ -1685,28 +1685,29 @@ scramp = ">=1.4.4" [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -2280,7 +2281,7 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +resolved_reference = "8871ffcce8cc2232a5d7a3adb6103dfaf0d7689f" subdirectory = "python" [[package]] @@ -2425,7 +2426,7 @@ develop = false type = "git" url = "https://github.com/bcgov/lear.git" reference = "feature-legal-name" -resolved_reference = "e7d04a5b6a6b7fd278a4a445f8b6303583f68aec" +resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" subdirectory = "python/common/sql-versioning" [[package]] diff --git a/pay-queue/src/pay_queue/external/gcp_auth.py b/pay-queue/src/pay_queue/external/gcp_auth.py index 81ac57ba5..e6e180f9f 100644 --- a/pay-queue/src/pay_queue/external/gcp_auth.py +++ b/pay-queue/src/pay_queue/external/gcp_auth.py @@ -35,7 +35,8 @@ def ensure_authorized_queue_user(f): @functools.wraps(f) def decorated_function(*args, **kwargs): # Use CacheControl to avoid re-fetching certificates for every request. - if current_app.config.get('VERIFY_PUBSUB_VIA_JWT', True): + config_value = current_app.config.get('VERIFY_PUBSUB_VIA_JWT', True) + if config_value is True: if message := verify_jwt(CacheControl(Session())): abort(HTTPStatus.UNAUTHORIZED) return f(*args, **kwargs) From e36c4defeed9b16b9105e841fe15f1d5a296203b Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Tue, 23 Apr 2024 20:25:56 -0700 Subject: [PATCH 55/87] 20454 - Add test logs (#1493) --- pay-queue/devops/vaults.gcp.env | 1 + pay-queue/src/pay_queue/config.py | 4 +++- pay-queue/src/pay_queue/external/gcp_auth.py | 7 +++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pay-queue/devops/vaults.gcp.env b/pay-queue/devops/vaults.gcp.env index 5bfb8a754..fa6f68780 100644 --- a/pay-queue/devops/vaults.gcp.env +++ b/pay-queue/devops/vaults.gcp.env @@ -19,6 +19,7 @@ PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" PAY_SUB_AUDIENCE="op://gcp-queue/$APP_ENV/base/PAY_SUB_AUDIENCE" VERIFY_PUBSUB_EMAIL="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_EMAIL" VERIFY_PUBSUB_VIA_JWT="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_VIA_JWT" +DEBUG_REQUEST="op://gcp-queue/$APP_ENV/base/DEBUG_REQUEST" ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" diff --git a/pay-queue/src/pay_queue/config.py b/pay-queue/src/pay_queue/config.py index 2b2a07ab4..cbceb71f4 100644 --- a/pay-queue/src/pay_queue/config.py +++ b/pay-queue/src/pay_queue/config.py @@ -109,7 +109,9 @@ class _Config(): # pylint: disable=too-few-public-methods ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) PAY_SUB_AUDIENCE = os.getenv('PAY_SUB_AUDIENCE', None) VERIFY_PUBSUB_EMAIL = os.getenv('VERIFY_PUBSUB_EMAIL', None) - VERIFY_PUBSUB_VIA_JWT = os.getenv('VERIFY_PUBSUB_VIA_JWT', None) + + VERIFY_PUBSUB_VIA_JWT = os.getenv('VERIFY_PUBSUB_VIA_JWT', 'true').lower() == 'true' + VERIFY_PUBSUB_VIA_JWT = os.getenv('DEBUG_REQUEST', 'true').lower() == 'true' class DevConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/pay-queue/src/pay_queue/external/gcp_auth.py b/pay-queue/src/pay_queue/external/gcp_auth.py index e6e180f9f..31f575ac6 100644 --- a/pay-queue/src/pay_queue/external/gcp_auth.py +++ b/pay-queue/src/pay_queue/external/gcp_auth.py @@ -35,8 +35,11 @@ def ensure_authorized_queue_user(f): @functools.wraps(f) def decorated_function(*args, **kwargs): # Use CacheControl to avoid re-fetching certificates for every request. - config_value = current_app.config.get('VERIFY_PUBSUB_VIA_JWT', True) - if config_value is True: + if current_app.config.get('DEBUG_REQUEST') is True: + current_app.logger.info(f'Headers: {request.headers}') + verifyJWT = current_app.config.get('VERIFY_PUBSUB_VIA_JWT', True) + current_app.logger.info(f'verifyJWT: {verifyJWT}') + if verifyJWT is True: if message := verify_jwt(CacheControl(Session())): abort(HTTPStatus.UNAUTHORIZED) return f(*args, **kwargs) From b03d58658083fe920e10c19289845d9d699da572 Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Thu, 25 Apr 2024 10:37:25 -0700 Subject: [PATCH 56/87] 20414 - EFT Short name summaries search (#1494) * 20414 - EFT Short name summaries search * PR feedback --- pay-api/src/pay_api/models/__init__.py | 2 +- pay-api/src/pay_api/models/eft_short_names.py | 24 ++ .../pay_api/resources/v1/eft_short_names.py | 34 ++- pay-api/src/pay_api/services/__init__.py | 1 + .../services/eft_short_name_summaries.py | 134 ++++++++++ .../src/pay_api/services/eft_short_names.py | 6 +- pay-api/src/pay_api/utils/util.py | 25 ++ pay-api/src/pay_api/version.py | 2 +- pay-api/tests/unit/api/test_cors_preflight.py | 5 + .../tests/unit/api/test_eft_short_names.py | 252 ++++++++++++++++-- 10 files changed, 461 insertions(+), 24 deletions(-) create mode 100644 pay-api/src/pay_api/services/eft_short_name_summaries.py diff --git a/pay-api/src/pay_api/models/__init__.py b/pay-api/src/pay_api/models/__init__.py index 4d2e1d861..31ba7d754 100755 --- a/pay-api/src/pay_api/models/__init__.py +++ b/pay-api/src/pay_api/models/__init__.py @@ -32,7 +32,7 @@ from .eft_file import EFTFile from .eft_gl_transfers import EFTGLTransfer from .eft_process_status_code import EFTProcessStatusCode -from .eft_short_names import EFTShortnames, EFTShortnameSchema +from .eft_short_names import EFTShortnames, EFTShortnameSchema, EFTShortnameSummarySchema from .eft_short_name_links import EFTShortnameLinks, EFTShortnameLinkSchema from .eft_transaction import EFTTransaction, EFTTransactionSchema from .ejv_file import EjvFile diff --git a/pay-api/src/pay_api/models/eft_short_names.py b/pay-api/src/pay_api/models/eft_short_names.py index 027aad346..6e4af5a2e 100644 --- a/pay-api/src/pay_api/models/eft_short_names.py +++ b/pay-api/src/pay_api/models/eft_short_names.py @@ -96,3 +96,27 @@ def from_row(cls, row: EFTShortnames): updated_by_name=getattr(row, 'updated_by_name'), updated_on=getattr(row, 'updated_on') ) + + +@define +class EFTShortnameSummarySchema: + """Main schema used to serialize the EFT Short name summaries.""" + + id: int + short_name: str + last_payment_received_date: datetime + credits_remaining: Decimal + linked_accounts_count: int + + @classmethod + def from_row(cls, row: EFTShortnames): + """From row is used so we don't tightly couple to our database class. + + https://www.attrs.org/en/stable/init.html + """ + return cls(id=row.id, + short_name=row.short_name, + last_payment_received_date=getattr(row, 'last_payment_received_date', None), + credits_remaining=getattr(row, 'credits_remaining', None), + linked_accounts_count=getattr(row, 'linked_accounts_count', None) + ) diff --git a/pay-api/src/pay_api/resources/v1/eft_short_names.py b/pay-api/src/pay_api/resources/v1/eft_short_names.py index 5e2fbc9f3..432dffecf 100644 --- a/pay-api/src/pay_api/resources/v1/eft_short_names.py +++ b/pay-api/src/pay_api/resources/v1/eft_short_names.py @@ -20,14 +20,14 @@ from pay_api.exceptions import BusinessException from pay_api.services.eft_short_names import EFTShortnames as EFTShortnameService +from pay_api.services.eft_short_name_summaries import EFTShortnameSummaries as EFTShortnameSummariesService from pay_api.services.eft_short_names import EFTShortnamesSearch from pay_api.services.eft_transactions import EFTTransactions as EFTTransactionService from pay_api.services.eft_transactions import EFTTransactionSearch from pay_api.utils.auth import jwt as _jwt from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role -from pay_api.utils.util import string_to_date - +from pay_api.utils.util import string_to_date, string_to_decimal, string_to_int bp = Blueprint('EFT_SHORT_NAMES', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/eft-shortnames') @@ -73,6 +73,36 @@ def get_eft_shortnames(): return jsonify(response), status +@bp.route('/summaries', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_jwt.requires_auth +@_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) +def get_eft_shortname_summaries(): + """Get all eft short name summaries.""" + current_app.logger.info('get_eft_shortname_summaries') + return jsonify(response), status + + @bp.route('/', methods=['GET', 'OPTIONS']) @cross_origin(origins='*', methods=['GET']) @_jwt.requires_auth diff --git a/pay-api/src/pay_api/services/__init__.py b/pay-api/src/pay_api/services/__init__.py index b56e840dd..9dfd15cef 100755 --- a/pay-api/src/pay_api/services/__init__.py +++ b/pay-api/src/pay_api/services/__init__.py @@ -17,6 +17,7 @@ from .distribution_code import DistributionCode from .eft_service import EftService from .eft_short_names import EFTShortnames as EFTShortNamesService +from .eft_short_name_summaries import EFTShortnameSummaries as EFTShortNameSummaryService from .eft_gl_transfer import EFTGlTransfer from .fee_schedule import FeeSchedule from .hashing import HashingService diff --git a/pay-api/src/pay_api/services/eft_short_name_summaries.py b/pay-api/src/pay_api/services/eft_short_name_summaries.py new file mode 100644 index 000000000..52ed6d707 --- /dev/null +++ b/pay-api/src/pay_api/services/eft_short_name_summaries.py @@ -0,0 +1,134 @@ +# Copyright © 2024 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Service to provide summarized information for EFT Short names.""" +from __future__ import annotations + +from flask import current_app +from sqlalchemy import and_, func, or_ + +from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel +from pay_api.models import EFTShortnames as EFTShortnameModel +from pay_api.models import EFTShortnameSummarySchema as EFTSummarySchema +from pay_api.models import EFTTransaction as EFTTransactionModel +from pay_api.models import db +from pay_api.services.eft_short_names import EFTShortnamesSearch +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus +from pay_api.utils.util import unstructure_schema_items + + +class EFTShortnameSummaries: + """Service to provide summarized information for EFT Short names.""" + + @classmethod + def search(cls, search_criteria: EFTShortnamesSearch): + """Search eft short name summaries.""" + current_app.logger.debug('search') + return { + 'state_total': search_count, + 'page': search_criteria.page, + 'limit': search_criteria.limit, + 'items': summary_list, + 'total': pagination.total + } + + @staticmethod + def get_last_payment_received_query(): + """Query for EFT most recent transaction deposit date.""" + return db.session.query( + EFTTransactionModel.deposit_date, + EFTTransactionModel.short_name_id, + func.row_number().over(partition_by=EFTTransactionModel.short_name_id, + order_by=[EFTTransactionModel.deposit_date.desc(), + EFTTransactionModel.id]).label('rn') + ).filter(and_(EFTTransactionModel.short_name_id.isnot(None), + EFTTransactionModel.status_code == EFTProcessStatus.COMPLETED.value)) \ + .filter(EFTTransactionModel.line_type == EFTFileLineType.TRANSACTION.value) + + @staticmethod + def get_linked_count_query(): + """Query for EFT linked account count.""" + # pylint: disable=not-callable + return (db.session.query(EFTShortnameLinksModel.eft_short_name_id, + func.count( + EFTShortnameLinksModel.eft_short_name_id + ).label('count')) + .filter(EFTShortnameLinksModel.status_code.in_([EFTShortnameStatus.PENDING.value, + EFTShortnameStatus.LINKED.value])) + .group_by(EFTShortnameLinksModel.eft_short_name_id)) + + @staticmethod + def get_remaining_credit_query(): + """Query for EFT remaining credit amount.""" + return (db.session.query(EFTCreditModel.short_name_id, + func.sum(EFTCreditModel.remaining_amount).label('total')) + .group_by(EFTCreditModel.short_name_id)) + + @staticmethod + def get_search_count(): + """Get a total count of short name summary results.""" + current_app.logger.debug('get_search_count') + return count_query.count() + + @classmethod + def get_search_query(cls, search_criteria: EFTShortnamesSearch): + """Query for short names based on search criteria.""" + linked_account_subquery = cls.get_linked_count_query().subquery() + credit_remaining_subquery = cls.get_remaining_credit_query().subquery() + last_payment_subquery = cls.get_last_payment_received_query().subquery() + + query = (db.session.query( + EFTShortnameModel.id, + EFTShortnameModel.short_name, + func.coalesce(linked_account_subquery.c.count, 0).label('linked_accounts_count'), + func.coalesce(credit_remaining_subquery.c.total, 0).label('credits_remaining'), + last_payment_subquery.c.deposit_date.label('last_payment_received_date') + ).outerjoin( + linked_account_subquery, + linked_account_subquery.c.eft_short_name_id == EFTShortnameModel.id + ).outerjoin( + credit_remaining_subquery, + credit_remaining_subquery.c.short_name_id == EFTShortnameModel.id + ).outerjoin( + last_payment_subquery, + and_(last_payment_subquery.c.short_name_id == EFTShortnameModel.id, last_payment_subquery.c.rn == 1) + )) + + query = query.filter_conditionally(search_criteria.id, EFTShortnameModel.id) + query = query.filter_conditionally(search_criteria.short_name, EFTShortnameModel.short_name, is_like=True) + query = query.filter_conditional_date_range(start_date=search_criteria.deposit_start_date, + end_date=search_criteria.deposit_end_date, + model_attribute=last_payment_subquery.c.deposit_date) + query = query.filter_conditionally(search_criteria.credit_remaining, credit_remaining_subquery.c.total) + + if search_criteria.linked_accounts_count == 0: + query = query.filter(or_(linked_account_subquery.c.count == 0, linked_account_subquery.c.count.is_(None))) + else: + query = query.filter_conditionally(search_criteria.linked_accounts_count, linked_account_subquery.c.count) + + query = query.order_by(last_payment_subquery.c.deposit_date.asc()) + return query diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index 6644ab260..881147668 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -54,6 +54,8 @@ class EFTShortnamesSearch: # pylint: disable=too-many-instance-attributes deposit_start_date: Optional[date] = None deposit_end_date: Optional[date] = None deposit_amount: Optional[Decimal] = None + credit_remaining: Optional[Decimal] = None + linked_accounts_count: Optional[int] = None short_name: Optional[str] = None state: Optional[List[str]] = None page: Optional[int] = 1 @@ -147,7 +149,7 @@ def get_invoices_owing(auth_account_id: str) -> [InvoiceModel]: def find_by_short_name_id(cls, short_name_id: int) -> EFTShortnames: """Find EFT short name by short name id.""" current_app.logger.debug(' Decimal: """Return rounded decimal. (Default = ROUND_HALF_EVEN).""" return Decimal(amount).quantize(Decimal('1.00')) @@ -265,3 +282,11 @@ def get_topic_for_corp_type(corp_type: str): return None case _: return current_app.config.get('BUSINESS_PAY_TOPIC') + + +def unstructure_schema_items(schema, items): + """Return unstructured results by schema.""" + results = [schema.from_row(item) for item in items] + converter = Converter() + + return converter.unstructure(results) diff --git a/pay-api/src/pay_api/version.py b/pay-api/src/pay_api/version.py index a0ad3cd84..a9327588f 100644 --- a/pay-api/src/pay_api/version.py +++ b/pay-api/src/pay_api/version.py @@ -22,4 +22,4 @@ Development release segment: .devN """ -__version__ = '1.20.10' # pylint: disable=invalid-name +__version__ = '1.20.11' # pylint: disable=invalid-name diff --git a/pay-api/tests/unit/api/test_cors_preflight.py b/pay-api/tests/unit/api/test_cors_preflight.py index a4b574161..67ee915a5 100644 --- a/pay-api/tests/unit/api/test_cors_preflight.py +++ b/pay-api/tests/unit/api/test_cors_preflight.py @@ -229,6 +229,11 @@ def test_preflight_eft_shortnames(app, client, jwt, session): assert rv.status_code == 200 assert_access_control_headers(rv, '*', 'GET') + rv = client.options('/api/v1/eft-shortnames/summaries', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + rv = client.options('/api/v1/eft-shortnames/1/links', headers={'Access-Control-Request-Method': 'GET'}) assert rv.status_code == 200 diff --git a/pay-api/tests/unit/api/test_eft_short_names.py b/pay-api/tests/unit/api/test_eft_short_names.py index 245d31873..e0bbbca8f 100755 --- a/pay-api/tests/unit/api/test_eft_short_names.py +++ b/pay-api/tests/unit/api/test_eft_short_names.py @@ -19,6 +19,7 @@ import json from datetime import datetime +from decimal import Decimal import pytest from flask import current_app @@ -135,25 +136,27 @@ def test_get_eft_short_name_links(session, client, jwt, app): assert link['updatedBy'] == 'IDIR/JSMITH' -def assert_short_name(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel, - expected_status: str): - """Assert short name result.""" +def assert_short_name_summary(result_dict: dict, + short_name: EFTShortnamesModel, + transaction: EFTTransactionModel, + expected_credits_remaining: Decimal, + expected_linked_accounts_count: int): + """Assert short name summary result.""" date_format = '%Y-%m-%dT%H:%M:%S' + assert result_dict['id'] == short_name.id assert result_dict['shortName'] == short_name.short_name - assert result_dict['statusCode'] == expected_status - assert result_dict['depositAmount'] == transaction.deposit_amount_cents / 100 - assert datetime.strptime(result_dict['depositDate'], date_format) == transaction.deposit_date - assert result_dict['transactionId'] == transaction.id - assert datetime.strptime(result_dict['transactionDate'], date_format) == transaction.transaction_date + assert result_dict['creditsRemaining'] == expected_credits_remaining + assert result_dict['linkedAccountsCount'] == expected_linked_accounts_count + assert datetime.strptime(result_dict['lastPaymentReceivedDate'], date_format) == transaction.deposit_date -def test_search_eft_short_names(session, client, jwt, app): - """Assert that EFT short names can be searched.""" +def test_eft_short_name_summaries(session, client, jwt, app): + """Assert that EFT short names summaries can be searched.""" token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} # Assert initial search returns empty items - rv = client.get('/api/v1/eft-shortnames', headers=headers) + rv = client.get('/api/v1/eft-shortnames/summaries', headers=headers) assert rv.status_code == 200 result_dict = rv.json @@ -162,11 +165,170 @@ def test_search_eft_short_names(session, client, jwt, app): assert len(result_dict['items']) == 0 # create test data - payment_account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, - auth_account_id='1234', - name='ABC-123', - branch_name='123').save() + factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1234', + name='ABC-123', + branch_name='123').save() + + short_name_1, s1_transaction1, short_name_2, s2_transaction1 = create_eft_search_data() + + # Assert short name search brings back both short names + rv = client.get('/api/v1/eft-shortnames/summaries?shortName=SHORT', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 2 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 2 + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) + assert_short_name_summary(result_dict['items'][1], + short_name_2, s2_transaction1, 302.5, 1) + + # Assert short name search brings back first short name + rv = client.get('/api/v1/eft-shortnames/summaries?shortName=name1', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 1 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 1 + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) + + # Assert search linked accounts count + rv = client.get('/api/v1/eft-shortnames/summaries?linkedAccountsCount=0', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 1 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 1 + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) + + rv = client.get('/api/v1/eft-shortnames/summaries?linkedAccountsCount=1', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 1 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 1 + assert_short_name_summary(result_dict['items'][0], + short_name_2, s2_transaction1, 302.5, 1) + + # Assert search by payment received date + rv = client.get('/api/v1/eft-shortnames/summaries?' + 'paymentReceivedStartDate=2024-01-16&paymentReceivedEndDate=2024-01-16', headers=headers) + assert rv.status_code == 200 + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 1 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 1 + assert_short_name_summary(result_dict['items'][0], + short_name_2, s2_transaction1, 302.5, 1) + + # Assert search by short name id + rv = client.get(f'/api/v1/eft-shortnames/summaries?shortNameId={short_name_2.id}', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 1 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 1 + assert_short_name_summary(result_dict['items'][0], + short_name_2, s2_transaction1, 302.5, 1) + + # Assert search by remaining credits + rv = client.get('/api/v1/eft-shortnames/summaries?creditsRemaining=204', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 1 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 1 + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) + + # Assert search query by no state will return all records + rv = client.get('/api/v1/eft-shortnames/summaries', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 2 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 2 + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) + assert_short_name_summary(result_dict['items'][1], + short_name_2, s2_transaction1, 302.5, 1) + + # Assert search pagination - page 1 works + rv = client.get('/api/v1/eft-shortnames/summaries?page=1&limit=1', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 2 + assert result_dict['limit'] == 1 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 1 + assert_short_name_summary(result_dict['items'][0], + short_name_1, s1_transaction1, 204.0, 0) + + # Assert search pagination - page 2 works + rv = client.get('/api/v1/eft-shortnames/summaries?page=2&limit=1', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 2 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 2 + assert result_dict['limit'] == 1 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 1 + assert_short_name_summary(result_dict['items'][0], + short_name_2, s2_transaction1, 302.5, 1) + + +def create_eft_search_data(): + """Create seed data for EFT searches.""" eft_file: EFTFileModel = factory_eft_file() short_name_1 = factory_eft_shortname(short_name='TESTSHORTNAME1').save() short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2').save() @@ -186,11 +348,16 @@ def test_search_eft_short_names(session, client, jwt, app): deposit_date=datetime(2024, 1, 6, 10, 5), deposit_amount_cents=10150, short_name_id=short_name_1.id - ).save() + EFTCreditModel(eft_file_id=eft_file.id, + short_name_id=s1_transaction1.short_name_id, + amount=s1_transaction1.deposit_amount_cents / 100, + remaining_amount=s1_transaction1.deposit_amount_cents / 100 + ).save() + # Identical to transaction 1 should not return duplicate short name rows - partitioned by transaction date, id - EFTTransactionModel( + s1_transaction2: EFTTransactionModel = EFTTransactionModel( line_type=EFTFileLineType.TRANSACTION.value, line_number=1, file_id=eft_file.id, @@ -202,13 +369,19 @@ def test_search_eft_short_names(session, client, jwt, app): ).save() + EFTCreditModel(eft_file_id=eft_file.id, + short_name_id=s1_transaction2.short_name_id, + amount=s1_transaction2.deposit_amount_cents / 100, + remaining_amount=s1_transaction2.deposit_amount_cents / 100 + ).save() + EFTTransactionModel( line_type=EFTFileLineType.TRANSACTION.value, line_number=1, file_id=eft_file.id, status_code=EFTProcessStatus.COMPLETED.value, transaction_date=datetime(2024, 1, 10, 2, 30), - deposit_date=datetime(2024, 1, 11, 10, 5), + deposit_date=datetime(2024, 1, 5, 10, 5), deposit_amount_cents=30150, short_name_id=short_name_1.id ).save() @@ -226,6 +399,49 @@ def test_search_eft_short_names(session, client, jwt, app): ).save() + EFTCreditModel(eft_file_id=eft_file.id, + short_name_id=s2_transaction1.short_name_id, + amount=s2_transaction1.deposit_amount_cents / 100, + remaining_amount=s2_transaction1.deposit_amount_cents / 100 + ).save() + + return short_name_1, s1_transaction1, short_name_2, s2_transaction1 + + +def assert_short_name(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel, + expected_status: str): + """Assert short name result.""" + date_format = '%Y-%m-%dT%H:%M:%S' + assert result_dict['shortName'] == short_name.short_name + assert result_dict['statusCode'] == expected_status + assert result_dict['depositAmount'] == transaction.deposit_amount_cents / 100 + assert datetime.strptime(result_dict['depositDate'], date_format) == transaction.deposit_date + assert result_dict['transactionId'] == transaction.id + assert datetime.strptime(result_dict['transactionDate'], date_format) == transaction.transaction_date + + +def test_search_eft_short_names(session, client, jwt, app): + """Assert that EFT short names can be searched.""" + token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) + headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + + # Assert initial search returns empty items + rv = client.get('/api/v1/eft-shortnames', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['items'] is not None + assert len(result_dict['items']) == 0 + + # create test data + payment_account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1234', + name='ABC-123', + branch_name='123').save() + + short_name_1, s1_transaction1, short_name_2, s2_transaction1 = create_eft_search_data() + # Assert search returns unlinked short names rv = client.get('/api/v1/eft-shortnames?state=UNLINKED', headers=headers) assert rv.status_code == 200 From 83a64f610628a2d3a1af84e2e57e4ce1f94857b1 Mon Sep 17 00:00:00 2001 From: Rodrigo Barraza Date: Tue, 30 Apr 2024 08:09:30 -0700 Subject: [PATCH 57/87] 19722 - Unlinking EFT Task (#1487) * Unlinking EFT * Flake fixes * Unlinking EFT test * Cleanup and fixes * Fixing tests * Updates and fixes * renaming function * Lint fixes * Flake8 fix * Test fix --- jobs/payment-jobs/invoke_jobs.py | 4 +- .../tasks/electronic_funds_transfer_task.py | 136 ++++++++++++++---- jobs/payment-jobs/tests/jobs/factory.py | 15 +- .../test_electronic_funds_transfer_task.py | 64 ++++++++- 4 files changed, 175 insertions(+), 44 deletions(-) diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index 55fef4ffe..ebbb7d567 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -132,7 +132,9 @@ def run(job_name, argument=None): RoutingSlipTask.adjust_routing_slips() application.logger.info(f'<<<< Completed Routing Slip tasks >>>>') elif job_name == 'EFT': - ElectronicFundsTransferTask.link_electronic_funds_transfer() + ElectronicFundsTransferTask.link_electronic_funds_transfers() + ElectronicFundsTransferTask.unlink_electronic_funds_transfers() + application.logger.info(f'<<<< Completed EFT tasks >>>>') elif job_name == 'EJV_PAYMENT': EjvPaymentTask.create_ejv_file() application.logger.info(f'<<<< Completed running EJV payment >>>>') diff --git a/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py b/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py index ce1983a88..73ff70168 100644 --- a/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py +++ b/jobs/payment-jobs/tasks/electronic_funds_transfer_task.py @@ -18,19 +18,21 @@ from flask import current_app from pay_api.models import CfsAccount as CfsAccountModel -from pay_api.models import EFTShortnames as EFTShortnameModel -from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel +from pay_api.models import EFTShortnames as EFTShortNamesModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import InvoiceReference as InvoiceReferenceModel -from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Payment as PaymentModel +from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models import PaymentLineItem as PaymentLineItemModel +from pay_api.models import Receipt as ReceiptModel from pay_api.models import db -from pay_api.services.cfs_service import CFSService from pay_api.services import EFTShortNamesService +from pay_api.services.cfs_service import CFSService from pay_api.services.receipt import Receipt -from pay_api.utils.enums import CfsAccountStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus -from pay_api.utils.util import generate_receipt_number +from pay_api.utils.enums import ( + CfsAccountStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus, ReverseOperation) from sentry_sdk import capture_message @@ -40,6 +42,7 @@ class EFTShortnameInfo: id: int auth_account_id: str + version: int class ElectronicFundsTransferTask: # pylint:disable=too-few-public-methods @@ -67,10 +70,11 @@ def link_electronic_funds_transfers(cls): payment = db.session.query(PaymentModel) \ .join(PaymentAccountModel, PaymentAccountModel.id == PaymentModel.payment_account_id) \ .join(EFTCreditModel, EFTCreditModel.payment_account_id == PaymentAccountModel.id) \ - .join(EFTShortnameModel, EFTShortnameModel.id == EFTCreditModel.short_name_id) \ - .filter(EFTShortnameModel.id == eft_short_name.id).first() + .join(EFTShortNamesModel, EFTShortNamesModel.id == EFTCreditModel.short_name_id) \ + .filter(EFTShortNamesModel.id == eft_short_name.id).first() + + receipt_number = payment.id - receipt_number = generate_receipt_number(payment.id) CFSService.create_cfs_receipt( cfs_account=cfs_account, rcpt_number=receipt_number, @@ -81,7 +85,7 @@ def link_electronic_funds_transfers(cls): # apply receipt to cfs_account total_invoice_amount = cls._apply_electronic_funds_transfers_to_pending_invoices( - eft_short_name, payment) + eft_short_name, receipt_number) current_app.logger.debug(f'Total Invoice Amount : {total_invoice_amount}') eft_credit.remaining_amount = eft_credit.amount - total_invoice_amount @@ -96,16 +100,62 @@ def link_electronic_funds_transfers(cls): continue @classmethod - def _get_eft_short_names_by_status(cls, status: str) -> List[EFTShortnameModel]: + def unlink_electronic_funds_transfers(cls): + """Unlink electronic funds transfers.""" + eft_short_names = cls._get_eft_short_names_by_status(EFTShortnameStatus.UNLINKED.value) + for eft_short_name in eft_short_names: + try: + current_app.logger.debug(f'Unlinking Electronic Funds Transfer: {eft_short_name.id}') + payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id( + eft_short_name.auth_account_id) + cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) + eft_credit: EFTCreditModel = EFTCreditModel.find_by_payment_account_id(payment_account.id) + + payment = db.session.query(PaymentModel) \ + .join(PaymentAccountModel, PaymentAccountModel.id == PaymentModel.payment_account_id) \ + .join(EFTCreditModel, EFTCreditModel.payment_account_id == PaymentAccountModel.id) \ + .join(EFTShortNamesModel, EFTShortNamesModel.id == EFTCreditModel.short_name_id) \ + .filter(EFTShortNamesModel.id == eft_short_name.id).first() + + eft_short_name.version += 1 + + receipt_number = f'{payment.id}R{eft_short_name.version}' + + CFSService.reverse_rs_receipt_in_cfs(cfs_account, receipt_number, ReverseOperation.CORRECTION.value) + + CFSService.create_cfs_receipt( + cfs_account=cfs_account, + rcpt_number=f'R{receipt_number}', + rcpt_date=payment.payment_date.strftime('%Y-%m-%d'), + amount=payment.invoice_amount, + payment_method=payment_account.payment_method, + access_token=CFSService.get_fas_token().json().get('access_token')) + + cls._reset_invoices_and_references_to_created_for_electronic_funds_transfer(eft_short_name) + + cls._apply_electronic_funds_transfers_to_pending_invoices(eft_short_name, receipt_number) + + eft_credit.remaining_amount = eft_credit.amount + + eft_short_name.save() + + except Exception as e: # NOQA # pylint: disable=broad-except + capture_message( + f'Error on Processing UNLINKING for :={receipt_number}, ' + f'EFT Short Name : {eft_short_name.id}, ERROR : {str(e)}', level='error') + current_app.logger.error(e) + continue + + @classmethod + def _get_eft_short_names_by_status(cls, status: str) -> List[EFTShortNamesModel]: """Get electronic funds transfer by state.""" - query = db.session.query(EFTShortnameModel.id.label('short_name_id'), EFTShortnameLinksModel.auth_account_id) \ - .join(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortnameModel.id) \ + query = db.session.query(EFTShortNamesModel.id.label('short_name_id'), EFTShortnameLinksModel.auth_account_id, + EFTShortNamesModel.version.label('short_name_version'), ) \ + .join(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortNamesModel.id) \ .join(PaymentAccountModel, PaymentAccountModel.auth_account_id == EFTShortnameLinksModel.auth_account_id) \ .join(CfsAccountModel, CfsAccountModel.account_id == PaymentAccountModel.id) \ .filter(CfsAccountModel.status == CfsAccountStatus.ACTIVE.value) - if status == EFTShortnameStatus.UNLINKED.value: - query = query.filter(EFTShortnameLinksModel.id.is_(None)) if status == EFTShortnameStatus.LINKED.value: query = query.filter(EFTShortnameLinksModel.status_code == status) @@ -114,15 +164,16 @@ def _get_eft_short_names_by_status(cls, status: str) -> List[EFTShortnameModel]: # Short name can have multiple linked accounts, prepare list of dataclasses with the associated # auth_account_ids for the outer processing loops - for short_name_id, auth_account_id in result: - short_name_results.append(EFTShortnameInfo(id=short_name_id, auth_account_id=auth_account_id)) + for short_name_id, auth_account_id, short_name_version in result: + short_name_results.append(EFTShortnameInfo(id=short_name_id, auth_account_id=auth_account_id, + version=short_name_version)) return short_name_results @classmethod def _apply_electronic_funds_transfers_to_pending_invoices(cls, - eft_short_name: EFTShortnameModel, - payment: PaymentModel) -> float: + eft_short_name: EFTShortNamesModel, + receipt_number) -> float: """Apply the electronic funds transfers again.""" current_app.logger.info( f'Applying electronic funds transfer to pending invoices: {eft_short_name.id}') @@ -143,10 +194,9 @@ def _apply_electronic_funds_transfers_to_pending_invoices(cls, ) # apply invoice to the CFS_ACCOUNT - cls.apply_electronic_funds_transfer_to_invoice( - payment_account, cfs_account, eft_short_name, invoice, invoice_reference.invoice_number, payment + cls.apply_eft_receipt_to_invoice( + payment_account, cfs_account, eft_short_name, invoice, invoice_reference.invoice_number, receipt_number ) - # IF invoice balance is zero, then update records. if CFSService.get_invoice(cfs_account=cfs_account, inv_number=invoice_reference.invoice_number) \ .get('amount_due') == 0: @@ -158,18 +208,44 @@ def _apply_electronic_funds_transfers_to_pending_invoices(cls, return applied_amount @classmethod - def apply_electronic_funds_transfer_to_invoice(cls, # pylint: disable = too-many-arguments, too-many-locals - payment_account: PaymentAccountModel, - cfs_account: CfsAccountModel, - eft_short_name: EFTShortnameModel, - invoice: InvoiceModel, - invoice_number: str, - payment: PaymentModel) -> bool: + def _reset_invoices_and_references_to_created_for_electronic_funds_transfer(cls, + eft_short_name: EFTShortNamesModel): + """Reset Invoices, Invoice references and Receipts for EFT.""" + invoices: List[InvoiceModel] = db.session.query(InvoiceModel) \ + .join(InvoiceReferenceModel, PaymentModel.invoice_number == InvoiceReferenceModel.invoice_number) \ + .join(InvoiceModel, InvoiceReferenceModel.invoice_id == InvoiceModel.id) \ + .join(PaymentLineItemModel, PaymentLineItemModel.invoice_id == InvoiceModel.id) \ + .join(PaymentAccountModel, PaymentAccountModel.id == InvoiceModel.payment_account_id) \ + .join(EFTCreditModel, EFTCreditModel.payment_account_id == PaymentAccountModel.id) \ + .join(EFTShortNamesModel, EFTShortNamesModel.id == EFTCreditModel.short_name_id) \ + .join(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortNamesModel.id) \ + .filter(InvoiceModel.invoice_status_code == InvoiceStatus.PAID.value) \ + .filter(EFTShortNamesModel.auth_account_id == eft_short_name.auth_account_id) \ + .all() + + for invoice in invoices: + # Reset Invoice and Invoice Reference statuses. + invoice.invoice_status_code = InvoiceStatus.CREATED.value + invoice_reference = InvoiceReferenceModel.find_by_invoice_id_and_status( + invoice.id, InvoiceReferenceStatus.COMPLETED.value + ) + invoice_reference.status_code = InvoiceReferenceStatus.ACTIVE.value + # Delete receipts as they are now reversed in CFS. + for receipt in ReceiptModel.find_all_receipts_for_invoice(invoice.id): + db.session.delete(receipt) + + @classmethod + def apply_eft_receipt_to_invoice(cls, # pylint: disable = too-many-arguments, too-many-locals + payment_account: PaymentAccountModel, + cfs_account: CfsAccountModel, + eft_short_name: EFTShortNamesModel, + invoice: InvoiceModel, + invoice_number: str, + receipt_number) -> bool: """Apply electronic funds transfers (receipts in CFS) to invoice.""" has_errors = False # an invoice has to be applied to multiple receipts (incl. all linked RS); apply till the balance is zero try: - receipt_number = generate_receipt_number(payment.id) current_app.logger.debug(f'Apply receipt {receipt_number} on invoice {invoice_number} ' f'for electronic funds transfer {eft_short_name.id}') diff --git a/jobs/payment-jobs/tests/jobs/factory.py b/jobs/payment-jobs/tests/jobs/factory.py index e199cf1cc..e665c8d4e 100644 --- a/jobs/payment-jobs/tests/jobs/factory.py +++ b/jobs/payment-jobs/tests/jobs/factory.py @@ -218,11 +218,11 @@ def factory_routing_slip_account( def factory_create_eft_account(auth_account_id='1234', status=CfsAccountStatus.PENDING.value): """Return Factory.""" - account = PaymentAccount(auth_account_id=auth_account_id, - payment_method=PaymentMethod.EFT.value, - name=f'Test {auth_account_id}').save() - CfsAccount(status=status, account_id=account.id).save() - return account + payment_account = PaymentAccount(auth_account_id=auth_account_id, + payment_method=PaymentMethod.EFT.value, + name=f'Test {auth_account_id}').save() + CfsAccount(status=status, account_id=payment_account.id).save() + return payment_account def factory_create_eft_shortname(short_name: str): @@ -234,12 +234,13 @@ def factory_create_eft_shortname(short_name: str): def factory_eft_shortname_link(short_name_id: int, auth_account_id: str = '1234', - updated_by: str = None, updated_on: datetime = datetime.now()): + updated_by: str = None, updated_on: datetime = datetime.now(), + status_code: str = EFTShortnameStatus.LINKED.value): """Return an EFT short name link model.""" return EFTShortnameLinks( eft_short_name_id=short_name_id, auth_account_id=auth_account_id, - status_code=EFTShortnameStatus.LINKED.value, + status_code=status_code, updated_by=updated_by, updated_by_name=updated_by, updated_on=updated_on diff --git a/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py b/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py index ee3d183d3..0c4cd4942 100644 --- a/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py +++ b/jobs/payment-jobs/tests/jobs/test_electronic_funds_transfer_task.py @@ -20,22 +20,23 @@ from unittest.mock import patch from pay_api.models import CfsAccount as CfsAccountModel -from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTShortnames as EFTShortnameModel -from pay_api.utils.enums import CfsAccountStatus, PaymentMethod +from pay_api.models import FeeSchedule as FeeScheduleModel +from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.utils.enums import ( + CfsAccountStatus, EFTShortnameStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod) from pay_api.services import CFSService from tasks.electronic_funds_transfer_task import ElectronicFundsTransferTask - from .factory import ( factory_create_eft_account, factory_create_eft_credit, factory_create_eft_file, factory_create_eft_shortname, factory_create_eft_transaction, factory_eft_shortname_link, factory_invoice, factory_invoice_reference, - factory_payment) + factory_payment, factory_payment_line_item, factory_receipt) -def test_link_electronic_funds_transfer(session): - """Test link electronic funds transfer.""" +def test_link_electronic_funds_transfers(session): + """Test link electronic funds transfers.""" auth_account_id = '1234' short_name = 'TEST1' @@ -77,3 +78,54 @@ def test_link_electronic_funds_transfer(session): cfs_account: CfsAccountModel = CfsAccountModel.find_by_id(cfs_account.id) assert cfs_account.status == CfsAccountStatus.ACTIVE.value + + +def test_unlink_electronic_funds_transfers(session): + """Test unlink electronic funds transfers.""" + auth_account_id = '1234' + short_name = 'TEST1' + receipt_number = '1111R' + invoice_number = '1234' + + payment_account = factory_create_eft_account(auth_account_id=auth_account_id, status=CfsAccountStatus.ACTIVE.value) + eft_file = factory_create_eft_file() + eft_transaction = factory_create_eft_transaction(file_id=eft_file.id) + eft_short_name = factory_create_eft_shortname(short_name=short_name) + eft_short_name_link = factory_eft_shortname_link( + short_name_id=eft_short_name.id, + auth_account_id=auth_account_id, + updated_by='test', + status_code=EFTShortnameStatus.UNLINKED.value + ).save() + + invoice = factory_invoice(payment_account=payment_account, total=30, + status_code=InvoiceStatus.PAID.value, + payment_method_code=PaymentMethod.EFT.value) + + factory_payment(payment_account_id=payment_account.id, payment_method_code=PaymentMethod.EFT.value, + invoice_amount=351.50, invoice_number=invoice_number) + fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type('CP', 'OTANN') + factory_payment_line_item(invoice_id=invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id) + factory_invoice_reference(invoice_id=invoice.id, status_code=InvoiceReferenceStatus.COMPLETED.value, + invoice_number=invoice_number) + factory_create_eft_credit( + amount=100, remaining_amount=0, eft_file_id=eft_file.id, short_name_id=eft_short_name.id, + payment_account_id=payment_account.id, + eft_transaction_id=eft_transaction.id) + + factory_receipt(invoice.id, receipt_number) + + eft_short_name = EFTShortnameModel.find_by_short_name(short_name) + eft_short_name_link = EFTShortnameLinksModel.find_by_short_name_id(eft_short_name.id)[0] + eft_short_name_link.updated_by = None + eft_short_name_link.updated_by_name = None + eft_short_name_link.updated_on = None + eft_short_name.save() + + session.commit() + + with patch('pay_api.services.CFSService.reverse_rs_receipt_in_cfs') as mock_reverse: + with patch('pay_api.services.CFSService.create_cfs_receipt') as mock_create_receipt: + ElectronicFundsTransferTask.unlink_electronic_funds_transfers() + mock_reverse.assert_called() + mock_create_receipt.assert_called() From a298310a793b366922342353f741a834b86a757c Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Wed, 1 May 2024 08:36:36 -0700 Subject: [PATCH 58/87] 20420 - EFT Short name search (#1496) * 20420 - EFT Short name search - refactor for EFT Enabled Accounts Table search - clean up params / schema * version increment * clean up statement filter --- pay-api/src/pay_api/models/eft_short_names.py | 27 +- .../pay_api/resources/v1/eft_short_names.py | 19 +- .../src/pay_api/services/eft_short_names.py | 121 +++--- pay-api/src/pay_api/version.py | 2 +- .../tests/unit/api/test_eft_short_names.py | 345 +++++++++--------- 5 files changed, 234 insertions(+), 280 deletions(-) diff --git a/pay-api/src/pay_api/models/eft_short_names.py b/pay-api/src/pay_api/models/eft_short_names.py index 6e4af5a2e..4c8262cda 100644 --- a/pay-api/src/pay_api/models/eft_short_names.py +++ b/pay-api/src/pay_api/models/eft_short_names.py @@ -21,7 +21,6 @@ from .base_model import BaseModel from .db import db -from ..utils.util import cents_to_decimal class EFTShortnames(Versioned, BaseModel): # pylint: disable=too-many-instance-attributes @@ -61,19 +60,14 @@ class EFTShortnameSchema: # pylint: disable=too-few-public-methods """Main schema used to serialize the EFT Short name.""" id: int - short_name: str - status_code: str account_id: str account_name: str account_branch: str + amount_owing: Decimal created_on: datetime - transaction_id: int - transaction_date: datetime - deposit_date: datetime - deposit_amount: Decimal - updated_by: str - updated_by_name: str - updated_on: datetime + short_name: str + statement_id: int + status_code: str @classmethod def from_row(cls, row: EFTShortnames): @@ -82,19 +76,14 @@ def from_row(cls, row: EFTShortnames): https://www.attrs.org/en/stable/init.html """ return cls(id=row.id, - short_name=row.short_name, - status_code=getattr(row, 'status_code', None), account_id=getattr(row, 'auth_account_id', None), account_name=getattr(row, 'account_name', None), account_branch=getattr(row, 'account_branch', None), + amount_owing=getattr(row, 'total_owing', None), created_on=row.created_on, - transaction_id=getattr(row, 'transaction_id', None), - transaction_date=getattr(row, 'transaction_date', None), - deposit_date=getattr(row, 'deposit_date', None), - deposit_amount=cents_to_decimal(getattr(row, 'deposit_amount', None)), - updated_by=getattr(row, 'updated_by'), - updated_by_name=getattr(row, 'updated_by_name'), - updated_on=getattr(row, 'updated_on') + short_name=row.short_name, + statement_id=getattr(row, 'latest_statement_id', None), + status_code=getattr(row, 'status_code', None) ) diff --git a/pay-api/src/pay_api/resources/v1/eft_short_names.py b/pay-api/src/pay_api/resources/v1/eft_short_names.py index 432dffecf..5b9e7c88a 100644 --- a/pay-api/src/pay_api/resources/v1/eft_short_names.py +++ b/pay-api/src/pay_api/resources/v1/eft_short_names.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. """Resource for EFT Short name.""" -from decimal import Decimal from http import HTTPStatus from flask import Blueprint, current_app, jsonify, request @@ -43,28 +42,22 @@ def get_eft_shortnames(): state = request.args.get('state').split(',') if request.args.get('state', None) else None page: int = int(request.args.get('page', '1')) limit: int = int(request.args.get('limit', '10')) - transaction_start_date = request.args.get('transactionStartDate', None) - transaction_end_date = request.args.get('transactionEndDate', None) - deposit_amount = request.args.get('depositAmount', None) - deposit_start_date = request.args.get('depositStartDate', None) - deposit_end_date = request.args.get('depositEndDate', None) + amount_owing = request.args.get('amountOwing', None) short_name = request.args.get('shortName', None) + short_name_id = request.args.get('shortNameId', None) + statement_id = request.args.get('statementId', None) account_id = request.args.get('accountId', None) - account_id_list = request.args.get('accountIdList').split(',') if request.args.get('accountIdList', None) else None account_name = request.args.get('accountName', None) account_branch = request.args.get('accountBranch', None) response, status = EFTShortnameService.search(EFTShortnamesSearch( + id=short_name_id, account_id=account_id, - account_id_list=account_id_list, account_name=account_name, account_branch=account_branch, - deposit_start_date=string_to_date(deposit_start_date), - deposit_end_date=string_to_date(deposit_end_date), - deposit_amount=Decimal(deposit_amount) * Decimal(100) if deposit_amount else None, + amount_owing=string_to_decimal(amount_owing), short_name=short_name, - transaction_start_date=string_to_date(transaction_start_date), - transaction_end_date=string_to_date(transaction_end_date), + statement_id=string_to_int(statement_id), state=state, page=page, limit=limit)), HTTPStatus.OK diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index 881147668..a7c1eb50f 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -21,7 +21,7 @@ from _decimal import Decimal from flask import current_app -from sqlalchemy import case, func +from sqlalchemy import case, func, or_ from sqlalchemy.sql.expression import exists from pay_api.exceptions import BusinessException @@ -30,14 +30,16 @@ from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTShortnameLinkSchema from pay_api.models import EFTShortnameSchema -from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models import Statement as StatementModel +from pay_api.models import StatementInvoices as StatementInvoicesModel from pay_api.models import db from pay_api.utils.converter import Converter -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import EFTShortnameStatus, InvoiceStatus, PaymentMethod from pay_api.utils.errors import Error from pay_api.utils.user_context import user_context +from pay_api.utils.util import unstructure_schema_items @dataclass @@ -45,18 +47,16 @@ class EFTShortnamesSearch: # pylint: disable=too-many-instance-attributes """Used for searching EFT short name records.""" id: Optional[int] = None - account_id_list: Optional[List[str]] = None account_id: Optional[str] = None account_name: Optional[str] = None account_branch: Optional[str] = None - transaction_start_date: Optional[date] = None - transaction_end_date: Optional[date] = None + amount_owing: Optional[Decimal] = None deposit_start_date: Optional[date] = None deposit_end_date: Optional[date] = None - deposit_amount: Optional[Decimal] = None credit_remaining: Optional[Decimal] = None linked_accounts_count: Optional[int] = None short_name: Optional[str] = None + statement_id: Optional[int] = None state: Optional[List[str]] = None page: Optional[int] = 1 limit: Optional[int] = 10 @@ -203,9 +203,7 @@ def search(cls, search_criteria: EFTShortnamesSearch): pagination = search_query.paginate(per_page=search_criteria.limit, page=search_criteria.page) - short_name_list = [EFTShortnameSchema.from_row(short_name) for short_name in pagination.items] - converter = Converter() - short_name_list = converter.unstructure(short_name_list) + short_name_list = unstructure_schema_items(EFTShortnameSchema, pagination.items) current_app.logger.debug('>search') return { @@ -216,6 +214,21 @@ def search(cls, search_criteria: EFTShortnamesSearch): 'total': pagination.total } + @staticmethod + def get_statement_summary_query(): + """Query for latest statement id and total amount owing of invoices in statements.""" + return db.session.query( + StatementModel.payment_account_id, + func.max(StatementModel.id).label('latest_statement_id'), + func.coalesce(func.sum(InvoiceModel.total - InvoiceModel.paid), 0).label('total_owing') + ).outerjoin( + StatementInvoicesModel, + StatementInvoicesModel.statement_id == StatementModel.id + ).outerjoin( + InvoiceModel, + InvoiceModel.id == StatementInvoicesModel.invoice_id + ).group_by(StatementModel.payment_account_id) + @classmethod def get_search_count(cls, search_criteria: EFTShortnamesSearch): """Get total count of results based on short name state search criteria.""" @@ -227,25 +240,10 @@ def get_search_count(cls, search_criteria: EFTShortnamesSearch): current_app.logger.debug('>get_search_count') return count_query.count() - @staticmethod - def get_ordered_transaction_query(): - """Query for EFT transactions.""" - return db.session.query( - EFTTransactionModel.id, - EFTTransactionModel.transaction_date, - EFTTransactionModel.deposit_date, - EFTTransactionModel.deposit_amount_cents, - EFTTransactionModel.short_name_id, - func.row_number().over(partition_by=EFTTransactionModel.short_name_id, - order_by=[EFTTransactionModel.transaction_date, EFTTransactionModel.id]).label('rn') - ).filter(and_(EFTTransactionModel.short_name_id.isnot(None), - EFTTransactionModel.status_code == EFTProcessStatus.COMPLETED.value))\ - .filter(EFTTransactionModel.line_type == EFTFileLineType.TRANSACTION.value) - @classmethod def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = False): """Query for short names based on search criteria.""" - sub_query = None + statement_summary_query = cls.get_statement_summary_query().subquery() # Case statement is to check for and remove the branch name from the name, so they can be filtered on separately # The branch name was added to facilitate a better short name search experience and the existing @@ -255,45 +253,33 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = EFTShortnameModel.created_on, EFTShortnameLinksModel.status_code, EFTShortnameLinksModel.auth_account_id, - EFTShortnameLinksModel.updated_by, - EFTShortnameLinksModel.updated_by_name, - EFTShortnameLinksModel.updated_on, case( (EFTShortnameLinksModel.auth_account_id.is_(None), EFTShortnameStatus.UNLINKED.value ), else_=EFTShortnameLinksModel.status_code ).label('status_code')) - .outerjoin(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortnameModel.id)) + .outerjoin(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortnameModel.id) + .outerjoin(PaymentAccountModel, + PaymentAccountModel.auth_account_id == EFTShortnameLinksModel.auth_account_id)) # Join payment information if this is NOT the count query if not is_count: - sub_query = cls.get_ordered_transaction_query().subquery() - query = query.add_columns( + query = (query.add_columns( case( (PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') ), else_=PaymentAccountModel.name).label('account_name'), PaymentAccountModel.branch_name.label('account_branch'), - sub_query.c.id.label('transaction_id'), - sub_query.c.deposit_date.label('deposit_date'), - sub_query.c.transaction_date.label('transaction_date'), - sub_query.c.deposit_amount_cents.label('deposit_amount')) \ - .outerjoin(sub_query, and_(sub_query.c.short_name_id == EFTShortnameModel.id, sub_query.c.rn == 1)) \ - .outerjoin(PaymentAccountModel, - PaymentAccountModel.auth_account_id == EFTShortnameLinksModel.auth_account_id) - - # Sub query filters for EFT dates - query = query.filter_conditional_date_range(start_date=search_criteria.transaction_start_date, - end_date=search_criteria.transaction_end_date, - model_attribute=sub_query.c.transaction_date) - - query = query.filter_conditional_date_range(start_date=search_criteria.deposit_start_date, - end_date=search_criteria.deposit_end_date, - model_attribute=sub_query.c.deposit_date) - - # Sub query filters - query = query.filter_conditionally(search_criteria.deposit_amount, sub_query.c.deposit_amount_cents) + statement_summary_query.c.total_owing, + statement_summary_query.c.latest_statement_id + ).outerjoin( + statement_summary_query, + statement_summary_query.c.payment_account_id == PaymentAccountModel.id + )) + + # Short name link filters + query = query.filter_conditionally(search_criteria.id, EFTShortnameModel.id) query = query.filter_conditionally(search_criteria.account_id, EFTShortnameLinksModel.auth_account_id, is_like=True) # Payment account filters @@ -301,19 +287,25 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = query = query.filter_conditionally(search_criteria.account_branch, PaymentAccountModel.branch_name, is_like=True) - query = cls.get_link_state_filters(search_criteria, query) + # Statement summary filters + query = query.filter_conditionally(search_criteria.statement_id, + statement_summary_query.c.latest_statement_id) + if search_criteria.amount_owing == 0: + query = query.filter(or_(statement_summary_query.c.total_owing == 0, + statement_summary_query.c.total_owing.is_(None))) + else: + query = query.filter_conditionally(search_criteria.amount_owing, statement_summary_query.c.total_owing) - # Filter by a list of auth account ids - full match - if search_criteria.account_id_list: - query = query.filter(EFTShortnameLinksModel.auth_account_id.in_(search_criteria.account_id_list)) + query = cls.get_link_state_filters(search_criteria, query) + query = query.filter( + or_(PaymentAccountModel.payment_method == PaymentMethod.EFT.value, PaymentAccountModel.id.is_(None)) + ) # Short name filters query = query.filter_conditionally(search_criteria.id, EFTShortnameModel.id) query = query.filter_conditionally(search_criteria.short_name, EFTShortnameModel.short_name, is_like=True) - if not is_count: - query = cls.get_order_by(search_criteria, query, sub_query) if search_criteria.state else query - + query = query.order_by(EFTShortnameModel.short_name.asc(), EFTShortnameLinksModel.auth_account_id.asc()) return query @classmethod @@ -322,7 +314,7 @@ def get_link_state_filters(cls, search_criteria, query): if search_criteria.state: if EFTShortnameStatus.UNLINKED.value in search_criteria.state: # There can be multiple links to a short name, look for any links that don't have an UNLINKED status - # if they don't exist return them. + # if they don't exist return the short name. query = query.filter( ~exists() .where(EFTShortnameLinksModel.status_code != EFTShortnameStatus.UNLINKED.value) @@ -335,14 +327,3 @@ def get_link_state_filters(cls, search_criteria, query): EFTShortnameStatus.LINKED.value]) ) return query - - @classmethod - def get_order_by(cls, search_criteria, query, sub_query): - """Get the order by for search query.""" - if EFTShortnameStatus.LINKED.value in search_criteria.state: - return query.order_by(EFTShortnameLinksModel.updated_on.desc()) - - if EFTShortnameStatus.UNLINKED.value in search_criteria.state and sub_query is not None: - return query.order_by(sub_query.c.deposit_date.desc()) - - return query diff --git a/pay-api/src/pay_api/version.py b/pay-api/src/pay_api/version.py index a9327588f..b02bfda58 100644 --- a/pay-api/src/pay_api/version.py +++ b/pay-api/src/pay_api/version.py @@ -22,4 +22,4 @@ Development release segment: .devN """ -__version__ = '1.20.11' # pylint: disable=invalid-name +__version__ = '1.20.12' # pylint: disable=invalid-name diff --git a/pay-api/tests/unit/api/test_eft_short_names.py b/pay-api/tests/unit/api/test_eft_short_names.py index e0bbbca8f..c6a917e9d 100755 --- a/pay-api/tests/unit/api/test_eft_short_names.py +++ b/pay-api/tests/unit/api/test_eft_short_names.py @@ -29,12 +29,14 @@ from pay_api.models import EFTShortnames as EFTShortnamesModel from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Payment as PaymentModel +from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel from pay_api.utils.enums import ( - EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod, PaymentStatus, Role) + EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod, PaymentStatus, Role, + StatementFrequency) from tests.utilities.base_test import ( factory_eft_file, factory_eft_shortname, factory_eft_shortname_link, factory_invoice, factory_payment_account, - get_claims, token_header) + factory_statement, factory_statement_invoices, factory_statement_settings, get_claims, token_header) def test_create_eft_short_name_link(session, client, jwt, app): @@ -170,7 +172,7 @@ def test_eft_short_name_summaries(session, client, jwt, app): name='ABC-123', branch_name='123').save() - short_name_1, s1_transaction1, short_name_2, s2_transaction1 = create_eft_search_data() + short_name_1, s1_transaction1, short_name_2, s2_transaction1 = create_eft_summary_search_data() # Assert short name search brings back both short names rv = client.get('/api/v1/eft-shortnames/summaries?shortName=SHORT', headers=headers) @@ -327,8 +329,8 @@ def test_eft_short_name_summaries(session, client, jwt, app): short_name_2, s2_transaction1, 302.5, 1) -def create_eft_search_data(): - """Create seed data for EFT searches.""" +def create_eft_summary_search_data(): + """Create seed data for EFT summary searches.""" eft_file: EFTFileModel = factory_eft_file() short_name_1 = factory_eft_shortname(short_name='TESTSHORTNAME1').save() short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2').save() @@ -408,16 +410,107 @@ def create_eft_search_data(): return short_name_1, s1_transaction1, short_name_2, s2_transaction1 -def assert_short_name(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel, - expected_status: str): +def create_eft_search_data(): + """Create seed data for EFT searches.""" + payment_account_1 = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1111', + name='ABC-1111', + branch_name='111').save() + payment_account_2 = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='2222', + name='DEF-2222', + branch_name='222').save() + payment_account_3 = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='3333', + name='GHI-3333', + branch_name='333').save() + + # Create unlinked short name + short_name_unlinked = factory_eft_shortname(short_name='TESTSHORTNAME1').save() + + # Create single linked short name + short_name_linked = factory_eft_shortname(short_name='TESTSHORTNAME2').save() + factory_eft_shortname_link( + short_name_id=short_name_linked.id, + auth_account_id=payment_account_1.auth_account_id, + updated_by='IDIR/JSMITH' + ).save() + # Create statement with multiple invoices + s1_invoice_1 = factory_invoice(payment_account_1, payment_method_code=PaymentMethod.EFT.value, + total=50, paid=0).save() + s1_invoice_2 = factory_invoice(payment_account_1, payment_method_code=PaymentMethod.EFT.value, + total=100.50, paid=0).save() + s1_settings = factory_statement_settings(payment_account_id=payment_account_1.id, + frequency=StatementFrequency.MONTHLY.value) + statement_1 = factory_statement(payment_account_id=payment_account_1.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=s1_settings.id) + factory_statement_invoices(statement_id=statement_1.id, invoice_id=s1_invoice_1.id) + factory_statement_invoices(statement_id=statement_1.id, invoice_id=s1_invoice_2.id) + + # Create multi account linked short name + short_name_multi_linked = factory_eft_shortname(short_name='TESTSHORTNAME3').save() + factory_eft_shortname_link( + short_name_id=short_name_multi_linked.id, + auth_account_id=payment_account_2.auth_account_id, + updated_by='IDIR/JSMITH' + ).save() + factory_eft_shortname_link( + short_name_id=short_name_multi_linked.id, + auth_account_id=payment_account_3.auth_account_id, + updated_by='IDIR/JSMITH' + ).save() + + s2_settings = factory_statement_settings(payment_account_id=payment_account_2.id, + frequency=StatementFrequency.MONTHLY.value) + statement_2 = factory_statement(payment_account_id=payment_account_2.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=s2_settings.id) + + s3_settings = factory_statement_settings(payment_account_id=payment_account_3.id, + frequency=StatementFrequency.MONTHLY.value) + statement_3 = factory_statement(payment_account_id=payment_account_3.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=s3_settings.id) + s3_invoice_1 = factory_invoice(payment_account_3, payment_method_code=PaymentMethod.EFT.value, + total=33.33, paid=0).save() + factory_statement_invoices(statement_id=statement_3.id, invoice_id=s3_invoice_1.id) + + return { + 'single-linked': {'short_name': short_name_linked, + 'accounts': [payment_account_1], + 'statement_summary': [{'statement_id': statement_1.id, 'owing_amount': 150.50}]}, + 'multi-linked': {'short_name': short_name_multi_linked, + 'accounts': [payment_account_2, payment_account_3], + 'statement_summary': [{'statement_id': statement_2.id, 'owing_amount': 0}, + {'statement_id': statement_3.id, 'owing_amount': 33.33}]}, + 'unlinked': {'short_name': short_name_unlinked, + 'accounts': [], + 'statement_summary': None} + } + + +def assert_short_name(result_dict: dict, short_name: EFTShortnamesModel, + payment_account: PaymentAccountModel = None, + statement_summary=None): """Assert short name result.""" - date_format = '%Y-%m-%dT%H:%M:%S' assert result_dict['shortName'] == short_name.short_name - assert result_dict['statusCode'] == expected_status - assert result_dict['depositAmount'] == transaction.deposit_amount_cents / 100 - assert datetime.strptime(result_dict['depositDate'], date_format) == transaction.deposit_date - assert result_dict['transactionId'] == transaction.id - assert datetime.strptime(result_dict['transactionDate'], date_format) == transaction.transaction_date + + if not payment_account: + assert result_dict['accountId'] is None + assert result_dict['accountName'] is None + assert result_dict['accountBranch'] is None + else: + assert result_dict['accountId'] == payment_account.auth_account_id + assert payment_account.name.startswith(result_dict['accountName']) + assert result_dict['accountBranch'] == payment_account.branch_name + + if not statement_summary: + assert result_dict['amountOwing'] == 0 + assert result_dict['statementId'] is None + else: + assert result_dict['amountOwing'] == statement_summary['owing_amount'] + assert result_dict['statementId'] == statement_summary['statement_id'] def test_search_eft_short_names(session, client, jwt, app): @@ -435,12 +528,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert len(result_dict['items']) == 0 # create test data - payment_account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, - auth_account_id='1234', - name='ABC-123', - branch_name='123').save() - - short_name_1, s1_transaction1, short_name_2, s2_transaction1 = create_eft_search_data() + data_dict = create_eft_search_data() # Assert search returns unlinked short names rv = client.get('/api/v1/eft-shortnames?state=UNLINKED', headers=headers) @@ -455,7 +543,7 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 1 assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME1' - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) + assert_short_name(result_dict['items'][0], data_dict['unlinked']['short_name']) # Assert search returns linked short names with payment account name that has a branch rv = client.get('/api/v1/eft-shortnames?state=LINKED', headers=headers) @@ -464,15 +552,23 @@ def test_search_eft_short_names(session, client, jwt, app): result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 - assert result_dict['total'] == 1 + assert result_dict['stateTotal'] == 2 + assert result_dict['total'] == 3 assert result_dict['limit'] == 10 assert result_dict['items'] is not None - assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2' - assert result_dict['items'][0]['accountName'] == 'ABC' - assert result_dict['items'][0]['accountBranch'] == '123' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) + assert len(result_dict['items']) == 3 + assert_short_name(result_dict['items'][0], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) + assert_short_name(result_dict['items'][1], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][0], + data_dict['multi-linked']['statement_summary'][0]) + assert_short_name(result_dict['items'][2], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][1], + data_dict['multi-linked']['statement_summary'][1]) # Assert search account name rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountName=BC', headers=headers) @@ -481,13 +577,15 @@ def test_search_eft_short_names(session, client, jwt, app): result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 + assert result_dict['stateTotal'] == 2 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['accountName'] == 'ABC' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) + assert_short_name(result_dict['items'][0], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) # Assert search account branch rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountBranch=2', headers=headers) @@ -496,51 +594,15 @@ def test_search_eft_short_names(session, client, jwt, app): result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 - assert result_dict['total'] == 1 - assert result_dict['limit'] == 10 - assert result_dict['items'] is not None - assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['accountName'] == 'ABC' - assert result_dict['items'][0]['accountBranch'] == '123' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) - - # Update payment account to not have a branch name - payment_account.name = 'ABC' - payment_account.branch_name = None - payment_account.save() - - # Assert search returns linked short names with payment account name that has no branch - rv = client.get('/api/v1/eft-shortnames?state=LINKED', headers=headers) - assert rv.status_code == 200 - - result_dict = rv.json - assert result_dict is not None - assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 - assert result_dict['total'] == 1 - assert result_dict['limit'] == 10 - assert result_dict['items'] is not None - assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2' - assert result_dict['items'][0]['accountName'] == 'ABC' - assert result_dict['items'][0]['accountBranch'] is None - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) - - # Assert search account name - rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountName=BC', headers=headers) - assert rv.status_code == 200 - - result_dict = rv.json - assert result_dict is not None - assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 + assert result_dict['stateTotal'] == 2 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['accountName'] == 'ABC' - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) + assert_short_name(result_dict['items'][0], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][0], + data_dict['multi-linked']['statement_summary'][0]) # Assert search query by no state will return all records rv = client.get('/api/v1/eft-shortnames', headers=headers) @@ -549,13 +611,25 @@ def test_search_eft_short_names(session, client, jwt, app): result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 2 + assert result_dict['stateTotal'] == 3 + assert result_dict['total'] == 4 assert result_dict['limit'] == 10 assert result_dict['items'] is not None - assert len(result_dict['items']) == 2 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) - assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) + assert len(result_dict['items']) == 4 + assert_short_name(result_dict['items'][0], + data_dict['unlinked']['short_name']) + assert_short_name(result_dict['items'][1], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) + assert_short_name(result_dict['items'][2], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][0], + data_dict['multi-linked']['statement_summary'][0]) + assert_short_name(result_dict['items'][3], + data_dict['multi-linked']['short_name'], + data_dict['multi-linked']['accounts'][1], + data_dict['multi-linked']['statement_summary'][1]) # Assert search pagination - page 1 works rv = client.get('/api/v1/eft-shortnames?page=1&limit=1', headers=headers) @@ -564,12 +638,13 @@ def test_search_eft_short_names(session, client, jwt, app): result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 2 + assert result_dict['stateTotal'] == 3 + assert result_dict['total'] == 4 assert result_dict['limit'] == 1 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) + assert_short_name(result_dict['items'][0], + data_dict['unlinked']['short_name']) # Assert search pagination - page 2 works rv = client.get('/api/v1/eft-shortnames?page=2&limit=1', headers=headers) @@ -578,27 +653,15 @@ def test_search_eft_short_names(session, client, jwt, app): result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 2 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 2 + assert result_dict['stateTotal'] == 3 + assert result_dict['total'] == 4 assert result_dict['limit'] == 1 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) - - # Assert search text brings back both short names - rv = client.get('/api/v1/eft-shortnames?shortName=SHORT', headers=headers) - assert rv.status_code == 200 - - result_dict = rv.json - assert result_dict is not None - assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 2 - assert result_dict['limit'] == 10 - assert result_dict['items'] is not None - assert len(result_dict['items']) == 2 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) - assert_short_name(result_dict['items'][1], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) + assert_short_name(result_dict['items'][0], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) # Assert search text brings back one short name rv = client.get('/api/v1/eft-shortnames?shortName=name1', headers=headers) @@ -607,88 +670,16 @@ def test_search_eft_short_names(session, client, jwt, app): result_dict = rv.json assert result_dict is not None assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 1 - assert result_dict['limit'] == 10 - assert result_dict['items'] is not None - assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) - - # Assert search transaction date - rv = client.get('/api/v1/eft-shortnames?transactionStartDate=2024-01-04&transactionEndDate=2024-01-14', - headers=headers) - assert rv.status_code == 200 - - result_dict = rv.json - assert result_dict is not None - assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 1 - assert result_dict['limit'] == 10 - assert result_dict['items'] is not None - assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) - - # Assert search transaction date - rv = client.get('/api/v1/eft-shortnames?transactionStartDate=2024-01-04&transactionEndDate=2024-01-15', - headers=headers) - assert rv.status_code == 200 - - result_dict = rv.json - assert result_dict is not None - assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 2 - assert result_dict['limit'] == 10 - assert result_dict['items'] is not None - assert len(result_dict['items']) == 2 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) - assert_short_name(result_dict['items'][1], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) - - # Assert search transaction date - rv = client.get('/api/v1/eft-shortnames?depositStartDate=2024-01-16&depositEndDate=2024-01-16', headers=headers) - assert rv.status_code == 200 - - result_dict = rv.json - assert result_dict is not None - assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 - assert result_dict['total'] == 1 - assert result_dict['limit'] == 10 - assert result_dict['items'] is not None - assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) - - # Assert search deposit amount - rv = client.get('/api/v1/eft-shortnames?depositAmount=101.50', headers=headers) - assert rv.status_code == 200 - - result_dict = rv.json - assert result_dict is not None - assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 2 + assert result_dict['stateTotal'] == 3 assert result_dict['total'] == 1 assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_1, s1_transaction1, EFTShortnameStatus.UNLINKED.value) + assert_short_name(result_dict['items'][0], + data_dict['unlinked']['short_name']) # Assert search account id - rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountId=1234', headers=headers) - assert rv.status_code == 200 - - result_dict = rv.json - assert result_dict is not None - assert result_dict['page'] == 1 - assert result_dict['stateTotal'] == 1 - assert result_dict['total'] == 1 - assert result_dict['limit'] == 10 - assert result_dict['items'] is not None - assert len(result_dict['items']) == 1 - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) - - # Assert search account id list - rv = client.get('/api/v1/eft-shortnames?accountIdList=1,1234', headers=headers) + rv = client.get('/api/v1/eft-shortnames?state=LINKED&accountId=1111', headers=headers) assert rv.status_code == 200 result_dict = rv.json @@ -699,10 +690,10 @@ def test_search_eft_short_names(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['items'][0]['shortName'] == 'TESTSHORTNAME2' - assert result_dict['items'][0]['accountName'] == 'ABC' - assert result_dict['items'][0]['accountBranch'] is None - assert_short_name(result_dict['items'][0], short_name_2, s2_transaction1, EFTShortnameStatus.PENDING.value) + assert_short_name(result_dict['items'][0], + data_dict['single-linked']['short_name'], + data_dict['single-linked']['accounts'][0], + data_dict['single-linked']['statement_summary'][0]) @pytest.mark.skip(reason='This needs to be re-thought, the create cfs invoice job should be handling receipt creation' From 89595fb7d432a6ed73a03ffc4356d5f4be9ee04a Mon Sep 17 00:00:00 2001 From: Jia Xu Date: Thu, 9 May 2024 13:53:00 -0700 Subject: [PATCH 59/87] 20421 - Add cfs account status to search eft names (#1525) --- pay-api/src/pay_api/models/eft_short_names.py | 4 +++- pay-api/src/pay_api/services/eft_short_names.py | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pay-api/src/pay_api/models/eft_short_names.py b/pay-api/src/pay_api/models/eft_short_names.py index 4c8262cda..e9acbc11a 100644 --- a/pay-api/src/pay_api/models/eft_short_names.py +++ b/pay-api/src/pay_api/models/eft_short_names.py @@ -68,6 +68,7 @@ class EFTShortnameSchema: # pylint: disable=too-few-public-methods short_name: str statement_id: int status_code: str + cfs_account_status: str @classmethod def from_row(cls, row: EFTShortnames): @@ -83,7 +84,8 @@ def from_row(cls, row: EFTShortnames): created_on=row.created_on, short_name=row.short_name, statement_id=getattr(row, 'latest_statement_id', None), - status_code=getattr(row, 'status_code', None) + status_code=getattr(row, 'status_code', None), + cfs_account_status=getattr(row, 'cfs_account_status', None) ) diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index a7c1eb50f..0c1bbb4ff 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -26,6 +26,7 @@ from pay_api.exceptions import BusinessException from pay_api.factory.payment_system_factory import PaymentSystemFactory +from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import EFTShortnames as EFTShortnameModel from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTShortnameLinkSchema @@ -258,10 +259,13 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = EFTShortnameStatus.UNLINKED.value ), else_=EFTShortnameLinksModel.status_code - ).label('status_code')) + ).label('status_code'), + CfsAccountModel.status.label('cfs_account_status')) .outerjoin(EFTShortnameLinksModel, EFTShortnameLinksModel.eft_short_name_id == EFTShortnameModel.id) .outerjoin(PaymentAccountModel, - PaymentAccountModel.auth_account_id == EFTShortnameLinksModel.auth_account_id)) + PaymentAccountModel.auth_account_id == EFTShortnameLinksModel.auth_account_id) + .outerjoin(CfsAccountModel, + CfsAccountModel.account_id == PaymentAccountModel.id)) # Join payment information if this is NOT the count query if not is_count: From 594aef1a1039d4c13e8bece02aab8921625d29ee Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Fri, 10 May 2024 07:26:57 -0700 Subject: [PATCH 60/87] 20417 - EFT Shortname links / transaction search (#1526) * 20417 - EFT Shortname links / transaction search * fixes after doing some linking tests with the UI * Fix CI * Fixes, PR Feedback --------- Co-authored-by: Travis Semple --- .github/workflows/bcol-api-ci.yml | 2 +- .github/workflows/ftp-poller-ci.yml | 2 +- .github/workflows/notebook-report-ci.yml | 2 +- .github/workflows/pay-admin-ci.yml | 2 +- .github/workflows/pay-api-ci.yml | 2 +- .github/workflows/pay-queue-ci.yml | 2 +- .github/workflows/payment-jobs-ci.yml | 2 +- .github/workflows/report-api-ci.yml | 2 +- ...d37727a5_20417_eft_credit_invoice_links.py | 35 ++++ .../pay_api/models/eft_credit_invoice_link.py | 6 +- .../pay_api/models/eft_short_name_links.py | 7 +- pay-api/src/pay_api/models/eft_transaction.py | 24 ++- .../pay_api/resources/v1/eft_short_names.py | 2 +- .../services/eft_short_name_summaries.py | 11 +- .../src/pay_api/services/eft_short_names.py | 61 +++++-- .../src/pay_api/services/eft_transactions.py | 100 ++++++++--- pay-api/src/pay_api/utils/enums.py | 9 + pay-api/src/pay_api/version.py | 2 +- .../tests/unit/api/test_eft_short_names.py | 22 ++- .../tests/unit/api/test_eft_transactions.py | 170 +++++++++++++++--- .../models/test_eft_credit_invoice_link.py | 6 +- 21 files changed, 379 insertions(+), 92 deletions(-) create mode 100644 pay-api/migrations/versions/2024_05_07_4b3bd37727a5_20417_eft_credit_invoice_links.py diff --git a/.github/workflows/bcol-api-ci.yml b/.github/workflows/bcol-api-ci.yml index 54f82833c..8d4fe1ad5 100644 --- a/.github/workflows/bcol-api-ci.yml +++ b/.github/workflows/bcol-api-ci.yml @@ -117,7 +117,7 @@ jobs: file: ./bcol-api/coverage.xml flags: bcolapi name: codecov-bcol-api - fail_ci_if_error: true + fail_ci_if_error: false build-check: needs: setup-job diff --git a/.github/workflows/ftp-poller-ci.yml b/.github/workflows/ftp-poller-ci.yml index d9a998238..4c856cf02 100644 --- a/.github/workflows/ftp-poller-ci.yml +++ b/.github/workflows/ftp-poller-ci.yml @@ -95,7 +95,7 @@ jobs: file: ./jobs/ftp-poller/coverage.xml flags: ftppoller name: codecov-ftp-poller - fail_ci_if_error: true + fail_ci_if_error: false build-check: needs: setup-job diff --git a/.github/workflows/notebook-report-ci.yml b/.github/workflows/notebook-report-ci.yml index b4365bd1b..96a2e68b9 100644 --- a/.github/workflows/notebook-report-ci.yml +++ b/.github/workflows/notebook-report-ci.yml @@ -90,7 +90,7 @@ jobs: # file: ./jobs/ftp-poller/coverage.xml # flags: ftppoller # name: codecov-ftp-poller - # fail_ci_if_error: true + # fail_ci_if_error: false build-check: needs: setup-job diff --git a/.github/workflows/pay-admin-ci.yml b/.github/workflows/pay-admin-ci.yml index cccbe5a3e..1007a80eb 100644 --- a/.github/workflows/pay-admin-ci.yml +++ b/.github/workflows/pay-admin-ci.yml @@ -96,7 +96,7 @@ jobs: file: ./pay-admin/coverage.xml flags: payadmin name: codecov-pay-admin - fail_ci_if_error: true + fail_ci_if_error: false build-check: needs: setup-job diff --git a/.github/workflows/pay-api-ci.yml b/.github/workflows/pay-api-ci.yml index 00c080090..b2fa9295c 100644 --- a/.github/workflows/pay-api-ci.yml +++ b/.github/workflows/pay-api-ci.yml @@ -106,7 +106,7 @@ jobs: file: ./pay-api/coverage.xml flags: payapi name: codecov-pay-api - fail_ci_if_error: true + fail_ci_if_error: false build-check: needs: setup-job diff --git a/.github/workflows/pay-queue-ci.yml b/.github/workflows/pay-queue-ci.yml index 536f6a92c..567c4d23b 100644 --- a/.github/workflows/pay-queue-ci.yml +++ b/.github/workflows/pay-queue-ci.yml @@ -104,7 +104,7 @@ jobs: file: ./pay-queue/coverage.xml flags: paymentreconciliationsqueue name: codecov-payment-reconciliations - fail_ci_if_error: true + fail_ci_if_error: false build-check: needs: setup-job diff --git a/.github/workflows/payment-jobs-ci.yml b/.github/workflows/payment-jobs-ci.yml index 38b72035e..7a10f3519 100644 --- a/.github/workflows/payment-jobs-ci.yml +++ b/.github/workflows/payment-jobs-ci.yml @@ -97,7 +97,7 @@ jobs: file: ./jobs/payment-jobs/coverage.xml flags: paymentjobs name: codecov-payment-jobs - fail_ci_if_error: true + fail_ci_if_error: false build-check: needs: setup-job diff --git a/.github/workflows/report-api-ci.yml b/.github/workflows/report-api-ci.yml index 08fbb4a22..96d313e70 100644 --- a/.github/workflows/report-api-ci.yml +++ b/.github/workflows/report-api-ci.yml @@ -96,7 +96,7 @@ jobs: file: ./report-api/coverage.xml flags: reportapi name: codecov-report-api - fail_ci_if_error: true + fail_ci_if_error: false build-check: needs: setup-job diff --git a/pay-api/migrations/versions/2024_05_07_4b3bd37727a5_20417_eft_credit_invoice_links.py b/pay-api/migrations/versions/2024_05_07_4b3bd37727a5_20417_eft_credit_invoice_links.py new file mode 100644 index 000000000..ee102ecb6 --- /dev/null +++ b/pay-api/migrations/versions/2024_05_07_4b3bd37727a5_20417_eft_credit_invoice_links.py @@ -0,0 +1,35 @@ +"""Add better tracking to how much EFT credit is spent as it is possible an invoice is paid through multiple +EFT Transactions. A status is also added to allow for EFT credits to be applied later via a job. + +Revision ID: 4b3bd37727a5 +Revises: 29867cf1bd9e +Create Date: 2024-05-07 08:32:12.812898 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy import text +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '4b3bd37727a5' +down_revision = '29867cf1bd9e' +branch_labels = None +depends_on = None + + +def upgrade(): + with op.batch_alter_table('eft_credit_invoice_links', schema=None) as batch_op: + batch_op.add_column(sa.Column('amount', sa.Numeric(precision=19, scale=2), nullable=True)) + batch_op.add_column(sa.Column('status_code', sa.String(length=25), nullable=True)) + batch_op.create_index(batch_op.f('ix_eft_credit_invoice_links_status_code'), ['status_code'], unique=False) + + op.execute(text(f"UPDATE eft_credit_invoice_links " + f"SET status_code = 'PENDING'")) + + +def downgrade(): + with op.batch_alter_table('eft_credit_invoice_links', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_eft_credit_invoice_links_status_code')) + batch_op.drop_column('status_code') + batch_op.drop_column('amount') diff --git a/pay-api/src/pay_api/models/eft_credit_invoice_link.py b/pay-api/src/pay_api/models/eft_credit_invoice_link.py index 9cf6c372d..46e38a463 100644 --- a/pay-api/src/pay_api/models/eft_credit_invoice_link.py +++ b/pay-api/src/pay_api/models/eft_credit_invoice_link.py @@ -37,13 +37,17 @@ class EFTCreditInvoiceLink(BaseModel): # pylint: disable=too-few-public-methods __mapper_args__ = { 'include_properties': [ 'id', + 'amount', 'created_on', 'eft_credit_id', - 'invoice_id' + 'invoice_id', + 'status_code' ] } id = db.Column(db.Integer, primary_key=True, autoincrement=True) invoice_id = db.Column(db.Integer, ForeignKey('invoices.id'), nullable=False, index=True) eft_credit_id = db.Column(db.Integer, ForeignKey('eft_credits.id'), nullable=False, index=True) + amount = db.Column(db.Numeric(19, 2), nullable=True) + status_code = db.Column('status_code', db.String(25), nullable=False, index=True) created_on = db.Column('created_on', db.DateTime, nullable=False, default=datetime.now) diff --git a/pay-api/src/pay_api/models/eft_short_name_links.py b/pay-api/src/pay_api/models/eft_short_name_links.py index 5d2cb17db..1186f383c 100644 --- a/pay-api/src/pay_api/models/eft_short_name_links.py +++ b/pay-api/src/pay_api/models/eft_short_name_links.py @@ -22,7 +22,6 @@ from .base_model import BaseModel from .db import db -from ..utils.util import cents_to_decimal class EFTShortnameLinks(Versioned, BaseModel): # pylint: disable=too-many-instance-attributes @@ -77,7 +76,7 @@ class EFTShortnameLinkSchema: # pylint: disable=too-few-public-methods account_id: str account_name: str account_branch: str - latest_statement_id: str + statement_id: str amount_owing: Decimal updated_by: str updated_by_name: str @@ -95,8 +94,8 @@ def from_row(cls, row: EFTShortnameLinks): account_id=row.auth_account_id, account_name=getattr(row, 'account_name', None), account_branch=getattr(row, 'account_branch', None), - latest_statement_id=getattr(row, 'statement_id', None), - amount_owing=cents_to_decimal(getattr(row, 'amount_owing', None)), + statement_id=getattr(row, 'latest_statement_id', None), + amount_owing=getattr(row, 'total_owing', None), updated_by=row.updated_by, updated_by_name=row.updated_by_name, updated_on=row.updated_on diff --git a/pay-api/src/pay_api/models/eft_transaction.py b/pay-api/src/pay_api/models/eft_transaction.py index aef3c82fd..3f4fbb68b 100644 --- a/pay-api/src/pay_api/models/eft_transaction.py +++ b/pay-api/src/pay_api/models/eft_transaction.py @@ -14,6 +14,7 @@ """Model to handle EFT file processing.""" from datetime import datetime from _decimal import Decimal + from attrs import define from sqlalchemy import ForeignKey, String @@ -21,7 +22,6 @@ from .base_model import BaseModel from .db import db -from ..utils.util import cents_to_decimal class EFTTransaction(BaseModel): # pylint: disable=too-many-instance-attributes @@ -81,13 +81,17 @@ class EFTTransaction(BaseModel): # pylint: disable=too-many-instance-attributes @define class EFTTransactionSchema: # pylint: disable=too-few-public-methods - """Main schema used to serialize a EFT Transaction.""" + """Main schema used to serialize an EFT Transaction.""" - id: int + transaction_id: int + account_id: str + account_name: str + account_branch: str + statement_id: int short_name_id: int transaction_date: datetime - deposit_date: datetime - deposit_amount: Decimal + transaction_amount: Decimal + transaction_description: str @classmethod def from_row(cls, row: EFTTransaction): @@ -95,8 +99,12 @@ def from_row(cls, row: EFTTransaction): https://www.attrs.org/en/stable/init.html """ - return cls(id=row.id, + return cls(transaction_id=row.transaction_id, short_name_id=row.short_name_id, + account_id=getattr(row, 'auth_account_id', None), + account_name=getattr(row, 'account_name', None), + account_branch=getattr(row, 'account_branch', None), + statement_id=getattr(row, 'statement_id', None), transaction_date=getattr(row, 'transaction_date', None), - deposit_date=getattr(row, 'deposit_date', None), - deposit_amount=cents_to_decimal(getattr(row, 'deposit_amount_cents', None))) + transaction_amount=getattr(row, 'transaction_amount', None), + transaction_description=getattr(row, 'transaction_description', None)) diff --git a/pay-api/src/pay_api/resources/v1/eft_short_names.py b/pay-api/src/pay_api/resources/v1/eft_short_names.py index 5b9e7c88a..1a32ad772 100644 --- a/pay-api/src/pay_api/resources/v1/eft_short_names.py +++ b/pay-api/src/pay_api/resources/v1/eft_short_names.py @@ -83,7 +83,7 @@ def get_eft_shortname_summaries(): payment_received_end_date = request.args.get('paymentReceivedEndDate', None) response, status = EFTShortnameSummariesService.search(EFTShortnamesSearch( - id=short_name_id, + id=string_to_int(short_name_id), deposit_start_date=string_to_date(payment_received_start_date), deposit_end_date=string_to_date(payment_received_end_date), credit_remaining=string_to_decimal(credits_remaining), diff --git a/pay-api/src/pay_api/services/eft_short_name_summaries.py b/pay-api/src/pay_api/services/eft_short_name_summaries.py index 52ed6d707..82617c824 100644 --- a/pay-api/src/pay_api/services/eft_short_name_summaries.py +++ b/pay-api/src/pay_api/services/eft_short_name_summaries.py @@ -18,13 +18,14 @@ from sqlalchemy import and_, func, or_ from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceModel from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTShortnames as EFTShortnameModel from pay_api.models import EFTShortnameSummarySchema as EFTSummarySchema from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import db from pay_api.services.eft_short_names import EFTShortnamesSearch -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus +from pay_api.utils.enums import EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus, EFTShortnameStatus from pay_api.utils.util import unstructure_schema_items @@ -80,7 +81,13 @@ def get_linked_count_query(): def get_remaining_credit_query(): """Query for EFT remaining credit amount.""" return (db.session.query(EFTCreditModel.short_name_id, - func.sum(EFTCreditModel.remaining_amount).label('total')) + (func.coalesce( + func.sum(EFTCreditModel.remaining_amount), 0) - func.coalesce( + func.sum(EFTCreditInvoiceModel.amount), 0)) + .label('total')) + .outerjoin(EFTCreditInvoiceModel, + and_(EFTCreditInvoiceModel.eft_credit_id == EFTCreditModel.id, + EFTCreditInvoiceModel.status_code == EFTCreditInvoiceStatus.PENDING.value)) .group_by(EFTCreditModel.short_name_id)) @staticmethod diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index 0c1bbb4ff..2ddb98b28 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -49,6 +49,7 @@ class EFTShortnamesSearch: # pylint: disable=too-many-instance-attributes id: Optional[int] = None account_id: Optional[str] = None + allow_partial_account_id: Optional[bool] = True account_name: Optional[str] = None account_branch: Optional[str] = None amount_owing: Optional[Decimal] = None @@ -75,7 +76,7 @@ def create_shortname_link(cls, short_name_id: int, auth_account_id: str, **kwarg if auth_account_id is None: raise BusinessException(Error.EFT_SHORT_NAME_ACCOUNT_ID_REQUIRED) - short_name: EFTShortnameModel = cls.find_by_auth_account_id_state(short_name_id, + short_name: EFTShortnameModel = cls.find_by_auth_account_id_state(auth_account_id, [EFTShortnameStatus.LINKED.value, EFTShortnameStatus.PENDING.value]) @@ -99,17 +100,33 @@ def create_shortname_link(cls, short_name_id: int, auth_account_id: str, **kwarg def get_shortname_links(cls, short_name_id: int) -> List[EFTShortnameLinksModel]: """Get EFT short name account links.""" current_app.logger.debug('get_shortname_links') return { @@ -175,11 +192,11 @@ def find_by_auth_account_id_state(cls, auth_account_id: str, state: List[str]) - current_app.logger.debug('find_by_auth_account_id_state') return result @@ -241,6 +258,15 @@ def get_search_count(cls, search_criteria: EFTShortnamesSearch): current_app.logger.debug('>get_search_count') return count_query.count() + @staticmethod + def add_payment_account_name_columns(query): + """Add payment account name and branch to query select columns.""" + return query.add_columns(case( + (PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), + func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') + ), else_=PaymentAccountModel.name).label('account_name'), + PaymentAccountModel.branch_name.label('account_branch')) + @classmethod def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = False): """Query for short names based on search criteria.""" @@ -269,12 +295,8 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = # Join payment information if this is NOT the count query if not is_count: + query = cls.add_payment_account_name_columns(query) query = (query.add_columns( - case( - (PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), - func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') - ), else_=PaymentAccountModel.name).label('account_name'), - PaymentAccountModel.branch_name.label('account_branch'), statement_summary_query.c.total_owing, statement_summary_query.c.latest_statement_id ).outerjoin( @@ -285,7 +307,8 @@ def get_search_query(cls, search_criteria: EFTShortnamesSearch, is_count: bool = # Short name link filters query = query.filter_conditionally(search_criteria.id, EFTShortnameModel.id) query = query.filter_conditionally(search_criteria.account_id, - EFTShortnameLinksModel.auth_account_id, is_like=True) + EFTShortnameLinksModel.auth_account_id, + is_like=True) # Payment account filters query = query.filter_conditionally(search_criteria.account_name, PaymentAccountModel.name, is_like=True) query = query.filter_conditionally(search_criteria.account_branch, PaymentAccountModel.branch_name, diff --git a/pay-api/src/pay_api/services/eft_transactions.py b/pay-api/src/pay_api/services/eft_transactions.py index 5dddacc47..99eda5c16 100644 --- a/pay-api/src/pay_api/services/eft_transactions.py +++ b/pay-api/src/pay_api/services/eft_transactions.py @@ -15,13 +15,19 @@ from dataclasses import dataclass from typing import Optional -from sqlalchemy import func +from sqlalchemy import Float, and_, case, cast, desc, func, literal, null from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel from pay_api.models import EFTTransaction as EFTTransactionModel -from pay_api.models import EFTTransactionSchema, db -from pay_api.utils.converter import Converter -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus +from pay_api.models import EFTTransactionSchema +from pay_api.models import Invoice as InvoiceModel +from pay_api.models import PaymentAccount as PaymentAccountModel +from pay_api.models import Statement as StatementModel +from pay_api.models import StatementInvoices as StatementInvoicesModel +from pay_api.models import db +from pay_api.utils.enums import EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus +from pay_api.utils.util import unstructure_schema_items @dataclass @@ -36,36 +42,84 @@ class EFTTransactions: """Service to manage EFT Transactions.""" @staticmethod - def get_remaining_credits(short_name_id: int): - """Return the remaining credit for a short name.""" - return db.session.query(func.sum(EFTCreditModel.remaining_amount))\ - .filter(EFTCreditModel.short_name_id == short_name_id)\ - .group_by(EFTCreditModel.short_name_id).scalar() + def get_account_name(): + """Return case statement for deriving payment account name.""" + return case( + (PaymentAccountModel.name.like('%-' + PaymentAccountModel.branch_name), + func.replace(PaymentAccountModel.name, '-' + PaymentAccountModel.branch_name, '') + ), else_=PaymentAccountModel.name).label('account_name') + + @staticmethod + def get_funds_received_query(): + """Return the EFT transaction funds received.""" + # Null valued columns are defined for the purposes of unioning with funds applied query + # We don't need the account information and there will be no statement for EFT Transactions received + # through a TDI17 + return db.session.query(EFTTransactionModel.id.label('transaction_id'), + EFTTransactionModel.short_name_id, + EFTTransactionModel.deposit_date.label('transaction_date'), + (cast( + EFTTransactionModel.deposit_amount_cents, Float) / 100 + ).label('transaction_amount'), + literal('Funds Received').label('transaction_description'), + null().label('statement_id'), + null().label('auth_account_id'), + null().label('account_name'), + null().label('account_branch')) + + @staticmethod + def get_funds_applied_query(): + """Return the EFT transaction funds applied by account statement.""" + return (db.session.query(StatementModel.payment_account_id, + EFTCreditModel.short_name_id, + func.max(InvoiceModel.payment_date).label('transaction_date'), + StatementInvoicesModel.statement_id, + func.sum(EFTCreditInvoiceLinkModel.amount).label('paid_amount')) + .join(InvoiceModel, InvoiceModel.id == StatementInvoicesModel.invoice_id) + .join(StatementModel, StatementModel.id == StatementInvoicesModel.statement_id) + .join(EFTCreditInvoiceLinkModel, + and_(EFTCreditInvoiceLinkModel.invoice_id == StatementInvoicesModel.invoice_id, + EFTCreditInvoiceLinkModel.status_code == EFTCreditInvoiceStatus.COMPLETED.value)) + .join(EFTCreditModel, EFTCreditModel.id == EFTCreditInvoiceLinkModel.eft_credit_id) + .group_by(StatementModel.payment_account_id, + EFTCreditModel.short_name_id, + StatementInvoicesModel.statement_id) + ) @classmethod def search(cls, short_name_id: int, search_criteria: EFTTransactionSearch = EFTTransactionSearch()) -> [EFTTransactionModel]: """Return EFT Transfers by search criteria.""" - query = db.session.query(EFTTransactionModel) \ - .filter(EFTTransactionModel.short_name_id == short_name_id) \ - .filter(EFTTransactionModel.status_code == EFTProcessStatus.COMPLETED.value) \ - .filter(EFTTransactionModel.line_type == EFTFileLineType.TRANSACTION.value) \ - .order_by(EFTTransactionModel.transaction_date.desc()) + funds_received_query = (cls.get_funds_received_query() + .filter(EFTTransactionModel.short_name_id == short_name_id) + .filter(EFTTransactionModel.status_code == EFTProcessStatus.COMPLETED.value) + .filter(EFTTransactionModel.line_type == EFTFileLineType.TRANSACTION.value)) + + funds_applied_subquery = cls.get_funds_applied_query().subquery() + funds_applied_query = (db.session.query(null().label('transaction_id'), + funds_applied_subquery.c.short_name_id, + funds_applied_subquery.c.transaction_date, + funds_applied_subquery.c.paid_amount.label('transaction_amount'), + literal('Statement Paid').label('transaction_description'), + funds_applied_subquery.c.statement_id, + PaymentAccountModel.auth_account_id, + cls.get_account_name(), + PaymentAccountModel.branch_name) + .join(funds_applied_subquery, + and_(funds_applied_subquery.c.payment_account_id == PaymentAccountModel.id, + funds_applied_subquery.c.short_name_id == short_name_id))) - pagination = query.paginate(per_page=search_criteria.limit, - page=search_criteria.page) + union_query = funds_received_query.union(funds_applied_query) + union_query = union_query.order_by(desc('transaction_date')) - transaction_list = [EFTTransactionSchema.from_row(transaction) for transaction in pagination.items] - converter = Converter() - transaction_list = converter.unstructure(transaction_list) + pagination = union_query.paginate(per_page=search_criteria.limit, + page=search_criteria.page) - remaining_credit = cls.get_remaining_credits(short_name_id) - remaining_credit = float(remaining_credit) if remaining_credit else 0 + transaction_list = unstructure_schema_items(EFTTransactionSchema, pagination.items) return { 'page': search_criteria.page, 'limit': search_criteria.limit, 'items': transaction_list, - 'total': pagination.total, - 'remaining_credit': remaining_credit + 'total': pagination.total } diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index bf40d070e..1437c2e02 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -306,6 +306,15 @@ class CfsReceiptStatus(Enum): REV = 'REV' +class EFTCreditInvoiceStatus(Enum): + """EFT Credit Invoice Link Status.""" + + COMPLETED = 'COMPLETED' + PENDING = 'PENDING' + PENDING_REFUND = 'PENDING_REFUND' + REFUNDED = 'REFUNDED' + + class EFTProcessStatus(Enum): """EFT Process Status.""" diff --git a/pay-api/src/pay_api/version.py b/pay-api/src/pay_api/version.py index b02bfda58..7003370ab 100644 --- a/pay-api/src/pay_api/version.py +++ b/pay-api/src/pay_api/version.py @@ -22,4 +22,4 @@ Development release segment: .devN """ -__version__ = '1.20.12' # pylint: disable=invalid-name +__version__ = '1.20.13' # pylint: disable=invalid-name diff --git a/pay-api/tests/unit/api/test_eft_short_names.py b/pay-api/tests/unit/api/test_eft_short_names.py index c6a917e9d..040c6fadf 100755 --- a/pay-api/tests/unit/api/test_eft_short_names.py +++ b/pay-api/tests/unit/api/test_eft_short_names.py @@ -100,8 +100,21 @@ def test_get_eft_short_name_links(session, client, jwt, app): token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value], username='IDIR/JSMITH'), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1234', + name='ABC-123', + branch_name='123').save() short_name = factory_eft_shortname(short_name='TESTSHORTNAME').save() + invoice = factory_invoice(account, payment_method_code=PaymentMethod.EFT.value, + total=50, paid=0).save() + statement_settings = factory_statement_settings(payment_account_id=account.id, + frequency=StatementFrequency.MONTHLY.value) + statement = factory_statement(payment_account_id=account.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=statement_settings.id) + factory_statement_invoices(statement_id=statement.id, invoice_id=invoice.id) + # Assert an empty result set is properly returned rv = client.get(f'/api/v1/eft-shortnames/{short_name.id}/links', headers=headers) @@ -114,7 +127,7 @@ def test_get_eft_short_name_links(session, client, jwt, app): # Create a short name link rv = client.post(f'/api/v1/eft-shortnames/{short_name.id}/links', - data=json.dumps({'accountId': '1234'}), + data=json.dumps({'accountId': account.auth_account_id}), headers=headers) link_dict = rv.json @@ -131,9 +144,14 @@ def test_get_eft_short_name_links(session, client, jwt, app): assert len(link_list_dict['items']) == 1 link = link_list_dict['items'][0] - assert link['accountId'] == '1234' + assert link['accountId'] == account.auth_account_id assert link['id'] == link_dict['id'] assert link['shortNameId'] == short_name.id + assert link['accountId'] == account.auth_account_id + assert link['accountName'] == 'ABC' + assert link['accountBranch'] == '123' + assert link['amountOwing'] == invoice.total + assert link['statementId'] == statement.id assert link['statusCode'] == EFTShortnameStatus.PENDING.value assert link['updatedBy'] == 'IDIR/JSMITH' diff --git a/pay-api/tests/unit/api/test_eft_transactions.py b/pay-api/tests/unit/api/test_eft_transactions.py index 1748a76ab..108edd0a5 100755 --- a/pay-api/tests/unit/api/test_eft_transactions.py +++ b/pay-api/tests/unit/api/test_eft_transactions.py @@ -20,33 +20,39 @@ from datetime import datetime from pay_api.models import EFTCredit as EFTCreditModel +from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel from pay_api.models import EFTFile as EFTFileModel from pay_api.models import EFTShortnames as EFTShortnamesModel from pay_api.models import EFTTransaction as EFTTransactionModel -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, PaymentMethod, Role +from pay_api.utils.enums import ( + EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus, InvoiceStatus, PaymentMethod, Role, StatementFrequency) from tests.utilities.base_test import ( - factory_eft_file, factory_eft_shortname, factory_eft_shortname_link, factory_payment_account, get_claims, - token_header) + factory_eft_file, factory_eft_shortname, factory_eft_shortname_link, factory_invoice, factory_payment_account, + factory_statement, factory_statement_invoices, factory_statement_settings, get_claims, token_header) -def assert_transaction(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel): - """Assert short name result.""" +def assert_funds_received(result_dict: dict, short_name: EFTShortnamesModel, transaction: EFTTransactionModel): + """Assert funds received rows.""" date_format = '%Y-%m-%dT%H:%M:%S' - assert result_dict['id'] == transaction.id + assert result_dict['transactionId'] == transaction.id + assert not result_dict['accountId'] + assert not result_dict['accountName'] + assert not result_dict['accountBranch'] + assert not result_dict['statementId'] assert result_dict['shortNameId'] == short_name.id - assert result_dict['depositAmount'] == transaction.deposit_amount_cents / 100 - assert datetime.strptime(result_dict['depositDate'], date_format) == transaction.deposit_date - assert datetime.strptime(result_dict['transactionDate'], date_format) == transaction.transaction_date + assert result_dict['transactionAmount'] == transaction.deposit_amount_cents / 100 + assert datetime.strptime(result_dict['transactionDate'], date_format) == transaction.deposit_date + assert result_dict['transactionDescription'] == 'Funds Received' -def test_search_short_name_transactions(session, client, jwt, app): - """Assert that EFT short names transactions can be searched.""" +def test_search_short_name_funds_received(session, client, jwt, app): + """Assert that EFT short names funds received can be searched.""" token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} # create test data payment_account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, - auth_account_id='1234').save() + auth_account_id='1234', name='ABC-BRANCH', branch_name='BRANCH').save() eft_file: EFTFileModel = factory_eft_file() short_name_1 = factory_eft_shortname(short_name='TESTSHORTNAME1').save() short_name_2 = factory_eft_shortname(short_name='TESTSHORTNAME2').save() @@ -62,8 +68,8 @@ def test_search_short_name_transactions(session, client, jwt, app): line_number=1, file_id=eft_file.id, status_code=EFTProcessStatus.COMPLETED.value, - transaction_date=datetime(2024, 1, 5, 2, 30), - deposit_date=datetime(2024, 1, 6, 10, 5), + transaction_date=datetime(2024, 1, 4, 2, 30), + deposit_date=datetime(2024, 1, 5, 10, 5), deposit_amount_cents=10150, short_name_id=short_name_1.id @@ -106,7 +112,7 @@ def test_search_short_name_transactions(session, client, jwt, app): ).save() # short name 2 credit - s2_credit1 = EFTCreditModel( + EFTCreditModel( eft_file_id=eft_file.id, eft_transaction_id=s2_transaction1.id, short_name_id=short_name_2.id, @@ -114,7 +120,8 @@ def test_search_short_name_transactions(session, client, jwt, app): amount=302.50, remaining_amount=302.50 ).save() - s2_credit2 = EFTCreditModel( + + EFTCreditModel( eft_file_id=eft_file.id, eft_transaction_id=s2_transaction1.id, short_name_id=short_name_2.id, @@ -135,11 +142,11 @@ def test_search_short_name_transactions(session, client, jwt, app): assert result_dict['items'] is not None assert len(result_dict['items']) == 3 # Most recent transaction date first - assert_transaction(result_dict['items'][0], short_name_1, s1_transaction3) - assert_transaction(result_dict['items'][1], short_name_1, s1_transaction2) - assert_transaction(result_dict['items'][2], short_name_1, s1_transaction1) + assert_funds_received(result_dict['items'][0], short_name_1, s1_transaction3) + assert_funds_received(result_dict['items'][1], short_name_1, s1_transaction2) + assert_funds_received(result_dict['items'][2], short_name_1, s1_transaction1) - # Assert search returns unlinked short names + # Assert search returns funds received rows rv = client.get(f'/api/v1/eft-shortnames/{short_name_2.id}/transactions', headers=headers) assert rv.status_code == 200 @@ -150,5 +157,124 @@ def test_search_short_name_transactions(session, client, jwt, app): assert result_dict['limit'] == 10 assert result_dict['items'] is not None assert len(result_dict['items']) == 1 - assert result_dict['remainingCredit'] == (s2_credit1.remaining_amount + s2_credit2.remaining_amount) - assert_transaction(result_dict['items'][0], short_name_2, s2_transaction1) + assert_funds_received(result_dict['items'][0], short_name_2, s2_transaction1) + + +def test_search_short_name_funds_applied(session, client, jwt, app): + """Assert that EFT short names funds applied can be searched.""" + token = jwt.create_jwt(get_claims(roles=[Role.MANAGE_EFT.value]), token_header) + headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'} + + # create test data + payment_account = factory_payment_account(payment_method_code=PaymentMethod.EFT.value, + auth_account_id='1234', name='ABC-BRANCH', branch_name='BRANCH').save() + eft_file: EFTFileModel = factory_eft_file() + short_name = factory_eft_shortname(short_name='TESTSHORTNAME1').save() + + # Set up scenario where statement invoices have been paid via EFT + # EFT Transactions from TDI17 have been processed and EFT Credit records have been created and used. + transaction1: EFTTransactionModel = EFTTransactionModel( + line_type=EFTFileLineType.TRANSACTION.value, + line_number=1, + file_id=eft_file.id, + status_code=EFTProcessStatus.COMPLETED.value, + transaction_date=datetime(2024, 1, 4, 2, 30), + deposit_date=datetime(2024, 1, 5, 10, 5), + deposit_amount_cents=10150, + short_name_id=short_name.id).save() + t1_eft_credit = EFTCreditModel( + eft_file_id=eft_file.id, + eft_transaction_id=transaction1.id, + short_name_id=short_name.id, + payment_account_id=payment_account.id, + amount=101.50, + remaining_amount=0).save() + + transaction2 = EFTTransactionModel( + line_type=EFTFileLineType.TRANSACTION.value, + line_number=1, + file_id=eft_file.id, + status_code=EFTProcessStatus.COMPLETED.value, + transaction_date=datetime(2024, 1, 5, 3, 30), + deposit_date=datetime(2024, 1, 6, 10, 5), + deposit_amount_cents=10000, + short_name_id=short_name.id).save() + + t2_eft_credit = EFTCreditModel( + eft_file_id=eft_file.id, + eft_transaction_id=transaction2.id, + short_name_id=short_name.id, + payment_account_id=payment_account.id, + amount=10.25, + remaining_amount=10.25).save() + + # Create statement and invoices that have been paid + statement_payment_date = datetime(2024, 1, 10, 9, 0) + invoice_1 = factory_invoice(payment_account, payment_method_code=PaymentMethod.EFT.value, + total=105.50, paid=0, status_code=InvoiceStatus.PAID.value).save() + invoice_1.payment_date = statement_payment_date + invoice_1.save() + invoice_2 = factory_invoice(payment_account, payment_method_code=PaymentMethod.EFT.value, + total=96.00, paid=0, status_code=InvoiceStatus.PAID.value).save() + invoice_2.payment_date = statement_payment_date + invoice_2.save() + + statement_settings = factory_statement_settings(payment_account_id=payment_account.id, + frequency=StatementFrequency.MONTHLY.value) + statement = factory_statement(payment_account_id=payment_account.id, + frequency=StatementFrequency.MONTHLY.value, + statement_settings_id=statement_settings.id) + factory_statement_invoices(statement_id=statement.id, invoice_id=invoice_1.id) + factory_statement_invoices(statement_id=statement.id, invoice_id=invoice_2.id) + + # Establish EFT Credit to invoice links for applied funds and amount + EFTCreditInvoiceLinkModel( + eft_credit_id=t1_eft_credit.id, + invoice_id=invoice_1.id, + amount=101.50, + status_code=EFTCreditInvoiceStatus.COMPLETED.value + ).save() + + # Testing transactions query where first set of credits were insufficient and a second set was applied + # This should just roll up as a total amount paid + EFTCreditInvoiceLinkModel( + eft_credit_id=t2_eft_credit.id, + invoice_id=invoice_1.id, + amount=4.00, + status_code=EFTCreditInvoiceStatus.COMPLETED.value + ).save() + + # Remaining credit paid to invoice 2 + EFTCreditInvoiceLinkModel( + eft_credit_id=t2_eft_credit.id, + invoice_id=invoice_2.id, + amount=96.00, + status_code=EFTCreditInvoiceStatus.COMPLETED.value + ).save() + + # Assert search returns funds received and funds applied rows + rv = client.get(f'/api/v1/eft-shortnames/{short_name.id}/transactions', headers=headers) + assert rv.status_code == 200 + + result_dict = rv.json + assert result_dict is not None + assert result_dict['page'] == 1 + assert result_dict['total'] == 3 + assert result_dict['limit'] == 10 + assert result_dict['items'] is not None + assert len(result_dict['items']) == 3 + + statement_paid = result_dict['items'][0] + date_format = '%Y-%m-%dT%H:%M:%S' + assert not statement_paid['transactionId'] + assert statement_paid['statementId'] == statement.id + assert statement_paid['accountId'] == payment_account.auth_account_id + assert statement_paid['accountName'] == 'ABC' + assert statement_paid['accountBranch'] == 'BRANCH' + assert statement_paid['shortNameId'] == short_name.id + assert statement_paid['transactionAmount'] == 201.50 + assert datetime.strptime(statement_paid['transactionDate'], date_format) == statement_payment_date + assert statement_paid['transactionDescription'] == 'Statement Paid' + + assert_funds_received(result_dict['items'][1], short_name, transaction2) + assert_funds_received(result_dict['items'][2], short_name, transaction1) diff --git a/pay-api/tests/unit/models/test_eft_credit_invoice_link.py b/pay-api/tests/unit/models/test_eft_credit_invoice_link.py index bbb2e9ac2..42c7db5ee 100644 --- a/pay-api/tests/unit/models/test_eft_credit_invoice_link.py +++ b/pay-api/tests/unit/models/test_eft_credit_invoice_link.py @@ -18,7 +18,7 @@ """ from pay_api.models import EFTCredit, EFTCreditInvoiceLink, EFTFile, EFTShortnames, EFTTransaction -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus +from pay_api.utils.enums import EFTCreditInvoiceStatus, EFTFileLineType, EFTProcessStatus, EFTShortnameStatus from tests.utilities.base_test import factory_invoice, factory_payment_account @@ -63,9 +63,13 @@ def test_eft_credit_invoice_link(session): eft_credit_invoice_link = EFTCreditInvoiceLink() eft_credit_invoice_link.invoice_id = invoice.id eft_credit_invoice_link.eft_credit_id = eft_credit.id + eft_credit_invoice_link.status_code = EFTCreditInvoiceStatus.PENDING.value + eft_credit_invoice_link.amount = 50.00 eft_credit_invoice_link.save() eft_credit_invoice_link = EFTCreditInvoiceLink.find_by_id(eft_credit_invoice_link.id) assert eft_credit_invoice_link.id is not None assert eft_credit_invoice_link.eft_credit_id == eft_credit.id assert eft_credit_invoice_link.invoice_id == invoice.id + assert eft_credit_invoice_link.status_code == EFTCreditInvoiceStatus.PENDING.value + assert eft_credit_invoice_link.amount == 50.00 From cd3901d1dd075f4fa1cad7561df8112608f606a1 Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Mon, 27 May 2024 12:09:06 -0700 Subject: [PATCH 61/87] 20419 - Short name linking status (#1540) - Handle basic unlinking for short name still in pending - return whether associated account link has pending payments --- .../pay_api/models/eft_short_name_links.py | 4 ++- .../pay_api/resources/v1/eft_short_names.py | 22 +++++++++++++ .../src/pay_api/services/eft_short_names.py | 32 ++++++++++++++++-- pay-api/src/pay_api/utils/errors.py | 1 + .../tests/unit/api/test_eft_short_names.py | 33 +++++++++++++++++++ 5 files changed, 88 insertions(+), 4 deletions(-) diff --git a/pay-api/src/pay_api/models/eft_short_name_links.py b/pay-api/src/pay_api/models/eft_short_name_links.py index 1186f383c..f439c5873 100644 --- a/pay-api/src/pay_api/models/eft_short_name_links.py +++ b/pay-api/src/pay_api/models/eft_short_name_links.py @@ -81,6 +81,7 @@ class EFTShortnameLinkSchema: # pylint: disable=too-few-public-methods updated_by: str updated_by_name: str updated_on: datetime + has_pending_payment: bool @classmethod def from_row(cls, row: EFTShortnameLinks): @@ -98,5 +99,6 @@ def from_row(cls, row: EFTShortnameLinks): amount_owing=getattr(row, 'total_owing', None), updated_by=row.updated_by, updated_by_name=row.updated_by_name, - updated_on=row.updated_on + updated_on=row.updated_on, + has_pending_payment=bool(getattr(row, 'invoice_count', 0)) ) diff --git a/pay-api/src/pay_api/resources/v1/eft_short_names.py b/pay-api/src/pay_api/resources/v1/eft_short_names.py index 1a32ad772..1fadbc58f 100644 --- a/pay-api/src/pay_api/resources/v1/eft_short_names.py +++ b/pay-api/src/pay_api/resources/v1/eft_short_names.py @@ -171,3 +171,25 @@ def post_eft_shortname_link(short_name_id: int): current_app.logger.debug('>post_eft_shortname_link') return jsonify(response), status + + +@bp.route('//links/', methods=['DELETE', 'OPTIONS']) +@cross_origin(origins='*', methods=['DELETE']) +@_jwt.has_one_of_roles([Role.SYSTEM.value, Role.MANAGE_EFT.value]) +def delete_eft_shortname_link(short_name_id: int, short_name_link_id: int): + """Delete EFT short name to account link.""" + current_app.logger.info('delete_eft_shortname_link') + return jsonify(response), status diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index 2ddb98b28..9d1da9b71 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -27,9 +27,10 @@ from pay_api.exceptions import BusinessException from pay_api.factory.payment_system_factory import PaymentSystemFactory from pay_api.models import CfsAccount as CfsAccountModel -from pay_api.models import EFTShortnames as EFTShortnameModel +from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel from pay_api.models import EFTShortnameLinks as EFTShortnameLinksModel from pay_api.models import EFTShortnameLinkSchema +from pay_api.models import EFTShortnames as EFTShortnameModel from pay_api.models import EFTShortnameSchema from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel @@ -37,7 +38,7 @@ from pay_api.models import StatementInvoices as StatementInvoicesModel from pay_api.models import db from pay_api.utils.converter import Converter -from pay_api.utils.enums import EFTShortnameStatus, InvoiceStatus, PaymentMethod +from pay_api.utils.enums import EFTCreditInvoiceStatus, EFTShortnameStatus, InvoiceStatus, PaymentMethod from pay_api.utils.errors import Error from pay_api.utils.user_context import user_context from pay_api.utils.util import unstructure_schema_items @@ -96,18 +97,33 @@ def create_shortname_link(cls, short_name_id: int, auth_account_id: str, **kwarg current_app.logger.debug('>create_shortname_link') return cls.find_link_by_id(eft_short_name_link.id) + @classmethod + def delete_shortname_link(cls, short_name_link_id: int): + """Delete EFT short name auth account link.""" + current_app.logger.debug('delete_shortname_link') + @classmethod def get_shortname_links(cls, short_name_id: int) -> List[EFTShortnameLinksModel]: """Get EFT short name account links.""" current_app.logger.debug(' Date: Thu, 30 May 2024 20:12:01 -0700 Subject: [PATCH 62/87] Merge branch 'main' of https://github.com/bcgov/sbc-pay into feature-queue-python-upgrade --- .github/workflows/pay-queue-cd.yml | 118 +++++++++++++++--- .github/workflows/pay-queue-ci.yml | 1 - .github/workflows/pay-queue-gcp-cd.yml | 33 +++++ .gitignore | 3 + bcol-api/requirements.txt | 2 +- jobs/ftp-poller/config.py | 8 +- jobs/ftp-poller/devops/vaults.json | 7 ++ jobs/ftp-poller/invoke_jobs.py | 3 + jobs/ftp-poller/poetry.lock | 2 +- .../tasks/cgi_feeder_poller_task.py | 6 +- jobs/ftp-poller/tasks/eft_poller_ftp.py | 4 +- jobs/ftp-poller/tests/jobs/test_sftp.py | 15 ++- jobs/ftp-poller/utils/utils.py | 14 ++- jobs/notebook-report/requirements.txt | 2 +- jobs/notebook-report/requirements/prod.txt | 4 +- jobs/payment-jobs/config.py | 5 + jobs/payment-jobs/devops/vaults.json | 70 +++++++++++ jobs/payment-jobs/invoke_jobs.py | 2 + .../tasks/activate_pad_account_task.py | 5 +- jobs/payment-jobs/tasks/ap_task.py | 4 +- .../tasks/cfs_create_account_task.py | 3 +- .../tasks/cfs_create_invoice_task.py | 4 +- .../tasks/unpaid_invoice_notify_task.py | 6 +- jobs/payment-jobs/tests/jobs/conftest.py | 2 - .../tests/jobs/test_statement_due_task.py | 1 + jobs/payment-jobs/utils/mailer.py | 28 ++--- pay-api/devops/vaults.json | 59 +++++++++ pay-api/gunicorn_config.py | 2 +- pay-api/manage.py | 2 +- pay-api/scripts/verify_license_headers.sh | 4 +- pay-api/setup.cfg | 2 +- pay-api/setup.py | 2 +- pay-api/src/pay_api/__init__.py | 4 +- pay-api/src/pay_api/config.py | 16 ++- pay-api/src/pay_api/exceptions/__init__.py | 2 +- pay-api/src/pay_api/factory/__init__.py | 2 +- .../pay_api/factory/payment_system_factory.py | 2 +- pay-api/src/pay_api/models/__init__.py | 2 +- pay-api/src/pay_api/models/account_fee.py | 2 +- pay-api/src/pay_api/models/audit.py | 2 +- pay-api/src/pay_api/models/base_model.py | 2 +- pay-api/src/pay_api/models/base_schema.py | 2 +- pay-api/src/pay_api/models/cfs_account.py | 2 +- .../pay_api/models/cfs_account_status_code.py | 2 +- pay-api/src/pay_api/models/code_table.py | 2 +- pay-api/src/pay_api/models/comment.py | 2 +- pay-api/src/pay_api/models/corp_type.py | 2 +- pay-api/src/pay_api/models/credit.py | 2 +- pay-api/src/pay_api/models/db.py | 2 +- .../models/disbursement_status_code.py | 2 +- pay-api/src/pay_api/models/ejv_file.py | 2 +- pay-api/src/pay_api/models/ejv_header.py | 2 +- pay-api/src/pay_api/models/ejv_link.py | 2 +- pay-api/src/pay_api/models/error_code.py | 2 +- pay-api/src/pay_api/models/fee_code.py | 2 +- pay-api/src/pay_api/models/fee_schedule.py | 2 +- pay-api/src/pay_api/models/filing_type.py | 2 +- pay-api/src/pay_api/models/invoice.py | 2 +- pay-api/src/pay_api/models/invoice_batch.py | 2 +- .../src/pay_api/models/invoice_batch_link.py | 2 +- .../src/pay_api/models/invoice_reference.py | 2 +- .../models/invoice_reference_status_code.py | 2 +- .../src/pay_api/models/invoice_status_code.py | 2 +- .../pay_api/models/line_item_status_code.py | 2 +- .../pay_api/models/non_sufficient_funds.py | 2 +- .../models/notification_status_code.py | 2 +- pay-api/src/pay_api/models/payment.py | 2 +- pay-api/src/pay_api/models/payment_account.py | 2 +- .../src/pay_api/models/payment_line_item.py | 2 +- pay-api/src/pay_api/models/payment_method.py | 2 +- .../src/pay_api/models/payment_status_code.py | 2 +- pay-api/src/pay_api/models/payment_system.py | 2 +- .../src/pay_api/models/payment_transaction.py | 2 +- pay-api/src/pay_api/models/receipt.py | 2 +- pay-api/src/pay_api/models/refund.py | 2 +- pay-api/src/pay_api/models/routing_slip.py | 2 +- .../models/routing_slip_status_code.py | 2 +- pay-api/src/pay_api/models/statement.py | 2 +- .../src/pay_api/models/statement_invoices.py | 2 +- .../pay_api/models/statement_recipients.py | 2 +- .../src/pay_api/models/statement_settings.py | 2 +- .../pay_api/models/transaction_status_code.py | 2 +- pay-api/src/pay_api/resources/__init__.py | 2 +- pay-api/src/pay_api/resources/ops.py | 2 +- pay-api/src/pay_api/resources/v1/account.py | 2 +- .../resources/v1/account_statements.py | 2 +- .../v1/account_statements_notifications.py | 2 +- .../v1/account_statements_settings.py | 2 +- .../src/pay_api/resources/v1/bank_accounts.py | 2 +- pay-api/src/pay_api/resources/v1/code.py | 2 +- .../src/pay_api/resources/v1/distributions.py | 2 +- .../src/pay_api/resources/v1/fas/__init__.py | 2 +- .../src/pay_api/resources/v1/fas/refund.py | 2 +- .../pay_api/resources/v1/fas/routing_slip.py | 3 +- pay-api/src/pay_api/resources/v1/fee.py | 2 +- .../src/pay_api/resources/v1/fee_schedule.py | 2 +- pay-api/src/pay_api/resources/v1/invoice.py | 2 +- .../pay_api/resources/v1/invoice_receipt.py | 2 +- pay-api/src/pay_api/resources/v1/invoices.py | 2 +- pay-api/src/pay_api/resources/v1/meta.py | 2 +- .../resources/v1/non_sufficient_funds.py | 2 +- pay-api/src/pay_api/resources/v1/payment.py | 3 +- pay-api/src/pay_api/resources/v1/refund.py | 2 +- .../src/pay_api/resources/v1/transaction.py | 3 +- pay-api/src/pay_api/schemas/__init__.py | 2 +- pay-api/src/pay_api/schemas/utils.py | 2 +- pay-api/src/pay_api/services/__init__.py | 4 +- pay-api/src/pay_api/services/auth.py | 2 +- .../pay_api/services/base_payment_system.py | 15 ++- pay-api/src/pay_api/services/bcol_service.py | 2 +- pay-api/src/pay_api/services/cfs_service.py | 2 +- pay-api/src/pay_api/services/code.py | 2 +- .../src/pay_api/services/deposit_service.py | 6 +- .../pay_api/services/direct_pay_service.py | 2 +- .../src/pay_api/services/distribution_code.py | 2 +- pay-api/src/pay_api/services/eft_service.py | 2 +- .../src/pay_api/services/ejv_pay_service.py | 2 +- pay-api/src/pay_api/services/fas/__init__.py | 2 +- pay-api/src/pay_api/services/fas/comment.py | 2 +- .../src/pay_api/services/fas/routing_slip.py | 2 +- .../routing_slip_status_transition_service.py | 2 +- pay-api/src/pay_api/services/fee_schedule.py | 2 +- .../pay_api/services/gcp_queue/__init__.py | 2 +- .../pay_api/services/gcp_queue_publisher.py | 14 ++- pay-api/src/pay_api/services/hashing.py | 2 +- .../pay_api/services/internal_pay_service.py | 2 +- pay-api/src/pay_api/services/invoice.py | 2 +- .../src/pay_api/services/invoice_reference.py | 2 +- .../pay_api/services/non_sufficient_funds.py | 2 +- pay-api/src/pay_api/services/oauth_service.py | 2 +- .../services/online_banking_service.py | 2 +- pay-api/src/pay_api/services/pad_service.py | 2 +- pay-api/src/pay_api/services/paybc_service.py | 2 +- pay-api/src/pay_api/services/payment.py | 2 +- .../src/pay_api/services/payment_account.py | 29 +++-- .../src/pay_api/services/payment_line_item.py | 2 +- .../src/pay_api/services/payment_service.py | 2 +- .../pay_api/services/payment_transaction.py | 8 +- pay-api/src/pay_api/services/receipt.py | 2 +- pay-api/src/pay_api/services/refund.py | 2 +- pay-api/src/pay_api/services/statement.py | 2 +- .../pay_api/services/statement_recipients.py | 2 +- .../pay_api/services/statement_settings.py | 2 +- pay-api/src/pay_api/services/wire_service.py | 2 +- pay-api/src/pay_api/utils/__init__.py | 2 +- pay-api/src/pay_api/utils/auth.py | 2 +- pay-api/src/pay_api/utils/cache.py | 2 +- pay-api/src/pay_api/utils/constants.py | 2 +- pay-api/src/pay_api/utils/enums.py | 32 +---- pay-api/src/pay_api/utils/errors.py | 2 +- pay-api/src/pay_api/utils/logging.py | 2 +- .../utils/paybc_transaction_error_message.py | 2 +- pay-api/src/pay_api/utils/run_version.py | 2 +- pay-api/src/pay_api/utils/user_context.py | 2 +- pay-api/src/pay_api/utils/util.py | 20 +-- pay-api/src/pay_api/version.py | 2 +- pay-api/tests/__init__.py | 2 +- pay-api/tests/conftest.py | 27 +++- pay-api/tests/unit/__init__.py | 2 +- pay-api/tests/unit/api/__init__.py | 2 +- pay-api/tests/unit/api/fas/__init__.py | 2 +- pay-api/tests/unit/api/fas/test_refund.py | 2 +- .../tests/unit/api/fas/test_routing_slip.py | 2 +- pay-api/tests/unit/api/test_account.py | 2 +- pay-api/tests/unit/api/test_bank_accounts.py | 2 +- pay-api/tests/unit/api/test_code.py | 2 +- pay-api/tests/unit/api/test_distributions.py | 2 +- pay-api/tests/unit/api/test_fee.py | 2 +- pay-api/tests/unit/api/test_fee_schedule.py | 2 +- pay-api/tests/unit/api/test_invoice.py | 2 +- pay-api/tests/unit/api/test_meta.py | 2 +- .../unit/api/test_non_sufficient_funds.py | 2 +- pay-api/tests/unit/api/test_ops.py | 2 +- pay-api/tests/unit/api/test_payment.py | 2 +- .../tests/unit/api/test_payment_request.py | 2 +- pay-api/tests/unit/api/test_receipt.py | 2 +- pay-api/tests/unit/api/test_refund.py | 2 +- pay-api/tests/unit/api/test_statement.py | 2 +- .../tests/unit/api/test_statement_settings.py | 2 +- pay-api/tests/unit/api/test_transaction.py | 2 +- pay-api/tests/unit/conf/__init__.py | 2 +- pay-api/tests/unit/conf/test_configuration.py | 2 +- pay-api/tests/unit/conf/test_version.py | 2 +- pay-api/tests/unit/factory/__init__.py | 2 +- .../factory/test_payment_system_factory.py | 2 +- pay-api/tests/unit/models/__init__.py | 2 +- pay-api/tests/unit/models/test_comment.py | 2 +- pay-api/tests/unit/models/test_corp_type.py | 2 +- pay-api/tests/unit/models/test_fee_code.py | 2 +- .../tests/unit/models/test_fee_schedule.py | 2 +- pay-api/tests/unit/models/test_filing_type.py | 2 +- pay-api/tests/unit/models/test_invoice.py | 2 +- .../unit/models/test_non_sufficient_funds.py | 2 +- pay-api/tests/unit/models/test_payment.py | 2 +- .../tests/unit/models/test_payment_account.py | 2 +- .../tests/unit/models/test_payment_method.py | 2 +- .../tests/unit/models/test_payment_system.py | 2 +- .../unit/models/test_payment_transaction.py | 2 +- pay-api/tests/unit/models/test_receipt.py | 2 +- .../tests/unit/models/test_routing_slip.py | 2 +- pay-api/tests/unit/models/test_status_code.py | 2 +- pay-api/tests/unit/services/__init__.py | 2 +- pay-api/tests/unit/services/test_auth.py | 2 +- .../tests/unit/services/test_bcol_service.py | 2 +- .../tests/unit/services/test_cfs_service.py | 2 +- pay-api/tests/unit/services/test_code.py | 2 +- pay-api/tests/unit/services/test_comment.py | 2 +- .../unit/services/test_distribution_code.py | 2 +- .../tests/unit/services/test_eft_service.py | 2 +- .../tests/unit/services/test_fee_schedule.py | 2 +- pay-api/tests/unit/services/test_flags.py | 2 +- pay-api/tests/unit/services/test_gcp_queue.py | 67 ++++++---- .../unit/services/test_hashing_service.py | 2 +- pay-api/tests/unit/services/test_invoice.py | 2 +- .../unit/services/test_invoice_reference.py | 2 +- .../services/test_non_sufficient_funds.py | 2 +- .../tests/unit/services/test_oauth_service.py | 2 +- .../tests/unit/services/test_pad_service.py | 2 +- pay-api/tests/unit/services/test_payment.py | 2 +- .../unit/services/test_payment_account.py | 2 +- .../unit/services/test_payment_line_item.py | 2 +- .../unit/services/test_payment_service.py | 2 +- .../services/test_payment_system_service.py | 2 +- .../unit/services/test_payment_transaction.py | 2 +- pay-api/tests/unit/services/test_receipt.py | 2 +- pay-api/tests/unit/services/test_refund.py | 2 +- .../services/test_routing_slip_service.py | 2 +- pay-api/tests/unit/services/test_statement.py | 2 +- .../unit/services/test_statement_settings.py | 2 +- .../tests/unit/services/test_wire_service.py | 2 +- pay-api/tests/unit/utils/__init__.py | 2 +- pay-api/tests/unit/utils/test_error.py | 2 +- pay-api/tests/unit/utils/test_logging.py | 2 +- pay-api/tests/unit/utils/test_util.py | 2 +- pay-api/tests/unit/utils/test_util_cors.py | 2 +- pay-api/tests/utilities/__init__.py | 2 +- pay-api/tests/utilities/base_test.py | 2 +- pay-api/tests/utilities/decorators.py | 2 +- pay-api/tests/utilities/schema_assertions.py | 2 +- pay-api/wsgi.py | 2 +- pay-queue/README.md | 4 +- pay-queue/app.py | 9 +- pay-queue/devops/vaults.gcp.env | 6 +- pay-queue/devops/vaults.json | 58 +++++++++ pay-queue/logging.conf | 34 ----- pay-queue/scripts/verify_license_headers.sh | 4 +- pay-queue/setup.cfg | 9 +- pay-queue/setup.py | 4 +- pay-queue/src/pay_queue/__init__.py | 30 ++++- pay-queue/src/pay_queue/config.py | 27 ++-- pay-queue/src/pay_queue/enums.py | 2 +- pay-queue/src/pay_queue/external/__init__.py | 14 +++ pay-queue/src/pay_queue/external/gcp_auth.py | 20 ++- pay-queue/src/pay_queue/minio.py | 2 +- pay-queue/src/pay_queue/resources/worker.py | 35 ++++-- pay-queue/src/pay_queue/services/__init__.py | 5 - .../pay_queue/services/cgi_reconciliations.py | 55 +++++--- .../services/eft/eft_reconciliation.py | 5 +- .../services/payment_reconciliations.py | 29 ++--- pay-queue/src/pay_queue/version.py | 2 +- pay-queue/tests/__init__.py | 2 +- pay-queue/tests/conftest.py | 21 +++- pay-queue/tests/integration/__init__.py | 2 +- pay-queue/tests/integration/factory.py | 2 +- .../integration/test_cgi_reconciliations.py | 63 +++++----- .../integration/test_eft_reconciliation.py | 47 ++++--- .../test_payment_reconciliations.py | 69 ++++++---- .../tests/integration/test_worker_queue.py | 4 +- pay-queue/tests/integration/utils.py | 11 +- report-api/requirements.txt | 2 +- 270 files changed, 1026 insertions(+), 611 deletions(-) create mode 100644 .github/workflows/pay-queue-gcp-cd.yml create mode 100644 jobs/payment-jobs/devops/vaults.json create mode 100644 pay-api/devops/vaults.json create mode 100644 pay-queue/devops/vaults.json delete mode 100644 pay-queue/logging.conf create mode 100644 pay-queue/src/pay_queue/external/__init__.py diff --git a/.github/workflows/pay-queue-cd.yml b/.github/workflows/pay-queue-cd.yml index 73e2b0518..96124a711 100644 --- a/.github/workflows/pay-queue-cd.yml +++ b/.github/workflows/pay-queue-cd.yml @@ -4,30 +4,112 @@ on: push: branches: - main - - feature* paths: - "pay-queue/**" - "pay-api/src/pay_api/models/**" - "pay-api/src/pay_api/services/cfs_service.py" workflow_dispatch: inputs: - target: - description: "Deploy To" + environment: + description: "Environment (dev/test/prod)" required: true - type: choice - options: - - dev - - test - - sandbox - - prod + default: "dev" + +defaults: + run: + shell: bash + working-directory: ./pay-queue + +env: + APP_NAME: "pay-queue" + TAG_NAME: "dev" jobs: - pay-queue-cd: - uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main - with: - target: ${{ inputs.target }} - app_name: "pay-queue" - working_directory: "./pay-queue" - secrets: - WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file + pay-queue-cd-by-push: + runs-on: ubuntu-20.04 + + if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' + environment: + name: "dev" + + steps: + - uses: actions/checkout@v3 + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Payment Reconciliations Queue Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} + + pay-queue-cd-by-dispatch: + runs-on: ubuntu-20.04 + + if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' + environment: + name: "${{ github.event.inputs.environment }}" + + steps: + - uses: actions/checkout@v3 + - name: Set env by input + run: | + echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Payment Reconciliations Queue Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pay-queue-ci.yml b/.github/workflows/pay-queue-ci.yml index 567c4d23b..238da6343 100644 --- a/.github/workflows/pay-queue-ci.yml +++ b/.github/workflows/pay-queue-ci.yml @@ -14,7 +14,6 @@ defaults: run: shell: bash working-directory: ./pay-queue - jobs: setup-job: runs-on: ubuntu-20.04 diff --git a/.github/workflows/pay-queue-gcp-cd.yml b/.github/workflows/pay-queue-gcp-cd.yml new file mode 100644 index 000000000..73e2b0518 --- /dev/null +++ b/.github/workflows/pay-queue-gcp-cd.yml @@ -0,0 +1,33 @@ +name: Pay Queue CD + +on: + push: + branches: + - main + - feature* + paths: + - "pay-queue/**" + - "pay-api/src/pay_api/models/**" + - "pay-api/src/pay_api/services/cfs_service.py" + workflow_dispatch: + inputs: + target: + description: "Deploy To" + required: true + type: choice + options: + - dev + - test + - sandbox + - prod + +jobs: + pay-queue-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-queue" + working_directory: "./pay-queue" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index b3f42e947..ba3b879e1 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,6 @@ ACK.INBOX.F12022020202 pay-queue/ACK.* pay-queue/FEEDBACK.* jobs/notebook-report/data/ +test_eft_tdi17.txt +ACK.INBOX* +FEEDBACK.INBOX* diff --git a/bcol-api/requirements.txt b/bcol-api/requirements.txt index 44c6e1330..f9b2fad15 100644 --- a/bcol-api/requirements.txt +++ b/bcol-api/requirements.txt @@ -36,7 +36,7 @@ python-ldap==3.4.4 pytz==2024.1 requests-file==2.0.0 requests-toolbelt==1.0.0 -requests==2.31.0 +requests==2.32.2 rsa==4.9 sentry-sdk==1.41.0 six==1.16.0 diff --git a/jobs/ftp-poller/config.py b/jobs/ftp-poller/config.py index 81a331765..12082de1e 100644 --- a/jobs/ftp-poller/config.py +++ b/jobs/ftp-poller/config.py @@ -136,11 +136,9 @@ class _Config(object): # pylint: disable=too-few-public-methods SENTRY_ENABLE = os.getenv('SENTRY_ENABLE', 'False') SENTRY_DSN = os.getenv('SENTRY_DSN', None) - # GCP PubSub - AUDIENCE = os.getenv('AUDIENCE', None) - GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) - PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) - FTP_POLLER_TOPIC = os.getenv('FTP_POLLER_TOPIC', None) + # PUB/SUB - PUB: ftp-poller-payment-reconciliation-dev + FTP_POLLER_TOPIC = os.getenv('FTP_POLLER_TOPIC', 'ftp-poller-payment-reconciliation-dev') + GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) TESTING = False DEBUG = True diff --git a/jobs/ftp-poller/devops/vaults.json b/jobs/ftp-poller/devops/vaults.json index 433ed9cbd..209734136 100644 --- a/jobs/ftp-poller/devops/vaults.json +++ b/jobs/ftp-poller/devops/vaults.json @@ -19,5 +19,12 @@ "application": [ "relationship-api" ] + }, + { + "vault": "gcp-queue", + "application": [ + "gtksf3", + "topics" + ] } ] diff --git a/jobs/ftp-poller/invoke_jobs.py b/jobs/ftp-poller/invoke_jobs.py index 5e18dcccb..eddfc319f 100755 --- a/jobs/ftp-poller/invoke_jobs.py +++ b/jobs/ftp-poller/invoke_jobs.py @@ -21,6 +21,7 @@ import sentry_sdk from flask import Flask from sentry_sdk.integrations.flask import FlaskIntegration +from pay_api.services.gcp_queue import queue import config from utils.logger import setup_logging @@ -44,6 +45,8 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): integrations=[FlaskIntegration()] ) app.logger.info(f'<<<< Starting Ftp Poller Job >>>>') + queue.init_app(app) + db.init_app(app) ma.init_app(app) register_shellcontext(app) diff --git a/jobs/ftp-poller/poetry.lock b/jobs/ftp-poller/poetry.lock index c49f113da..7aa6aec79 100644 --- a/jobs/ftp-poller/poetry.lock +++ b/jobs/ftp-poller/poetry.lock @@ -2235,7 +2235,7 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +resolved_reference = "94986110a7f6c7ba4f57ed8b038101ba7d864a94" subdirectory = "python" [[package]] diff --git a/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py b/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py index 7542a4d84..ba79e2347 100644 --- a/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py +++ b/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py @@ -16,8 +16,8 @@ from flask import current_app from paramiko.sftp_attr import SFTPAttributes +from sbc_common_components.utils.enums import QueueMessageTypes -from pay_api.utils.enums import MessageType from services.sftp import SFTPService from utils import utils @@ -48,12 +48,12 @@ def poll_ftp(cls): f'Skipping directory {file_name}.') continue if cls._is_ack_file(file_name): - utils.publish_to_queue([file_name], MessageType.CGI_ACK_RECEIVED.value) + utils.publish_to_queue([file_name], QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) cls._move_file_to_backup(sftp_client, [file_name]) elif cls._is_feedback_file(file_name): bucket_name = current_app.config.get('MINIO_CGI_BUCKET_NAME') utils.upload_to_minio(file, file_full_name, sftp_client, bucket_name) - utils.publish_to_queue([file_name], MessageType.CGI_FEEDBACK_RECEIVED.value, + utils.publish_to_queue([file_name], QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value, location=bucket_name) cls._move_file_to_backup(sftp_client, [file_name]) elif cls._is_a_trigger_file(file_name): diff --git a/jobs/ftp-poller/tasks/eft_poller_ftp.py b/jobs/ftp-poller/tasks/eft_poller_ftp.py index 10f59a4f4..ff6b3dc52 100644 --- a/jobs/ftp-poller/tasks/eft_poller_ftp.py +++ b/jobs/ftp-poller/tasks/eft_poller_ftp.py @@ -17,7 +17,7 @@ from flask import current_app from paramiko.sftp_attr import SFTPAttributes -from pay_api.utils.enums import MessageType +from sbc_common_components.utils.enums import QueueMessageTypes from services.sftp import SFTPService from utils.utils import publish_to_queue, upload_to_minio @@ -66,7 +66,7 @@ def _post_process(cls, sftp_client, payment_file_list: List[str]): 2.Send a message to queue """ cls._move_file_to_backup(sftp_client, payment_file_list) - publish_to_queue(payment_file_list, MessageType.EFT_FILE_UPLOADED.value, + publish_to_queue(payment_file_list, QueueMessageTypes.EFT_FILE_UPLOADED.value, location=current_app.config.get('MINIO_EFT_BUCKET_NAME')) @classmethod diff --git a/jobs/ftp-poller/tests/jobs/test_sftp.py b/jobs/ftp-poller/tests/jobs/test_sftp.py index d88470a8d..07dc79d5b 100644 --- a/jobs/ftp-poller/tests/jobs/test_sftp.py +++ b/jobs/ftp-poller/tests/jobs/test_sftp.py @@ -19,6 +19,8 @@ import pytest from flask import current_app +from sbc_common_components.utils.enums import QueueMessageTypes + from services.sftp import SFTPService from utils.utils import publish_to_queue @@ -38,7 +40,14 @@ def test_poll_ftp_task(): assert len(files) == 1, 'Files exist in FTP folder' -@pytest.mark.skip(reason='leave this to manually verify pubsub connection; needs env vars') -def test_queue_message(): +@pytest.mark.skip(reason='leave this to manually verify pubsub connection;' + 'needs env vars, disable def mock_queue_publish(monkeypatch):') +def test_queue_message(session): # pylint:disable=unused-argument """Test publishing to topic.""" - publish_to_queue(['file1.csv']) + file_name = 'file1.csv' + publish_to_queue([file_name]) + publish_to_queue([file_name], QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + publish_to_queue([file_name], QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value, + location=current_app.config.get('MINIO_CGI_BUCKET_NAME')) + publish_to_queue([file_name], QueueMessageTypes.EFT_FILE_UPLOADED.value, + location=current_app.config.get('MINIO_EFT_BUCKET_NAME')) diff --git a/jobs/ftp-poller/utils/utils.py b/jobs/ftp-poller/utils/utils.py index aa452f5c4..afc22828a 100644 --- a/jobs/ftp-poller/utils/utils.py +++ b/jobs/ftp-poller/utils/utils.py @@ -12,18 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. """Service to manage PAYBC services.""" +from time import time from typing import List from flask import current_app from paramiko import SFTPFile from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage -from pay_api.utils.enums import MessageType, QueueSources +from pay_api.utils.enums import QueueSources +from sbc_common_components.utils.enums import QueueMessageTypes from utils.minio import put_object -def publish_to_queue(payment_file_list: List[str], message_type=MessageType.CAS_UPLOADED.value, location: str = ''): +def publish_to_queue(payment_file_list: List[str], message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value, + location: str = ''): """Publish message to the Queue, saying file has been uploaded. Using the event spec.""" queue_data = { 'fileSource': 'MINIO', @@ -38,11 +41,11 @@ def publish_to_queue(payment_file_list: List[str], message_type=MessageType.CAS_ source=QueueSources.FTP_POLLER.value, message_type=message_type, payload=queue_data, - topic=current_app.config.get('FTP_POLLER_TOPIC') + topic=current_app.config.get('FTP_POLLER_TOPIC'), + ordering_key=str(time()) ) ) except Exception as e: # NOQA # pylint: disable=broad-except - current_app.logger.error(e) current_app.logger.warning( f'Notification to Queue failed for the file {file_name}', e) @@ -57,7 +60,6 @@ def upload_to_minio(file, file_full_name, sftp_client, bucket_name): value_as_bytes = f.read() try: put_object(value_as_bytes, file.filename, bucket_name, file.st_size, ) - except Exception as e: # NOQA # pylint: disable=broad-except - current_app.logger.error(e) + except Exception: # NOQA # pylint: disable=broad-except current_app.logger.error(f'upload to minio failed for the file: {file_full_name}') raise diff --git a/jobs/notebook-report/requirements.txt b/jobs/notebook-report/requirements.txt index bb27f8d33..4a095802e 100644 --- a/jobs/notebook-report/requirements.txt +++ b/jobs/notebook-report/requirements.txt @@ -30,5 +30,5 @@ marshmallow==2.20.5 Werkzeug==0.16.1 certifi==2023.7.22 urllib3==1.26.17 -idna==2.9 +idna==3.7 pylint diff --git a/jobs/notebook-report/requirements/prod.txt b/jobs/notebook-report/requirements/prod.txt index 1f8cbae23..6d869f683 100644 --- a/jobs/notebook-report/requirements/prod.txt +++ b/jobs/notebook-report/requirements/prod.txt @@ -25,9 +25,9 @@ pyrsistent==0.16.0 Flask==1.1.2 Click==7.1.2 python-dotenv==0.13.0 -requests==2.31.0 +requests==2.32.2 marshmallow==2.20.5 Werkzeug==0.16.1 certifi==2023.7.22 urllib3==1.26.17 -idna==2.9 +idna==3.7 diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index cede16606..891a99927 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -116,6 +116,11 @@ class _Config(object): # pylint: disable=too-few-public-methods AUTH_WEB_STATEMENT_URL = os.getenv('AUTH_WEB_STATEMENT_URL', 'account/orgId/settings/statements') REGISTRIES_LOGO_IMAGE_NAME = os.getenv('REGISTRIES_LOGO_IMAGE_NAME', 'bc_logo_for_email.png') + # PUB/SUB- PUB: account-mailer-dev + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') + GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) + + CFS_ACCOUNT_DESCRIPTION = os.getenv('CFS_ACCOUNT_DESCRIPTION', 'BCR') CFS_INVOICE_PREFIX = os.getenv('CFS_INVOICE_PREFIX', 'REG') CFS_STOP_PAD_ACCOUNT_CREATION = os.getenv('CFS_STOP_PAD_ACCOUNT_CREATION', 'false').lower() == 'true' diff --git a/jobs/payment-jobs/devops/vaults.json b/jobs/payment-jobs/devops/vaults.json new file mode 100644 index 000000000..420b0bbe9 --- /dev/null +++ b/jobs/payment-jobs/devops/vaults.json @@ -0,0 +1,70 @@ +[ + { + "vault": "shared", + "application": [ + "api-endpoints", + "encryption-key" + ] + }, + { + "vault": "keycloak", + "application": [ + "jwt-base", + "sbc-auth-admin" + ] + }, + { + "vault": "payment-external-services", + "application": [ + "paybc", + "cfs" + ] + }, + { + "vault": "relationship", + "application": [ + "postgres-pay", + "pay-api", + "jwt", + "payment-jobs", + "ftp-poller" + ] + }, + { + "vault": "sentry", + "application": [ + "relationship-api" + ] + }, + { + "vault": "minio", + "application": [ + "payment-jobs" + ] + }, + { + "vault": "minio", + "application": [ + "base" + ] + }, + { + "vault": "launchdarkly", + "application": [ + "pay" + ] + }, + { + "vault": "entity", + "application": [ + "colin-api" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "gtksf3", + "topics" + ] + } +] diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index ebbb7d567..7636770a6 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -30,6 +30,7 @@ from utils.logger import setup_logging from pay_api.services import Flags +from pay_api.services.gcp_queue import queue setup_logging(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first @@ -51,6 +52,7 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production'), job_name='unk release=f'payment-jobs-{job_name}@-', ) app.logger.info('<<<< Starting Payment Jobs >>>>') + queue.init_app(app) db.init_app(app) if init_oracle: oracle_db.init_app(app) diff --git a/jobs/payment-jobs/tasks/activate_pad_account_task.py b/jobs/payment-jobs/tasks/activate_pad_account_task.py index d24127537..2659006aa 100644 --- a/jobs/payment-jobs/tasks/activate_pad_account_task.py +++ b/jobs/payment-jobs/tasks/activate_pad_account_task.py @@ -19,7 +19,8 @@ from flask import current_app from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.utils.enums import CfsAccountStatus, MessageType, PaymentMethod +from pay_api.utils.enums import CfsAccountStatus, PaymentMethod +from sbc_common_components.utils.enums import QueueMessageTypes from utils import mailer @@ -57,4 +58,4 @@ def activate_pad_accounts(cls): if pay_account.payment_method != PaymentMethod.PAD.value: pay_account.payment_method = PaymentMethod.PAD.value pay_account.save() - mailer.publish_mailer_events(MessageType.PAD_CONFIRMATION_PERIOD_OVER, pay_account) + mailer.publish_mailer_events(QueueMessageTypes.CONFIRMATION_PERIOD_OVER.value, pay_account) diff --git a/jobs/payment-jobs/tasks/ap_task.py b/jobs/payment-jobs/tasks/ap_task.py index 0633a8c3b..c317cb447 100644 --- a/jobs/payment-jobs/tasks/ap_task.py +++ b/jobs/payment-jobs/tasks/ap_task.py @@ -76,7 +76,7 @@ def _create_routing_slip_refund_file(cls): # pylint:disable=too-many-locals, to if not routing_slips_dao: return - for routing_slips in batched(routing_slips_dao, 250): + for routing_slips in list(batched(routing_slips_dao, 250)): ejv_file_model: EjvFileModel = EjvFileModel( file_type=cls.ap_type.value, file_ref=cls.get_file_name(), @@ -118,7 +118,7 @@ def _create_non_gov_disbursement_file(cls): # pylint:disable=too-many-locals return # 250 MAX is all the transactions the feeder can take per batch. - for invoices in batched(total_invoices, 250): + for invoices in list(batched(total_invoices, 250)): bca_distribution = cls._get_bca_distribution_string() ejv_file_model: EjvFileModel = EjvFileModel( file_type=cls.ap_type.value, diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index 4c075604c..d19682ed1 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -23,6 +23,7 @@ from pay_api.services.oauth_service import OAuthService from pay_api.utils.constants import RECEIPT_METHOD_EFT_MONTHLY, RECEIPT_METHOD_PAD_DAILY from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, MessageType, PaymentMethod +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from services import routing_slip from utils import mailer @@ -140,7 +141,7 @@ def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: Paym capture_message(f'User Input needed for creating CFS Account: account id={pay_account.id}, ' f'auth account : {pay_account.auth_account_id}, ERROR : Invalid Bank Details', level='error') - mailer.publish_mailer_events(MessageType.PAD_SETUP_FAILED, pay_account) + mailer.publish_mailer_events(QueueMessageTypes.PAD_SETUP_FAILED.value, pay_account) pending_account.status = CfsAccountStatus.INACTIVE.value pending_account.save() return diff --git a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py index bdc24ec4a..052b69ea0 100644 --- a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py @@ -34,6 +34,7 @@ from pay_api.utils.enums import ( CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, PaymentSystem) from pay_api.utils.util import generate_transaction_number +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from utils import mailer @@ -320,7 +321,8 @@ def _create_pad_invoices(cls): # pylint: disable=too-many-locals 'invoice_total': float(invoice_total), 'invoice_process_date': f'{datetime.now()}' } - mailer.publish_mailer_events(MessageType.PAD_INVOICE_CREATED, payment_account, additional_params) + mailer.publish_mailer_events(QueueMessageTypes.PAD_INVOICE_CREATED.value, payment_account, + additional_params) # Iterate invoice and create invoice reference records for invoice in account_invoices: invoice_reference = InvoiceReferenceModel( diff --git a/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py b/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py index 2a04f3485..200f792c6 100644 --- a/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py +++ b/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py @@ -19,7 +19,8 @@ from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import db -from pay_api.utils.enums import InvoiceStatus, MessageType, PaymentMethod +from pay_api.utils.enums import InvoiceStatus, PaymentMethod +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from sqlalchemy import Date, and_, cast, func @@ -76,7 +77,8 @@ def _notify_for_ob(cls): # pylint: disable=too-many-locals 'cfsAccountId': cfs_account.cfs_account, 'authAccountId': pay_account.auth_account_id, } - mailer.publish_mailer_events(MessageType.ONLINE_BANKING_OUTSTANDING_INVOICE, pay_account, + mailer.publish_mailer_events(QueueMessageTypes.PAYMENT_PENDING.value, + pay_account, addition_params_to_mailer) except Exception as e: # NOQA # pylint: disable=broad-except capture_message(f'Error on notifying mailer OB Pending invoice: account id={pay_account.id}, ' diff --git a/jobs/payment-jobs/tests/jobs/conftest.py b/jobs/payment-jobs/tests/jobs/conftest.py index 0f6bcfeb3..7287b03ce 100644 --- a/jobs/payment-jobs/tests/jobs/conftest.py +++ b/jobs/payment-jobs/tests/jobs/conftest.py @@ -32,13 +32,11 @@ def app(): """Return a session-wide application configured in TEST mode.""" return create_app('testing') - @pytest.fixture(scope='function') def app_request(): """Return a session-wide application configured in TEST mode.""" return create_app('testing') - @pytest.fixture(scope='session') def client(app): # pylint: disable=redefined-outer-name """Return a session-wide Flask test client.""" diff --git a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py index 9b3f596d8..7b4fd3960 100644 --- a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py +++ b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py @@ -45,6 +45,7 @@ app = None +# Travis Semple - in the future please remove this, this should be inside of conftest.py like the other fixtures. @pytest.fixture def setup(): """Initialize app with test env for testing.""" diff --git a/jobs/payment-jobs/utils/mailer.py b/jobs/payment-jobs/utils/mailer.py index 1379db03c..69cf1a6b9 100644 --- a/jobs/payment-jobs/utils/mailer.py +++ b/jobs/payment-jobs/utils/mailer.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -14,15 +14,14 @@ """Task to activate accounts with pending activation.Mostly for PAD with 3 day activation period.""" from dataclasses import dataclass from datetime import datetime -from typing import Dict from flask import current_app from pay_api.models import FeeSchedule as FeeScheduleModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Statement as StatementModel from pay_api.services import gcp_queue_publisher -from pay_api.services.gcp_queue_publisher import QueueMessage -from pay_api.utils.enums import QueueSources, MessageType +from pay_api.utils.enums import QueueSources +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message @@ -38,8 +37,7 @@ class StatementNotificationInfo: total_amount_owing: float -def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, - additional_params: Dict = {}): +def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, additional_params=None): """Publish payment message to the mailer queue.""" # Publish message to the Queue, saying account has been activated. Using the event spec. @@ -48,11 +46,11 @@ def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, payload = { 'accountId': pay_account.auth_account_id, 'nsfFee': float(fee_schedule.fee.amount), - **additional_params + **(additional_params or {}) } try: gcp_queue_publisher.publish_to_queue( - QueueMessage( + gcp_queue_publisher.QueueMessage( source=QueueSources.PAY_JOBS.value, message_type=message_type, payload=payload, @@ -71,6 +69,7 @@ def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, def publish_statement_notification(pay_account: PaymentAccountModel, statement: StatementModel, total_amount_owing: float, emails: str) -> bool: """Publish payment statement notification message to the mailer queue.""" + message_type = QueueMessageTypes.STATEMENT_NOTIFICATION.value payload = { 'emailAddresses': emails, 'accountId': pay_account.auth_account_id, @@ -81,9 +80,9 @@ def publish_statement_notification(pay_account: PaymentAccountModel, statement: } try: gcp_queue_publisher.publish_to_queue( - QueueMessage( + gcp_queue_publisher.QueueMessage( source=QueueSources.PAY_JOBS.value, - message_type=MessageType.STATEMENT_NOTIFICATION.value, + message_type=message_type, payload=payload, topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') ) @@ -103,8 +102,8 @@ def publish_statement_notification(pay_account: PaymentAccountModel, statement: def publish_payment_notification(info: StatementNotificationInfo) -> bool: """Publish payment notification message to the mailer queue.""" - notification_type = MessageType.STATEMENT_DUE_NOTIFICATION.value if info.is_due \ - else MessageType.STATEMENT_REMINDER_NOTIFICATION.value + message_type = QueueMessageTypes.PAYMENT_DUE_NOTIFICATION.value if info.is_due \ + else QueueMessageTypes.PAYMENT_REMINDER_NOTIFICATION.value payload = { 'emailAddresses': info.emails, @@ -115,9 +114,9 @@ def publish_payment_notification(info: StatementNotificationInfo) -> bool: } try: gcp_queue_publisher.publish_to_queue( - QueueMessage( + gcp_queue_publisher.QueueMessage( source=QueueSources.PAY_JOBS.value, - message_type=notification_type, + message_type=message_type, payload=payload, topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') ) @@ -133,4 +132,3 @@ def publish_payment_notification(info: StatementNotificationInfo) -> bool: return False return True - diff --git a/pay-api/devops/vaults.json b/pay-api/devops/vaults.json new file mode 100644 index 000000000..5a1bc6bf6 --- /dev/null +++ b/pay-api/devops/vaults.json @@ -0,0 +1,59 @@ +[ + { + "vault": "shared", + "application": [ + "api-endpoints", + "encryption-key" + ] + }, + { + "vault": "keycloak", + "application": [ + "jwt-base", + "sbc-auth-admin" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "base", + "account-events-listener", + "payment", + "account-mailer" + ] + }, + { + "vault": "payment-external-services", + "application": [ + "paybc", + "cfs" + ] + }, + { + "vault": "relationship", + "application": [ + "postgres-pay", + "pay-api", + "jwt" + ] + }, + { + "vault": "sentry", + "application": [ + "relationship-api" + ] + }, + { + "vault": "launchdarkly", + "application": [ + "pay" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "gtksf3", + "topics" + ] + } +] diff --git a/pay-api/gunicorn_config.py b/pay-api/gunicorn_config.py index a3ab9633f..5a05fdb3b 100755 --- a/pay-api/gunicorn_config.py +++ b/pay-api/gunicorn_config.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/manage.py b/pay-api/manage.py index 8e1952a5c..7f786c35b 100755 --- a/pay-api/manage.py +++ b/pay-api/manage.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/scripts/verify_license_headers.sh b/pay-api/scripts/verify_license_headers.sh index 028b95c63..a160d3ac8 100755 --- a/pay-api/scripts/verify_license_headers.sh +++ b/pay-api/scripts/verify_license_headers.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ # limitations under the License. -COPYRIGHT="Copyright © 2019 Province of British Columbia" +COPYRIGHT="Copyright © 2024 Province of British Columbia" RET=0 for file in $(find $@ -not \( -path */venv -prune \) -not \( -path */migrations -prune \) -not \( -path */tests -prune \) -not \( -path */.egg* -prune \) -name \*.py) diff --git a/pay-api/setup.cfg b/pay-api/setup.cfg index 4a29f38b9..ccbd8d7ce 100755 --- a/pay-api/setup.cfg +++ b/pay-api/setup.cfg @@ -9,7 +9,7 @@ classifiers = Topic :: Payment License :: OSI Approved :: Apache Software License Natural Language :: English - Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.12 license = Apache Software License Version 2.0 description = A short description of the project long_description = file: README.md diff --git a/pay-api/setup.py b/pay-api/setup.py index 9f0ddb399..59919617f 100755 --- a/pay-api/setup.py +++ b/pay-api/setup.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia. +# Copyright © 2024 Province of British Columbia. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index 611fdcba2..950fd5e65 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ from pay_api.utils.cache import cache from pay_api.utils.logging import setup_logging from pay_api.utils.run_version import get_run_version +from .services.gcp_queue import queue setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) @@ -48,6 +49,7 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): app.config.from_object(config.CONFIGURATION[run_mode]) flags.init_app(app) + queue.init_app(app) db.init_app(app) queue.init_app(app) Migrate(app, db) diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 408484a69..5d2593124 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -132,14 +132,12 @@ class _Config: # pylint: disable=too-few-public-methods 'PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL' ) - # GCP PubSub - AUDIENCE = os.getenv('AUDIENCE', None) - GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) - PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher') - ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) - EVENT_LISTENER_TOPIC = os.getenv('EVENT_LISTENER_TOPIC', None) - NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', None) - BUSINESS_PAY_TOPIC = os.getenv('BUSINESS_PAY_TOPIC', None) + # PUB/SUB - PUB: auth-event-dev, account-mailer-dev, business-pay-dev, namex-pay-dev + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') + AUTH_EVENT_TOPIC = os.getenv('AUTH_EVENT_TOPIC', 'auth-event-dev') + BUSINESS_PAY_TOPIC = os.getenv('BUSINESS_PAY_TOPIC', 'business-pay-dev') + GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) + NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', 'namex-pay-dev') # API Endpoints AUTH_API_URL = os.getenv('AUTH_API_URL', '') diff --git a/pay-api/src/pay_api/exceptions/__init__.py b/pay-api/src/pay_api/exceptions/__init__.py index 03c47bcea..07dd45970 100755 --- a/pay-api/src/pay_api/exceptions/__init__.py +++ b/pay-api/src/pay_api/exceptions/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/factory/__init__.py b/pay-api/src/pay_api/factory/__init__.py index c24369f03..2f5095570 100644 --- a/pay-api/src/pay_api/factory/__init__.py +++ b/pay-api/src/pay_api/factory/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/factory/payment_system_factory.py b/pay-api/src/pay_api/factory/payment_system_factory.py index f5ffbb998..b9000b55f 100644 --- a/pay-api/src/pay_api/factory/payment_system_factory.py +++ b/pay-api/src/pay_api/factory/payment_system_factory.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/__init__.py b/pay-api/src/pay_api/models/__init__.py index 31ba7d754..c8f85920e 100755 --- a/pay-api/src/pay_api/models/__init__.py +++ b/pay-api/src/pay_api/models/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/account_fee.py b/pay-api/src/pay_api/models/account_fee.py index 7df2f5663..a23a340ef 100644 --- a/pay-api/src/pay_api/models/account_fee.py +++ b/pay-api/src/pay_api/models/account_fee.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/audit.py b/pay-api/src/pay_api/models/audit.py index 663ee514b..5845ac386 100644 --- a/pay-api/src/pay_api/models/audit.py +++ b/pay-api/src/pay_api/models/audit.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/base_model.py b/pay-api/src/pay_api/models/base_model.py index 22a10e467..7ad36bbac 100644 --- a/pay-api/src/pay_api/models/base_model.py +++ b/pay-api/src/pay_api/models/base_model.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/base_schema.py b/pay-api/src/pay_api/models/base_schema.py index e4255a85b..d628f1cb6 100644 --- a/pay-api/src/pay_api/models/base_schema.py +++ b/pay-api/src/pay_api/models/base_schema.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/cfs_account.py b/pay-api/src/pay_api/models/cfs_account.py index b16eb778b..2d13c78ac 100644 --- a/pay-api/src/pay_api/models/cfs_account.py +++ b/pay-api/src/pay_api/models/cfs_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/cfs_account_status_code.py b/pay-api/src/pay_api/models/cfs_account_status_code.py index 1507d6495..b13ba1a3c 100644 --- a/pay-api/src/pay_api/models/cfs_account_status_code.py +++ b/pay-api/src/pay_api/models/cfs_account_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/code_table.py b/pay-api/src/pay_api/models/code_table.py index aac84d3c1..9cb7afd11 100644 --- a/pay-api/src/pay_api/models/code_table.py +++ b/pay-api/src/pay_api/models/code_table.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/comment.py b/pay-api/src/pay_api/models/comment.py index eece02a87..1f57b9f43 100644 --- a/pay-api/src/pay_api/models/comment.py +++ b/pay-api/src/pay_api/models/comment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/corp_type.py b/pay-api/src/pay_api/models/corp_type.py index 0174857b7..587e59cc5 100644 --- a/pay-api/src/pay_api/models/corp_type.py +++ b/pay-api/src/pay_api/models/corp_type.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/credit.py b/pay-api/src/pay_api/models/credit.py index 1b010b921..d604f1e3c 100644 --- a/pay-api/src/pay_api/models/credit.py +++ b/pay-api/src/pay_api/models/credit.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/db.py b/pay-api/src/pay_api/models/db.py index dee2fa81c..9ee95c8fd 100755 --- a/pay-api/src/pay_api/models/db.py +++ b/pay-api/src/pay_api/models/db.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/disbursement_status_code.py b/pay-api/src/pay_api/models/disbursement_status_code.py index 7ffb5720c..bed1297e1 100644 --- a/pay-api/src/pay_api/models/disbursement_status_code.py +++ b/pay-api/src/pay_api/models/disbursement_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/ejv_file.py b/pay-api/src/pay_api/models/ejv_file.py index d0f5b4343..7c44c6d8e 100644 --- a/pay-api/src/pay_api/models/ejv_file.py +++ b/pay-api/src/pay_api/models/ejv_file.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/ejv_header.py b/pay-api/src/pay_api/models/ejv_header.py index aa514e4f8..c6fe07fa1 100644 --- a/pay-api/src/pay_api/models/ejv_header.py +++ b/pay-api/src/pay_api/models/ejv_header.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/ejv_link.py b/pay-api/src/pay_api/models/ejv_link.py index 4055df51d..210803a5a 100644 --- a/pay-api/src/pay_api/models/ejv_link.py +++ b/pay-api/src/pay_api/models/ejv_link.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/error_code.py b/pay-api/src/pay_api/models/error_code.py index c0190cbb7..630f0d32d 100644 --- a/pay-api/src/pay_api/models/error_code.py +++ b/pay-api/src/pay_api/models/error_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/fee_code.py b/pay-api/src/pay_api/models/fee_code.py index b0992c696..5c5854173 100644 --- a/pay-api/src/pay_api/models/fee_code.py +++ b/pay-api/src/pay_api/models/fee_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/fee_schedule.py b/pay-api/src/pay_api/models/fee_schedule.py index 70686b08f..044cc567a 100644 --- a/pay-api/src/pay_api/models/fee_schedule.py +++ b/pay-api/src/pay_api/models/fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/filing_type.py b/pay-api/src/pay_api/models/filing_type.py index 08f45fb84..08d8c010a 100644 --- a/pay-api/src/pay_api/models/filing_type.py +++ b/pay-api/src/pay_api/models/filing_type.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice.py b/pay-api/src/pay_api/models/invoice.py index cdafa08bb..1627d5fd4 100644 --- a/pay-api/src/pay_api/models/invoice.py +++ b/pay-api/src/pay_api/models/invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_batch.py b/pay-api/src/pay_api/models/invoice_batch.py index 7d769dc11..27ee84d97 100644 --- a/pay-api/src/pay_api/models/invoice_batch.py +++ b/pay-api/src/pay_api/models/invoice_batch.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_batch_link.py b/pay-api/src/pay_api/models/invoice_batch_link.py index 2c1451d6b..111a62bcc 100644 --- a/pay-api/src/pay_api/models/invoice_batch_link.py +++ b/pay-api/src/pay_api/models/invoice_batch_link.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_reference.py b/pay-api/src/pay_api/models/invoice_reference.py index fcfb903ca..dd2f8747c 100644 --- a/pay-api/src/pay_api/models/invoice_reference.py +++ b/pay-api/src/pay_api/models/invoice_reference.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_reference_status_code.py b/pay-api/src/pay_api/models/invoice_reference_status_code.py index ea707dd5e..998ae5892 100644 --- a/pay-api/src/pay_api/models/invoice_reference_status_code.py +++ b/pay-api/src/pay_api/models/invoice_reference_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_status_code.py b/pay-api/src/pay_api/models/invoice_status_code.py index ce92ef38c..696fa15d3 100644 --- a/pay-api/src/pay_api/models/invoice_status_code.py +++ b/pay-api/src/pay_api/models/invoice_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/line_item_status_code.py b/pay-api/src/pay_api/models/line_item_status_code.py index 4f7d86690..5fe9ea968 100644 --- a/pay-api/src/pay_api/models/line_item_status_code.py +++ b/pay-api/src/pay_api/models/line_item_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/non_sufficient_funds.py b/pay-api/src/pay_api/models/non_sufficient_funds.py index deb5169ef..21dc9bd13 100644 --- a/pay-api/src/pay_api/models/non_sufficient_funds.py +++ b/pay-api/src/pay_api/models/non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/notification_status_code.py b/pay-api/src/pay_api/models/notification_status_code.py index 856ae0aa6..b86942693 100644 --- a/pay-api/src/pay_api/models/notification_status_code.py +++ b/pay-api/src/pay_api/models/notification_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment.py b/pay-api/src/pay_api/models/payment.py index be3c35823..fc996bc5b 100644 --- a/pay-api/src/pay_api/models/payment.py +++ b/pay-api/src/pay_api/models/payment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_account.py b/pay-api/src/pay_api/models/payment_account.py index 3e2b7b72a..46bbc179f 100644 --- a/pay-api/src/pay_api/models/payment_account.py +++ b/pay-api/src/pay_api/models/payment_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_line_item.py b/pay-api/src/pay_api/models/payment_line_item.py index 85a5acfd7..62635f4a5 100644 --- a/pay-api/src/pay_api/models/payment_line_item.py +++ b/pay-api/src/pay_api/models/payment_line_item.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_method.py b/pay-api/src/pay_api/models/payment_method.py index 8f77ef949..ed9b4edb7 100644 --- a/pay-api/src/pay_api/models/payment_method.py +++ b/pay-api/src/pay_api/models/payment_method.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_status_code.py b/pay-api/src/pay_api/models/payment_status_code.py index 6fcf9da34..bb4f92378 100644 --- a/pay-api/src/pay_api/models/payment_status_code.py +++ b/pay-api/src/pay_api/models/payment_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_system.py b/pay-api/src/pay_api/models/payment_system.py index dd6f29074..91a0ab6b7 100644 --- a/pay-api/src/pay_api/models/payment_system.py +++ b/pay-api/src/pay_api/models/payment_system.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_transaction.py b/pay-api/src/pay_api/models/payment_transaction.py index ea1877c14..0e38d3966 100644 --- a/pay-api/src/pay_api/models/payment_transaction.py +++ b/pay-api/src/pay_api/models/payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/receipt.py b/pay-api/src/pay_api/models/receipt.py index 91b3faa27..a9f617f94 100644 --- a/pay-api/src/pay_api/models/receipt.py +++ b/pay-api/src/pay_api/models/receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/refund.py b/pay-api/src/pay_api/models/refund.py index c8db6e5ce..bba73faed 100644 --- a/pay-api/src/pay_api/models/refund.py +++ b/pay-api/src/pay_api/models/refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/routing_slip.py b/pay-api/src/pay_api/models/routing_slip.py index 8a5cd062c..a6c3163fe 100644 --- a/pay-api/src/pay_api/models/routing_slip.py +++ b/pay-api/src/pay_api/models/routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/routing_slip_status_code.py b/pay-api/src/pay_api/models/routing_slip_status_code.py index f1ae33e70..9f7273aed 100644 --- a/pay-api/src/pay_api/models/routing_slip_status_code.py +++ b/pay-api/src/pay_api/models/routing_slip_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement.py b/pay-api/src/pay_api/models/statement.py index 422bf1ffd..6899525bd 100644 --- a/pay-api/src/pay_api/models/statement.py +++ b/pay-api/src/pay_api/models/statement.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement_invoices.py b/pay-api/src/pay_api/models/statement_invoices.py index 5dea45eec..92060cb53 100644 --- a/pay-api/src/pay_api/models/statement_invoices.py +++ b/pay-api/src/pay_api/models/statement_invoices.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement_recipients.py b/pay-api/src/pay_api/models/statement_recipients.py index 5a89f4a4b..531fffd62 100644 --- a/pay-api/src/pay_api/models/statement_recipients.py +++ b/pay-api/src/pay_api/models/statement_recipients.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement_settings.py b/pay-api/src/pay_api/models/statement_settings.py index 3b4eacbbd..325b31b80 100644 --- a/pay-api/src/pay_api/models/statement_settings.py +++ b/pay-api/src/pay_api/models/statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/transaction_status_code.py b/pay-api/src/pay_api/models/transaction_status_code.py index c42128c72..615a1c9ae 100644 --- a/pay-api/src/pay_api/models/transaction_status_code.py +++ b/pay-api/src/pay_api/models/transaction_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/__init__.py b/pay-api/src/pay_api/resources/__init__.py index b9e0dcbb5..13c649a27 100644 --- a/pay-api/src/pay_api/resources/__init__.py +++ b/pay-api/src/pay_api/resources/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/ops.py b/pay-api/src/pay_api/resources/ops.py index 2926fa831..c399771a1 100755 --- a/pay-api/src/pay_api/resources/ops.py +++ b/pay-api/src/pay_api/resources/ops.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account.py b/pay-api/src/pay_api/resources/v1/account.py index 097a638ae..ad54e40ea 100644 --- a/pay-api/src/pay_api/resources/v1/account.py +++ b/pay-api/src/pay_api/resources/v1/account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account_statements.py b/pay-api/src/pay_api/resources/v1/account_statements.py index 32a348c61..8559819d7 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements.py +++ b/pay-api/src/pay_api/resources/v1/account_statements.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py index 59beb51af..0c8dba6ef 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account_statements_settings.py b/pay-api/src/pay_api/resources/v1/account_statements_settings.py index e1f4b42e7..60cdc4335 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_settings.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/bank_accounts.py b/pay-api/src/pay_api/resources/v1/bank_accounts.py index 9398164e8..2783c5855 100644 --- a/pay-api/src/pay_api/resources/v1/bank_accounts.py +++ b/pay-api/src/pay_api/resources/v1/bank_accounts.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/code.py b/pay-api/src/pay_api/resources/v1/code.py index d1f3d64d6..a578bd1e8 100644 --- a/pay-api/src/pay_api/resources/v1/code.py +++ b/pay-api/src/pay_api/resources/v1/code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/distributions.py b/pay-api/src/pay_api/resources/v1/distributions.py index 42e5b7b75..3f9c1a94b 100644 --- a/pay-api/src/pay_api/resources/v1/distributions.py +++ b/pay-api/src/pay_api/resources/v1/distributions.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/fas/__init__.py b/pay-api/src/pay_api/resources/v1/fas/__init__.py index 7ec3a8f2d..d248d9f54 100644 --- a/pay-api/src/pay_api/resources/v1/fas/__init__.py +++ b/pay-api/src/pay_api/resources/v1/fas/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/fas/refund.py b/pay-api/src/pay_api/resources/v1/fas/refund.py index 78e098f32..b6ca9cd39 100644 --- a/pay-api/src/pay_api/resources/v1/fas/refund.py +++ b/pay-api/src/pay_api/resources/v1/fas/refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py index aab02d8be..f8311e8f5 100644 --- a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py +++ b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -190,6 +190,7 @@ def get_routing_slip_comments(routing_slip_number: str): def post_routing_slip_comment(routing_slip_number: str): """Create comment for a slip.""" current_app.logger.info(' InvoiceReference: """Return a static invoice number for direct pay.""" current_app.logger.debug(' PaymentAccount: pay_account.save() pa_service = cls.find_by_id(pay_account.id) if not already_has_eft_enabled: - payload = pa_service.create_account_event_payload(MessageType.EFT_AVAILABLE_NOTIFICATION.value) - pa_service._publish_queue_message(payload, MessageType.EFT_AVAILABLE_NOTIFICATION.value) + payload = pa_service.create_account_event_payload(QueueMessageTypes.EFT_AVAILABLE_NOTIFICATION.value) + pa_service._publish_queue_message(payload, QueueMessageTypes.EFT_AVAILABLE_NOTIFICATION.value) return pa_service diff --git a/pay-api/src/pay_api/services/payment_line_item.py b/pay-api/src/pay_api/services/payment_line_item.py index 6c3b36d4f..9098efd7f 100644 --- a/pay-api/src/pay_api/services/payment_line_item.py +++ b/pay-api/src/pay_api/services/payment_line_item.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/payment_service.py b/pay-api/src/pay_api/services/payment_service.py index 6da906950..feecefa92 100644 --- a/pay-api/src/pay_api/services/payment_service.py +++ b/pay-api/src/pay_api/services/payment_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/payment_transaction.py b/pay-api/src/pay_api/services/payment_transaction.py index d93b02079..bfd19f6db 100644 --- a/pay-api/src/pay_api/services/payment_transaction.py +++ b/pay-api/src/pay_api/services/payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -23,6 +23,8 @@ import humps from flask import current_app from sentry_sdk import capture_message +from sbc_common_components.utils.dataclasses import PaymentToken +from sbc_common_components.utils.enums import QueueMessageTypes from pay_api.exceptions import BusinessException, ServiceUnavailableException from pay_api.factory.payment_system_factory import PaymentSystemFactory @@ -36,7 +38,7 @@ from pay_api.services.payment_account import PaymentAccount from pay_api.services.receipt import Receipt from pay_api.utils.enums import ( - InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, QueueSources, TransactionStatus) + InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, QueueSources, TransactionStatus) from pay_api.utils.errors import Error from pay_api.utils.util import get_topic_for_corp_type, is_valid_redirect_url @@ -511,7 +513,7 @@ def publish_status(transaction_dao: PaymentTransactionModel, invoice: Invoice): gcp_queue_publisher.publish_to_queue( QueueMessage( source=QueueSources.PAY_API.value, - message_type=MessageType.PAYMENT.value, + message_type=QueueMessageTypes.PAYMENT.value, payload=PaymentTransaction.create_event_payload(invoice, status_code), topic=get_topic_for_corp_type(invoice.corp_type_code) ) diff --git a/pay-api/src/pay_api/services/receipt.py b/pay-api/src/pay_api/services/receipt.py index 3cc5a108e..7d1312de0 100644 --- a/pay-api/src/pay_api/services/receipt.py +++ b/pay-api/src/pay_api/services/receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/refund.py b/pay-api/src/pay_api/services/refund.py index d0d21b8e7..3fc23d9c3 100644 --- a/pay-api/src/pay_api/services/refund.py +++ b/pay-api/src/pay_api/services/refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/statement.py b/pay-api/src/pay_api/services/statement.py index 5a23b00c5..ab0f9e77f 100644 --- a/pay-api/src/pay_api/services/statement.py +++ b/pay-api/src/pay_api/services/statement.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/statement_recipients.py b/pay-api/src/pay_api/services/statement_recipients.py index 750577f68..8180cf7c7 100644 --- a/pay-api/src/pay_api/services/statement_recipients.py +++ b/pay-api/src/pay_api/services/statement_recipients.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/statement_settings.py b/pay-api/src/pay_api/services/statement_settings.py index 602c943ce..c233b96f3 100644 --- a/pay-api/src/pay_api/services/statement_settings.py +++ b/pay-api/src/pay_api/services/statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/wire_service.py b/pay-api/src/pay_api/services/wire_service.py index 1d4d20d91..20bfa2333 100644 --- a/pay-api/src/pay_api/services/wire_service.py +++ b/pay-api/src/pay_api/services/wire_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/__init__.py b/pay-api/src/pay_api/utils/__init__.py index 9b5291dad..8150902f6 100755 --- a/pay-api/src/pay_api/utils/__init__.py +++ b/pay-api/src/pay_api/utils/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/auth.py b/pay-api/src/pay_api/utils/auth.py index c900a8ab7..19435fc86 100644 --- a/pay-api/src/pay_api/utils/auth.py +++ b/pay-api/src/pay_api/utils/auth.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/cache.py b/pay-api/src/pay_api/utils/cache.py index c1011ca67..1f57a55cf 100644 --- a/pay-api/src/pay_api/utils/cache.py +++ b/pay-api/src/pay_api/utils/cache.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/constants.py b/pay-api/src/pay_api/utils/constants.py index a01f3d7c2..657e5793a 100644 --- a/pay-api/src/pay_api/utils/constants.py +++ b/pay-api/src/pay_api/utils/constants.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index 1437c2e02..7a91fd76a 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -195,6 +195,9 @@ class CfsAccountStatus(Enum): class CorpType(Enum): """Corp Type.""" + BTR = 'BTR' + ESRA = 'ESRA' + MHR = 'MHR' NRO = 'NRO' PPR = 'PPR' VS = 'VS' @@ -216,6 +219,7 @@ class Product(Enum): """Product.""" BUSINESS = 'BUSINESS' + NRO = 'NRO' class RoutingSlipStatus(Enum): @@ -349,32 +353,6 @@ class EFTShortnameStatus(Enum): PENDING = 'PENDING' -class MessageType(Enum): - """Queue Event Types.""" - - EFT_AVAILABLE_NOTIFICATION = 'eftAvailableNotification' - PAD_PAYMENT_SUCCESS = 'PAD.PaymentSuccess' - PAD_ACCOUNT_CREATE = 'padAccountCreate' - NSF_LOCK_ACCOUNT = 'lockAccount' - NSF_UNLOCK_ACCOUNT = 'unlockAccount' - STATEMENT_NOTIFICATION = 'statementNotification' - STATEMENT_DUE_NOTIFICATION = 'statementDueNotification' - STATEMENT_REMINDER_NOTIFICATION = 'statementReminderNotification' - PAYMENT = 'payment' - EJV_FAILED = 'ejvFailed' - CAS_UPLOADED = 'casSettlementUploaded' - INCORPORATION = 'incorporationApplication' - REGISTRATION = 'registration' - CGI_ACK_RECEIVED = 'ACKReceived' - CGI_FEEDBACK_RECEIVED = 'FEEDBACKReceived' - EFT_FILE_UPLOADED = 'eftFileUploaded' - EFT_INVOICE_CREATED = 'eft.invoiceCreated' - PAD_INVOICE_CREATED = 'pad.invoiceCreated' - PAD_SETUP_FAILED = 'PadSetupFailed' - PAD_CONFIRMATION_PERIOD_OVER = 'confirmationPeriodOver' - ONLINE_BANKING_OUTSTANDING_INVOICE = 'ob.outstandingInvoice' - - class PaymentDetailsGlStatus(Enum): """Payment details GL status.""" diff --git a/pay-api/src/pay_api/utils/errors.py b/pay-api/src/pay_api/utils/errors.py index f8048e660..30a064df8 100644 --- a/pay-api/src/pay_api/utils/errors.py +++ b/pay-api/src/pay_api/utils/errors.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/logging.py b/pay-api/src/pay_api/utils/logging.py index 8568f87dd..905300f5a 100755 --- a/pay-api/src/pay_api/utils/logging.py +++ b/pay-api/src/pay_api/utils/logging.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/paybc_transaction_error_message.py b/pay-api/src/pay_api/utils/paybc_transaction_error_message.py index c93067cbb..73eb1b628 100644 --- a/pay-api/src/pay_api/utils/paybc_transaction_error_message.py +++ b/pay-api/src/pay_api/utils/paybc_transaction_error_message.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/run_version.py b/pay-api/src/pay_api/utils/run_version.py index 41c42458b..f447beb71 100755 --- a/pay-api/src/pay_api/utils/run_version.py +++ b/pay-api/src/pay_api/utils/run_version.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/user_context.py b/pay-api/src/pay_api/utils/user_context.py index 2719e323c..b31b5a813 100644 --- a/pay-api/src/pay_api/utils/user_context.py +++ b/pay-api/src/pay_api/utils/user_context.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/util.py b/pay-api/src/pay_api/utils/util.py index a2640f985..631d31f8f 100755 --- a/pay-api/src/pay_api/utils/util.py +++ b/pay-api/src/pay_api/utils/util.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,9 +28,11 @@ from dpath import get as dpath_get from flask import current_app +from pay_api.services.code import Code as CodeService + from .constants import DT_SHORT_FORMAT from .converter import Converter -from .enums import CorpType, StatementFrequency +from .enums import Code, CorpType, Product, StatementFrequency def cors_preflight(methods: str = 'GET'): @@ -274,14 +276,12 @@ def cents_to_decimal(amount: int): def get_topic_for_corp_type(corp_type: str): """Return a topic to direct the queue message to.""" - match corp_type: - case CorpType.NRO.value: - return current_app.config.get('NAMEX_PAY_TOPIC') - # Unused for now, intentionally don't send a queue message for these. - case CorpType.PPR.value | CorpType.VS.value | CorpType.CSO.value: - return None - case _: - return current_app.config.get('BUSINESS_PAY_TOPIC') + if corp_type == CorpType.NRO.value: + return current_app.config.get('NAMEX_PAY_TOPIC') + product_code = CodeService.find_code_value_by_type_and_code(Code.CORP_TYPE.value, corp_type).get('product') + if product_code == Product.BUSINESS.value: + return current_app.config.get('BUSINESS_PAY_TOPIC') + return None def unstructure_schema_items(schema, items): diff --git a/pay-api/src/pay_api/version.py b/pay-api/src/pay_api/version.py index 7003370ab..f80e8c8d1 100644 --- a/pay-api/src/pay_api/version.py +++ b/pay-api/src/pay_api/version.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/__init__.py b/pay-api/tests/__init__.py index 5b2e2f73f..212195959 100755 --- a/pay-api/tests/__init__.py +++ b/pay-api/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/conftest.py b/pay-api/tests/conftest.py index 0e56f8990..e6b606c2e 100755 --- a/pay-api/tests/conftest.py +++ b/pay-api/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,10 +35,27 @@ def app(): @pytest.fixture(autouse=True) -def mock_queue_publish(monkeypatch): - """Mock queue publish.""" - # TODO: so it can be used like this from gcp_queue_publisher import publish_to_queue - monkeypatch.setattr('pay_api.services.gcp_queue_publisher.publish_to_queue', lambda *args, **kwargs: None) +def mock_pub_sub_call(mocker): + """Mock pub sub call.""" + class Expando(object): + """Expando class.""" + + class PublisherMock: + """Publisher Mock.""" + + def __init__(self, *args, **kwargs): + def result(): + """Return true for mock.""" + return True + self.result = result + + def publish(self, *args, **kwargs): + """Publish mock.""" + ex = Expando() + ex.result = self.result + return ex + + mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) @pytest.fixture(scope='function') diff --git a/pay-api/tests/unit/__init__.py b/pay-api/tests/unit/__init__.py index d755f6a1d..65e08c540 100755 --- a/pay-api/tests/unit/__init__.py +++ b/pay-api/tests/unit/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/__init__.py b/pay-api/tests/unit/api/__init__.py index 02d5354ad..b8fd2aced 100755 --- a/pay-api/tests/unit/api/__init__.py +++ b/pay-api/tests/unit/api/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/fas/__init__.py b/pay-api/tests/unit/api/fas/__init__.py index 5bef134ec..c538a08a1 100644 --- a/pay-api/tests/unit/api/fas/__init__.py +++ b/pay-api/tests/unit/api/fas/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/fas/test_refund.py b/pay-api/tests/unit/api/fas/test_refund.py index 18cfc28f2..de9f3530f 100644 --- a/pay-api/tests/unit/api/fas/test_refund.py +++ b/pay-api/tests/unit/api/fas/test_refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/fas/test_routing_slip.py b/pay-api/tests/unit/api/fas/test_routing_slip.py index de829e550..64eab26fe 100755 --- a/pay-api/tests/unit/api/fas/test_routing_slip.py +++ b/pay-api/tests/unit/api/fas/test_routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_account.py b/pay-api/tests/unit/api/test_account.py index d417df26b..0aeda1e47 100755 --- a/pay-api/tests/unit/api/test_account.py +++ b/pay-api/tests/unit/api/test_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_bank_accounts.py b/pay-api/tests/unit/api/test_bank_accounts.py index ee49c5145..ccab4f65b 100755 --- a/pay-api/tests/unit/api/test_bank_accounts.py +++ b/pay-api/tests/unit/api/test_bank_accounts.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_code.py b/pay-api/tests/unit/api/test_code.py index e8d1020a2..5006fce95 100755 --- a/pay-api/tests/unit/api/test_code.py +++ b/pay-api/tests/unit/api/test_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_distributions.py b/pay-api/tests/unit/api/test_distributions.py index 2b3f0d799..7e2b08673 100755 --- a/pay-api/tests/unit/api/test_distributions.py +++ b/pay-api/tests/unit/api/test_distributions.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_fee.py b/pay-api/tests/unit/api/test_fee.py index 594aaa503..f8cd3928e 100755 --- a/pay-api/tests/unit/api/test_fee.py +++ b/pay-api/tests/unit/api/test_fee.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_fee_schedule.py b/pay-api/tests/unit/api/test_fee_schedule.py index 704517346..c6bc5638b 100755 --- a/pay-api/tests/unit/api/test_fee_schedule.py +++ b/pay-api/tests/unit/api/test_fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_invoice.py b/pay-api/tests/unit/api/test_invoice.py index a8fbe8d6a..253cb3fe7 100755 --- a/pay-api/tests/unit/api/test_invoice.py +++ b/pay-api/tests/unit/api/test_invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_meta.py b/pay-api/tests/unit/api/test_meta.py index 52df42ed2..02757760d 100755 --- a/pay-api/tests/unit/api/test_meta.py +++ b/pay-api/tests/unit/api/test_meta.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_non_sufficient_funds.py b/pay-api/tests/unit/api/test_non_sufficient_funds.py index 74dad35f0..ba7638669 100644 --- a/pay-api/tests/unit/api/test_non_sufficient_funds.py +++ b/pay-api/tests/unit/api/test_non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_ops.py b/pay-api/tests/unit/api/test_ops.py index ca4473f74..c081b9aa5 100755 --- a/pay-api/tests/unit/api/test_ops.py +++ b/pay-api/tests/unit/api/test_ops.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_payment.py b/pay-api/tests/unit/api/test_payment.py index 7aceb0dac..114125990 100755 --- a/pay-api/tests/unit/api/test_payment.py +++ b/pay-api/tests/unit/api/test_payment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_payment_request.py b/pay-api/tests/unit/api/test_payment_request.py index b7348b6e5..aa06aa51f 100755 --- a/pay-api/tests/unit/api/test_payment_request.py +++ b/pay-api/tests/unit/api/test_payment_request.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_receipt.py b/pay-api/tests/unit/api/test_receipt.py index f4ad0a0ee..a5ba50b64 100644 --- a/pay-api/tests/unit/api/test_receipt.py +++ b/pay-api/tests/unit/api/test_receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_refund.py b/pay-api/tests/unit/api/test_refund.py index 69b15993c..d92c23bb1 100644 --- a/pay-api/tests/unit/api/test_refund.py +++ b/pay-api/tests/unit/api/test_refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_statement.py b/pay-api/tests/unit/api/test_statement.py index f29c80eb1..d8e7463c6 100755 --- a/pay-api/tests/unit/api/test_statement.py +++ b/pay-api/tests/unit/api/test_statement.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_statement_settings.py b/pay-api/tests/unit/api/test_statement_settings.py index 3bacd7f0c..83c6bc3df 100755 --- a/pay-api/tests/unit/api/test_statement_settings.py +++ b/pay-api/tests/unit/api/test_statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_transaction.py b/pay-api/tests/unit/api/test_transaction.py index f1315518a..3d2a83d04 100755 --- a/pay-api/tests/unit/api/test_transaction.py +++ b/pay-api/tests/unit/api/test_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/conf/__init__.py b/pay-api/tests/unit/conf/__init__.py index 47a9b4f52..e369ad9a8 100755 --- a/pay-api/tests/unit/conf/__init__.py +++ b/pay-api/tests/unit/conf/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/conf/test_configuration.py b/pay-api/tests/unit/conf/test_configuration.py index fe7515de9..4e3dcf323 100755 --- a/pay-api/tests/unit/conf/test_configuration.py +++ b/pay-api/tests/unit/conf/test_configuration.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia. +# Copyright © 2024 Province of British Columbia. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/conf/test_version.py b/pay-api/tests/unit/conf/test_version.py index c21d371ee..9e6571073 100755 --- a/pay-api/tests/unit/conf/test_version.py +++ b/pay-api/tests/unit/conf/test_version.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/factory/__init__.py b/pay-api/tests/unit/factory/__init__.py index 02d5354ad..b8fd2aced 100644 --- a/pay-api/tests/unit/factory/__init__.py +++ b/pay-api/tests/unit/factory/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/factory/test_payment_system_factory.py b/pay-api/tests/unit/factory/test_payment_system_factory.py index 3e52c2b4f..932b56394 100644 --- a/pay-api/tests/unit/factory/test_payment_system_factory.py +++ b/pay-api/tests/unit/factory/test_payment_system_factory.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/__init__.py b/pay-api/tests/unit/models/__init__.py index 4a0c382d6..c0d5410ee 100755 --- a/pay-api/tests/unit/models/__init__.py +++ b/pay-api/tests/unit/models/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_comment.py b/pay-api/tests/unit/models/test_comment.py index 8cd96b28d..26694f80b 100644 --- a/pay-api/tests/unit/models/test_comment.py +++ b/pay-api/tests/unit/models/test_comment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_corp_type.py b/pay-api/tests/unit/models/test_corp_type.py index 1a90df909..5541b42ce 100644 --- a/pay-api/tests/unit/models/test_corp_type.py +++ b/pay-api/tests/unit/models/test_corp_type.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_fee_code.py b/pay-api/tests/unit/models/test_fee_code.py index 82a75f1e3..d3bed9c4b 100644 --- a/pay-api/tests/unit/models/test_fee_code.py +++ b/pay-api/tests/unit/models/test_fee_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_fee_schedule.py b/pay-api/tests/unit/models/test_fee_schedule.py index 89d7b43fb..b23f934fe 100644 --- a/pay-api/tests/unit/models/test_fee_schedule.py +++ b/pay-api/tests/unit/models/test_fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_filing_type.py b/pay-api/tests/unit/models/test_filing_type.py index f5cc35831..608a77240 100644 --- a/pay-api/tests/unit/models/test_filing_type.py +++ b/pay-api/tests/unit/models/test_filing_type.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_invoice.py b/pay-api/tests/unit/models/test_invoice.py index 3b2e999c4..4878a3799 100644 --- a/pay-api/tests/unit/models/test_invoice.py +++ b/pay-api/tests/unit/models/test_invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_non_sufficient_funds.py b/pay-api/tests/unit/models/test_non_sufficient_funds.py index 8f88ce59f..6782ec2b1 100644 --- a/pay-api/tests/unit/models/test_non_sufficient_funds.py +++ b/pay-api/tests/unit/models/test_non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment.py b/pay-api/tests/unit/models/test_payment.py index fde0ebfe4..e3b474096 100644 --- a/pay-api/tests/unit/models/test_payment.py +++ b/pay-api/tests/unit/models/test_payment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_account.py b/pay-api/tests/unit/models/test_payment_account.py index 0e6b35724..9dd064c5e 100644 --- a/pay-api/tests/unit/models/test_payment_account.py +++ b/pay-api/tests/unit/models/test_payment_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_method.py b/pay-api/tests/unit/models/test_payment_method.py index e3042161e..5c6009315 100644 --- a/pay-api/tests/unit/models/test_payment_method.py +++ b/pay-api/tests/unit/models/test_payment_method.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_system.py b/pay-api/tests/unit/models/test_payment_system.py index e5095f2d9..7e12a68ff 100644 --- a/pay-api/tests/unit/models/test_payment_system.py +++ b/pay-api/tests/unit/models/test_payment_system.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_transaction.py b/pay-api/tests/unit/models/test_payment_transaction.py index 4ad738bef..b14788a11 100644 --- a/pay-api/tests/unit/models/test_payment_transaction.py +++ b/pay-api/tests/unit/models/test_payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_receipt.py b/pay-api/tests/unit/models/test_receipt.py index b04303dcb..9acf4b405 100644 --- a/pay-api/tests/unit/models/test_receipt.py +++ b/pay-api/tests/unit/models/test_receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_routing_slip.py b/pay-api/tests/unit/models/test_routing_slip.py index 68943e440..e4aff84b2 100644 --- a/pay-api/tests/unit/models/test_routing_slip.py +++ b/pay-api/tests/unit/models/test_routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_status_code.py b/pay-api/tests/unit/models/test_status_code.py index d67bf3b80..a85ec1ff2 100644 --- a/pay-api/tests/unit/models/test_status_code.py +++ b/pay-api/tests/unit/models/test_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/__init__.py b/pay-api/tests/unit/services/__init__.py index c467e0241..051177240 100755 --- a/pay-api/tests/unit/services/__init__.py +++ b/pay-api/tests/unit/services/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_auth.py b/pay-api/tests/unit/services/test_auth.py index d1ec39d9f..adba238fa 100644 --- a/pay-api/tests/unit/services/test_auth.py +++ b/pay-api/tests/unit/services/test_auth.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_bcol_service.py b/pay-api/tests/unit/services/test_bcol_service.py index 52cb3a044..c1f856950 100644 --- a/pay-api/tests/unit/services/test_bcol_service.py +++ b/pay-api/tests/unit/services/test_bcol_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_cfs_service.py b/pay-api/tests/unit/services/test_cfs_service.py index 94ab0d5b2..14c74377b 100644 --- a/pay-api/tests/unit/services/test_cfs_service.py +++ b/pay-api/tests/unit/services/test_cfs_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_code.py b/pay-api/tests/unit/services/test_code.py index 165838cb5..5232be98c 100644 --- a/pay-api/tests/unit/services/test_code.py +++ b/pay-api/tests/unit/services/test_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_comment.py b/pay-api/tests/unit/services/test_comment.py index 17beae748..e8bd43232 100644 --- a/pay-api/tests/unit/services/test_comment.py +++ b/pay-api/tests/unit/services/test_comment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_distribution_code.py b/pay-api/tests/unit/services/test_distribution_code.py index 26dd69c5c..d4bcad620 100644 --- a/pay-api/tests/unit/services/test_distribution_code.py +++ b/pay-api/tests/unit/services/test_distribution_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_eft_service.py b/pay-api/tests/unit/services/test_eft_service.py index b3382b78f..c236f925d 100644 --- a/pay-api/tests/unit/services/test_eft_service.py +++ b/pay-api/tests/unit/services/test_eft_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_fee_schedule.py b/pay-api/tests/unit/services/test_fee_schedule.py index a0a8307a9..aed929a34 100644 --- a/pay-api/tests/unit/services/test_fee_schedule.py +++ b/pay-api/tests/unit/services/test_fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_flags.py b/pay-api/tests/unit/services/test_flags.py index f1259206c..b59a11aba 100644 --- a/pay-api/tests/unit/services/test_flags.py +++ b/pay-api/tests/unit/services/test_flags.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_gcp_queue.py b/pay-api/tests/unit/services/test_gcp_queue.py index 6b293f1b5..8ded5facb 100644 --- a/pay-api/tests/unit/services/test_gcp_queue.py +++ b/pay-api/tests/unit/services/test_gcp_queue.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,29 +18,27 @@ """ from unittest.mock import ANY, MagicMock, patch +from dataclasses import asdict +from dotenv import load_dotenv +from gcp_queue.gcp_queue import GcpQueue +import humps import pytest -from flask import Flask -from pay_api.services.gcp_queue.gcp_queue import GcpQueue +from pay_api import create_app +from pay_api.services import gcp_queue_publisher +from pay_api.services.payment_transaction import PaymentToken from pay_api.services.gcp_queue_publisher import QueueMessage, publish_to_queue +from pay_api.utils.enums import TransactionStatus -@pytest.fixture(autouse=True) -def setup(): - """Initialize app with test env for testing.""" - global app - app = Flask(__name__) - app.env = 'testing' - - -@pytest.fixture(autouse=True) +@pytest.fixture() def mock_publisher_client(): """Mock the PublisherClient used in GcpQueue.""" with patch('google.cloud.pubsub_v1.PublisherClient') as publisher: yield publisher.return_value -@pytest.fixture(autouse=True) +@pytest.fixture() def mock_credentials(): """Mock Credentials.""" with patch('google.auth.jwt.Credentials') as mock: @@ -48,7 +46,7 @@ def mock_credentials(): yield mock -def test_publish_to_queue_success(): +def test_publish_to_queue_success(app, mock_credentials, mock_publisher_client): """Test publishing to GCP PubSub Queue successfully.""" with patch.object(GcpQueue, 'publish') as mock_publisher: with app.app_context(): @@ -63,17 +61,34 @@ def test_publish_to_queue_success(): mock_publisher.assert_called_once_with('projects/project-id/topics/topic', ANY) -def test_publish_to_queue_no_topic(): +def test_publish_to_queue_no_topic(app, mock_credentials, mock_publisher_client): """Test that publish_to_queue does not publish if no topic is set.""" with patch.object(GcpQueue, 'publish') as mock_publisher: - with patch.object(Flask, 'logger') as logger: - with app.app_context(): - queue_message = QueueMessage( - source='test-source', - message_type='test-message-type', - payload={'key': 'value'}, - topic=None - ) - publish_to_queue(queue_message) - mock_publisher.publish.assert_not_called() - logger.info.assert_called_once_with('Skipping queue message topic not set.') + with app.app_context(): + queue_message = QueueMessage( + source='test-source', + message_type='test-message-type', + payload={'key': 'value'}, + topic=None + ) + publish_to_queue(queue_message) + mock_publisher.publish.assert_not_called() + + +@pytest.mark.skip(reason='ADHOC only test.') +def test_gcp_pubsub_connectivity(monkeypatch): + """Test that a queue can publish to gcp pubsub.""" + # We don't want any of the monkeypatches by the fixtures. + monkeypatch.undo() + load_dotenv('.env') + app_prod = create_app('production') + payload = humps.camelize(asdict(PaymentToken(55, TransactionStatus.COMPLETED.value, 55, 'NRO'))) + with app_prod.app_context(): + gcp_queue_publisher.publish_to_queue( + QueueMessage(source='test', message_type='bc.registry.payment', payload=payload, + topic=app_prod.config.get('NAMEX_PAY_TOPIC')) + ) + gcp_queue_publisher.publish_to_queue( + QueueMessage(source='test', message_type='test', payload={'key': 'value'}, + topic=app_prod.config.get('ACCOUNT_MAILER_TOPIC')) + ) diff --git a/pay-api/tests/unit/services/test_hashing_service.py b/pay-api/tests/unit/services/test_hashing_service.py index 97a390115..b65b2defe 100644 --- a/pay-api/tests/unit/services/test_hashing_service.py +++ b/pay-api/tests/unit/services/test_hashing_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_invoice.py b/pay-api/tests/unit/services/test_invoice.py index c0030af6e..1c97a686e 100644 --- a/pay-api/tests/unit/services/test_invoice.py +++ b/pay-api/tests/unit/services/test_invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_invoice_reference.py b/pay-api/tests/unit/services/test_invoice_reference.py index 8d52fddda..3b34e3763 100644 --- a/pay-api/tests/unit/services/test_invoice_reference.py +++ b/pay-api/tests/unit/services/test_invoice_reference.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_non_sufficient_funds.py b/pay-api/tests/unit/services/test_non_sufficient_funds.py index 8f639fdd6..d10e33d31 100644 --- a/pay-api/tests/unit/services/test_non_sufficient_funds.py +++ b/pay-api/tests/unit/services/test_non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_oauth_service.py b/pay-api/tests/unit/services/test_oauth_service.py index e1a35f197..a49473ec8 100644 --- a/pay-api/tests/unit/services/test_oauth_service.py +++ b/pay-api/tests/unit/services/test_oauth_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_pad_service.py b/pay-api/tests/unit/services/test_pad_service.py index 276b49938..99cf83071 100644 --- a/pay-api/tests/unit/services/test_pad_service.py +++ b/pay-api/tests/unit/services/test_pad_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment.py b/pay-api/tests/unit/services/test_payment.py index dbde1d521..ecdce80d0 100644 --- a/pay-api/tests/unit/services/test_payment.py +++ b/pay-api/tests/unit/services/test_payment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_account.py b/pay-api/tests/unit/services/test_payment_account.py index d6e465752..da1a626dd 100644 --- a/pay-api/tests/unit/services/test_payment_account.py +++ b/pay-api/tests/unit/services/test_payment_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_line_item.py b/pay-api/tests/unit/services/test_payment_line_item.py index c5157f259..db802f04c 100644 --- a/pay-api/tests/unit/services/test_payment_line_item.py +++ b/pay-api/tests/unit/services/test_payment_line_item.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_service.py b/pay-api/tests/unit/services/test_payment_service.py index 727842b62..be9483799 100644 --- a/pay-api/tests/unit/services/test_payment_service.py +++ b/pay-api/tests/unit/services/test_payment_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_system_service.py b/pay-api/tests/unit/services/test_payment_system_service.py index b36490955..619c34ca8 100644 --- a/pay-api/tests/unit/services/test_payment_system_service.py +++ b/pay-api/tests/unit/services/test_payment_system_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_transaction.py b/pay-api/tests/unit/services/test_payment_transaction.py index 52e9c3f22..2f6387be3 100644 --- a/pay-api/tests/unit/services/test_payment_transaction.py +++ b/pay-api/tests/unit/services/test_payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_receipt.py b/pay-api/tests/unit/services/test_receipt.py index 357aa0e35..688c9c8c0 100644 --- a/pay-api/tests/unit/services/test_receipt.py +++ b/pay-api/tests/unit/services/test_receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_refund.py b/pay-api/tests/unit/services/test_refund.py index d28e305e1..a4a06986f 100644 --- a/pay-api/tests/unit/services/test_refund.py +++ b/pay-api/tests/unit/services/test_refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_routing_slip_service.py b/pay-api/tests/unit/services/test_routing_slip_service.py index f62c8ae16..ff283b40c 100644 --- a/pay-api/tests/unit/services/test_routing_slip_service.py +++ b/pay-api/tests/unit/services/test_routing_slip_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_statement.py b/pay-api/tests/unit/services/test_statement.py index 99239f168..5700ed97f 100644 --- a/pay-api/tests/unit/services/test_statement.py +++ b/pay-api/tests/unit/services/test_statement.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_statement_settings.py b/pay-api/tests/unit/services/test_statement_settings.py index fdabd32cb..c3dd9378d 100644 --- a/pay-api/tests/unit/services/test_statement_settings.py +++ b/pay-api/tests/unit/services/test_statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_wire_service.py b/pay-api/tests/unit/services/test_wire_service.py index c7a5427a3..528234770 100644 --- a/pay-api/tests/unit/services/test_wire_service.py +++ b/pay-api/tests/unit/services/test_wire_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/__init__.py b/pay-api/tests/unit/utils/__init__.py index a41ec6419..16727833c 100755 --- a/pay-api/tests/unit/utils/__init__.py +++ b/pay-api/tests/unit/utils/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_error.py b/pay-api/tests/unit/utils/test_error.py index 1b93a07ac..f0dd7b189 100755 --- a/pay-api/tests/unit/utils/test_error.py +++ b/pay-api/tests/unit/utils/test_error.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_logging.py b/pay-api/tests/unit/utils/test_logging.py index 81f8de9cc..0f07a8843 100755 --- a/pay-api/tests/unit/utils/test_logging.py +++ b/pay-api/tests/unit/utils/test_logging.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_util.py b/pay-api/tests/unit/utils/test_util.py index f40af87a6..80aae8383 100644 --- a/pay-api/tests/unit/utils/test_util.py +++ b/pay-api/tests/unit/utils/test_util.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_util_cors.py b/pay-api/tests/unit/utils/test_util_cors.py index df4bbf050..fea00d6f8 100755 --- a/pay-api/tests/unit/utils/test_util_cors.py +++ b/pay-api/tests/unit/utils/test_util_cors.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/__init__.py b/pay-api/tests/utilities/__init__.py index eb431264d..af6a70494 100644 --- a/pay-api/tests/utilities/__init__.py +++ b/pay-api/tests/utilities/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/base_test.py b/pay-api/tests/utilities/base_test.py index 0c322bce6..adf30be69 100644 --- a/pay-api/tests/utilities/base_test.py +++ b/pay-api/tests/utilities/base_test.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/decorators.py b/pay-api/tests/utilities/decorators.py index 88deec93f..d6be57699 100644 --- a/pay-api/tests/utilities/decorators.py +++ b/pay-api/tests/utilities/decorators.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/schema_assertions.py b/pay-api/tests/utilities/schema_assertions.py index 24d3d1f8e..55cd2e6b3 100755 --- a/pay-api/tests/utilities/schema_assertions.py +++ b/pay-api/tests/utilities/schema_assertions.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/wsgi.py b/pay-api/wsgi.py index d584bdf8a..7c25417b6 100755 --- a/pay-api/wsgi.py +++ b/pay-api/wsgi.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/README.md b/pay-queue/README.md index b98948f78..f2709a676 100755 --- a/pay-queue/README.md +++ b/pay-queue/README.md @@ -3,11 +3,11 @@ # Application Name -BC Registries Payment Reconciliation Queue +BC Registries Pay Queue ## Technology Stack Used * Python, Flask -* Postgres - SQLAlchemy, psycopg2-binary & alembic +* Postgres - SQLAlchemy, psycopg2-binary, alembic & Google PUB/SUB ## License diff --git a/pay-queue/app.py b/pay-queue/app.py index dd7bd16a7..a1f9fdbe5 100755 --- a/pay-queue/app.py +++ b/pay-queue/app.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -14,13 +14,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Provides the WSGI entry point for running the application.""" -import os +"""Initialize Flask app.""" +import os from pay_queue import create_app - app = create_app() if __name__ == '__main__': - server_port = os.environ.get('PORT', '8080') + server_port = os.environ.get('PORT', '5001') app.run(debug=False, port=server_port, host='0.0.0.0') diff --git a/pay-queue/devops/vaults.gcp.env b/pay-queue/devops/vaults.gcp.env index fa6f68780..ee10ddf5f 100644 --- a/pay-queue/devops/vaults.gcp.env +++ b/pay-queue/devops/vaults.gcp.env @@ -16,9 +16,8 @@ EFT_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/eft/EFT_INVOICE_PREF AUDIENCE="op://gcp-queue/$APP_ENV/base/AUDIENCE" GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/base/GCP_AUTH_KEY" PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" -PAY_SUB_AUDIENCE="op://gcp-queue/$APP_ENV/base/PAY_SUB_AUDIENCE" -VERIFY_PUBSUB_EMAIL="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_EMAIL" -VERIFY_PUBSUB_VIA_JWT="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_VIA_JWT" +PAY_AUDIENCE_SUB="op://gcp-queue/$APP_ENV/base/PAY_AUDIENCE_SUB" +VERIFY_PUBSUB_EMAILS="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_EMAILS" DEBUG_REQUEST="op://gcp-queue/$APP_ENV/base/DEBUG_REQUEST" ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" @@ -26,4 +25,3 @@ SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" LEGISLATIVE_TIMEZONE="op://relationship/$APP_ENV/pay-api/LEGISLATIVE_TIMEZONE" ACCOUNT_SECRET_KEY="op://relationship/$APP_ENV/pay-api/ACCOUNT_SECRET_KEY" DISABLE_EJV_ERROR_EMAIL="True" -DISABLE_PAD_SUCCESS_EMAIL="False" diff --git a/pay-queue/devops/vaults.json b/pay-queue/devops/vaults.json new file mode 100644 index 000000000..54912865c --- /dev/null +++ b/pay-queue/devops/vaults.json @@ -0,0 +1,58 @@ +[ + { + "vault": "shared", + "application": [ + "encryption-key" + ] + }, + { + "vault": "minio", + "application": [ + "base", + "pay-queue" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "base", + "account-events-listener", + "account-mailer", + "payment", + "pay-queue" + ] + }, + { + "vault": "payment-external-services", + "application": [ + "cfs" + ] + }, + { + "vault": "relationship", + "application": [ + "postgres-pay" + ] + }, + { + "vault": "sentry", + "application": [ + "relationship-api" + ] + }, + { + "vault": "launchdarkly", + "application": [ + "pay" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "a083gt", + "authpay", + "gtksf3", + "topics" + ] + } +] diff --git a/pay-queue/logging.conf b/pay-queue/logging.conf deleted file mode 100644 index ded5cb81c..000000000 --- a/pay-queue/logging.conf +++ /dev/null @@ -1,34 +0,0 @@ -[loggers] -keys=root,api,asyncio - -[handlers] -keys=console - -[formatters] -keys=simple - -[logger_root] -level=DEBUG -handlers=console - -[logger_asyncio] -level=DEBUG -handlers=console -qualname=asyncio -propagate=0 - -[logger_api] -level=DEBUG -handlers=console -qualname=api -propagate=0 - -[handler_console] -class=StreamHandler -level=DEBUG -formatter=simple -args=(sys.stdout,) - -[formatter_simple] -format=%(asctime)s - %(name)s - %(levelname)s in %(module)s:%(filename)s:%(lineno)d - %(funcName)s: %(message)s -datefmt= diff --git a/pay-queue/scripts/verify_license_headers.sh b/pay-queue/scripts/verify_license_headers.sh index 028b95c63..a160d3ac8 100755 --- a/pay-queue/scripts/verify_license_headers.sh +++ b/pay-queue/scripts/verify_license_headers.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ # limitations under the License. -COPYRIGHT="Copyright © 2019 Province of British Columbia" +COPYRIGHT="Copyright © 2024 Province of British Columbia" RET=0 for file in $(find $@ -not \( -path */venv -prune \) -not \( -path */migrations -prune \) -not \( -path */tests -prune \) -not \( -path */.egg* -prune \) -name \*.py) diff --git a/pay-queue/setup.cfg b/pay-queue/setup.cfg index a09beb534..a3d8c7414 100644 --- a/pay-queue/setup.cfg +++ b/pay-queue/setup.cfg @@ -1,6 +1,6 @@ [metadata] -name = pay-queue -url = https://github.com/bcgov/sbc-pay/pay-queue +name = pay_queue +url = https://github.com/bcgov/sbc-pay/ author = SBC Relationships team author_email = classifiers = @@ -22,7 +22,7 @@ include_package_data = True packages = find: [options.package_data] -reconciliations = +pay_queue = [wheel] universal = 1 @@ -34,6 +34,7 @@ universal = 1 test = pytest [flake8] +ignore = B902 exclude = .git,*migrations* max-line-length = 120 docstring-min-length=10 @@ -45,7 +46,7 @@ max_line_length = 120 ignore = E501 docstring-min-length=10 notes=FIXME,XXX # TODO is ignored -match_dir = src/reconciliations +match_dir = src/pay_queue ignored-modules=flask_sqlalchemy sqlalchemy per-file-ignores = diff --git a/pay-queue/setup.py b/pay-queue/setup.py index 31939e558..9bc1ac212 100644 --- a/pay-queue/setup.py +++ b/pay-queue/setup.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia. +# Copyright © 2024 Province of British Columbia. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ def read(filepath): REQUIREMENTS = read_requirements('requirements.txt') setup( - name="reconciliations", + name="pay_queue", version=version, author_email='', packages=find_packages('src'), diff --git a/pay-queue/src/pay_queue/__init__.py b/pay-queue/src/pay_queue/__init__.py index 01c2cf845..1fe3ebdcc 100644 --- a/pay-queue/src/pay_queue/__init__.py +++ b/pay-queue/src/pay_queue/__init__.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""The Reconciliations queue service. +"""The pay-queue service. The service worker for applying payments, receipts and account balance to payment system. """ @@ -23,21 +23,26 @@ from flask import Flask from pay_api.models import db from pay_api.services.flags import flags +from pay_api.services.gcp_queue import queue +from pay_api.utils.cache import cache +from pay_api.utils.logging import setup_logging from pay_api.utils.run_version import get_run_version from sentry_sdk.integrations.flask import FlaskIntegration -from pay_queue.config import CONFIGURATION +from pay_queue import config from pay_queue.version import __version__ from .resources import register_endpoints -from .services import queue + + +setup_logging(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')) -> Flask: """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.env = run_mode - app.config.from_object(CONFIGURATION[run_mode]) + app.config.from_object(config.CONFIGURATION[run_mode]) # Configure Sentry if dsn := app.config.get('SENTRY_DSN', None): @@ -48,10 +53,25 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')) -> Flask: send_default_pii=False, ) + queue.init_app(app) flags.init_app(app) db.init_app(app) - queue.init_app(app) register_endpoints(app) + build_cache(app) return app + + +def build_cache(app): + """Build cache.""" + cache.init_app(app) + with app.app_context(): + cache.clear() + if not app.config.get('TESTING', False): + try: + from pay_api.services.code import Code as CodeService # pylint: disable=import-outside-toplevel + CodeService.build_all_codes_cache() + except Exception as e: # NOQA pylint:disable=broad-except + app.logger.error('Error on caching ') + app.logger.error(e) diff --git a/pay-queue/src/pay_queue/config.py b/pay-queue/src/pay_queue/config.py index cbceb71f4..1a284c1a5 100644 --- a/pay-queue/src/pay_queue/config.py +++ b/pay-queue/src/pay_queue/config.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ def get_named_config(config_name: str = 'production'): return app_config -class _Config(): # pylint: disable=too-few-public-methods +class _Config(): # pylint: disable=too-few-public-methods,protected-access """Base class configuration that should set reasonable defaults. Used as the base for all the other configurations. @@ -99,19 +99,14 @@ class _Config(): # pylint: disable=too-few-public-methods # Disable EJV Error Email DISABLE_EJV_ERROR_EMAIL = os.getenv('DISABLE_EJV_ERROR_EMAIL', 'true').lower() == 'true' - # Disable PAD Success Email - Incase we need to reprocess records weeks/months later - DISABLE_PAD_SUCCESS_EMAIL = os.getenv('DISABLE_PAD_SUCCESS_EMAIL', 'false').lower() == 'true' - # GCP PubSub - AUDIENCE = os.getenv('AUDIENCE', None) - GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) - PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) - ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) - PAY_SUB_AUDIENCE = os.getenv('PAY_SUB_AUDIENCE', None) - VERIFY_PUBSUB_EMAIL = os.getenv('VERIFY_PUBSUB_EMAIL', None) - - VERIFY_PUBSUB_VIA_JWT = os.getenv('VERIFY_PUBSUB_VIA_JWT', 'true').lower() == 'true' - VERIFY_PUBSUB_VIA_JWT = os.getenv('DEBUG_REQUEST', 'true').lower() == 'true' + # PUB/SUB - PUB: account-mailer-dev, auth-event-dev, SUB to ftp-poller-payment-reconciliation-dev, business-events + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') + AUTH_EVENT_TOPIC = os.getenv('AUTH_EVENT_TOPIC', 'auth-event-dev') + GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) + # If blank in PUBSUB, this should match the https endpoint the subscription is pushing to. + PAY_AUDIENCE_SUB = os.getenv('PAY_AUDIENCE_SUB', None) + VERIFY_PUBSUB_EMAILS = f'{os.getenv("AUTHPAY_SERVICE_ACCOUNT")},{os.getenv("BUSINESS_SERVICE_ACCOUNT")}'.split(',') class DevConfig(_Config): # pylint: disable=too-few-public-methods @@ -157,11 +152,13 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods ACCOUNT_SECRET_KEY = os.getenv('ACCOUNT_SECRET_KEY', 'test') # Secrets for integration tests - TEST_GCP_PROJECT_NAME = 'abdefg-dev' + TEST_GCP_PROJECT_NAME = 'pay-queue-dev' # Needs to have ftp-poller-dev in it. TEST_GCP_TOPICS = ['account-mailer-dev', 'ftp-poller-dev', 'business-identifier-update-pay-dev'] TEST_PUSH_ENDPOINT_PORT = 5020 TEST_PUSH_ENDPOINT = os.getenv('TEST_PUSH_ENDPOINT', f'http://host.docker.internal:{str(TEST_PUSH_ENDPOINT_PORT)}/') + GCP_AUTH_KEY = None + DISABLE_EJV_ERROR_EMAIL = False class ProdConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/pay-queue/src/pay_queue/enums.py b/pay-queue/src/pay_queue/enums.py index cf16aabf2..f23c76288 100644 --- a/pay-queue/src/pay_queue/enums.py +++ b/pay-queue/src/pay_queue/enums.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/src/pay_queue/external/__init__.py b/pay-queue/src/pay_queue/external/__init__.py new file mode 100644 index 000000000..de7538dc3 --- /dev/null +++ b/pay-queue/src/pay_queue/external/__init__.py @@ -0,0 +1,14 @@ +# Copyright © 2024 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""External to gcp auth.""" diff --git a/pay-queue/src/pay_queue/external/gcp_auth.py b/pay-queue/src/pay_queue/external/gcp_auth.py index 31f575ac6..398b9ca5f 100644 --- a/pay-queue/src/pay_queue/external/gcp_auth.py +++ b/pay-queue/src/pay_queue/external/gcp_auth.py @@ -19,15 +19,16 @@ def verify_jwt(session): claims = id_token.verify_oauth2_token( jwt_token, Request(session=session), - audience=current_app.config.get('PAY_SUB_AUDIENCE') + audience=current_app.config.get('PAY_AUDIENCE_SUB') ) - # Check if the email is verified and matches the configured email - required_email = current_app.config.get('VERIFY_PUBSUB_EMAIL') - if not claims.get('email_verified') or claims.get('email') != required_email: + required_emails = current_app.config.get('VERIFY_PUBSUB_EMAILS') + if claims.get('email_verified') and claims.get('email') in required_emails: + return None + else: return 'Email not verified or does not match', 401 except Exception as e: + current_app.logger.info(f'Invalid token {e}') return f'Invalid token: {e}', 400 - return None def ensure_authorized_queue_user(f): @@ -35,12 +36,7 @@ def ensure_authorized_queue_user(f): @functools.wraps(f) def decorated_function(*args, **kwargs): # Use CacheControl to avoid re-fetching certificates for every request. - if current_app.config.get('DEBUG_REQUEST') is True: - current_app.logger.info(f'Headers: {request.headers}') - verifyJWT = current_app.config.get('VERIFY_PUBSUB_VIA_JWT', True) - current_app.logger.info(f'verifyJWT: {verifyJWT}') - if verifyJWT is True: - if message := verify_jwt(CacheControl(Session())): - abort(HTTPStatus.UNAUTHORIZED) + if verify_jwt(CacheControl(Session())): + abort(HTTPStatus.UNAUTHORIZED) return f(*args, **kwargs) return decorated_function diff --git a/pay-queue/src/pay_queue/minio.py b/pay-queue/src/pay_queue/minio.py index ad58ae8cd..d8e8f4eec 100644 --- a/pay-queue/src/pay_queue/minio.py +++ b/pay-queue/src/pay_queue/minio.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/src/pay_queue/resources/worker.py b/pay-queue/src/pay_queue/resources/worker.py index d429ec693..aee57b242 100644 --- a/pay-queue/src/pay_queue/resources/worker.py +++ b/pay-queue/src/pay_queue/resources/worker.py @@ -12,13 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. """Worker resource to handle incoming queue pushes from gcp.""" + +import dataclasses +import json from http import HTTPStatus -from flask import Blueprint, request -from pay_api.utils.enums import MessageType +from flask import Blueprint, current_app, request +from pay_api.services.gcp_queue_publisher import queue +from sbc_common_components.utils.enums import QueueMessageTypes from pay_queue.external.gcp_auth import ensure_authorized_queue_user -from pay_queue.services import queue, update_temporary_identifier +from pay_queue.services import update_temporary_identifier from pay_queue.services.cgi_reconciliations import reconcile_distributions from pay_queue.services.eft.eft_reconciliation import reconcile_eft_payments from pay_queue.services.payment_reconciliations import reconcile_payments @@ -31,22 +35,27 @@ @ensure_authorized_queue_user def worker(): """Worker to handle incoming queue pushes.""" - if not (ce := queue.get_simple_cloud_event(request)): - # Return a 200, so event is removed from the Queue + ce = queue.get_simple_cloud_event(request, wrapped=True) + if not ce: return {}, HTTPStatus.OK - match ce.type: - case MessageType.CAS_UPLOADED.value: + try: + current_app.logger.info('Event Message Received: %s ', json.dumps(dataclasses.asdict(ce))) + if ce.type == QueueMessageTypes.CAS_MESSAGE_TYPE.value: reconcile_payments(ce.data) - case MessageType.CGI_ACK_RECEIVED.value: + elif ce.type == QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value: reconcile_distributions(ce.data) - case MessageType.CGI_FEEDBACK_RECEIVED.value: + elif ce.type == QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value: reconcile_distributions(ce.data, is_feedback=True) - case MessageType.EFT_FILE_UPLOADED.value: + elif ce.type == QueueMessageTypes.EFT_FILE_UPLOADED.value: reconcile_eft_payments(ce.data) - case MessageType.INCORPORATION.value | MessageType.REGISTRATION.value: + elif ce.type in [QueueMessageTypes.INCORPORATION.value, QueueMessageTypes.REGISTRATION.value]: update_temporary_identifier(ce.data) - case _: + else: raise Exception('Invalid queue message type') # pylint: disable=broad-exception-raised - return {}, HTTPStatus.OK + return {}, HTTPStatus.OK + except Exception: # pylint: disable=broad-exception-caught + current_app.logger.error('Failed to process queue message: %s', HTTPStatus.INTERNAL_SERVER_ERROR) + # Optionally, return an error status code or message + return {}, HTTPStatus.OK diff --git a/pay-queue/src/pay_queue/services/__init__.py b/pay-queue/src/pay_queue/services/__init__.py index c86353543..45b466b91 100644 --- a/pay-queue/src/pay_queue/services/__init__.py +++ b/pay-queue/src/pay_queue/services/__init__.py @@ -33,9 +33,4 @@ # POSSIBILITY OF SUCH DAMAGE. """This module provides Queue type services.""" -from pay_api.services.gcp_queue import GcpQueue - from .identifier_updater import update_temporary_identifier - - -queue = GcpQueue() diff --git a/pay-queue/src/pay_queue/services/cgi_reconciliations.py b/pay-queue/src/pay_queue/services/cgi_reconciliations.py index 9926acb86..e47945b69 100644 --- a/pay-queue/src/pay_queue/services/cgi_reconciliations.py +++ b/pay-queue/src/pay_queue/services/cgi_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.utils.enums import ( - DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, + DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, PaymentSystem, QueueSources, RoutingSlipStatus) from sentry_sdk import capture_message @@ -90,25 +90,46 @@ def _update_acknowledgement(msg: Dict[str, any]): def _update_feedback(msg: Dict[str, any]): # pylint:disable=too-many-locals, too-many-statements # Read the file and find records from the database, and update status. + file_name: str = msg.get('fileName') minio_location: str = msg.get('location') file = get_object(minio_location, file_name) content = file.data.decode('utf-8-sig') group_batches: List[str] = _group_batches(content) - has_errors, already_processed = _process_ejv_feedback(group_batches['EJV'], file_name) - if not already_processed: - has_errors = _process_ap_feedback(group_batches['AP']) or has_errors + if _is_processed_or_processing(group_batches['EJV'], file_name): + return + + has_errors = _process_ejv_feedback(group_batches['EJV']) + has_errors = _process_ap_feedback(group_batches['AP']) or has_errors - if has_errors and not APP_CONFIG.DISABLE_EJV_ERROR_EMAIL: - _publish_mailer_events(file_name, minio_location) - current_app.logger.info('> update_feedback') + if has_errors and not APP_CONFIG.DISABLE_EJV_ERROR_EMAIL: + _publish_mailer_events(file_name, minio_location) + current_app.logger.info('Feedback file processing completed.') -def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:disable=too-many-locals +def _is_processed_or_processing(group_batches, file_name) -> bool: + """Check to see if file has already been processed. Mark them as processing.""" + for group_batch in group_batches: + ejv_file: Optional[EjvFileModel] = None + for line in group_batch.splitlines(): + is_batch_group: bool = line[2:4] == 'BG' + if is_batch_group: + batch_number = int(line[15:24]) + ejv_file = EjvFileModel.find_by_id(batch_number) + if ejv_file.feedback_file_ref: + current_app.logger.info( + 'EJV file id %s with feedback file %s is already processing or has been processed. Skipping.', + batch_number, file_name) + return True + ejv_file.feedback_file_ref = file_name + ejv_file.save() + return False + + +def _process_ejv_feedback(group_batches) -> bool: # pylint:disable=too-many-locals """Process EJV Feedback contents.""" has_errors = False - already_processed = False for group_batch in group_batches: ejv_file: Optional[EjvFileModel] = None receipt_number: Optional[str] = None @@ -121,13 +142,6 @@ def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:disable=t if is_batch_group: batch_number = int(line[15:24]) ejv_file = EjvFileModel.find_by_id(batch_number) - if ejv_file.feedback_file_ref: - current_app.logger.info( - 'EJV file id %s with feedback file %s has already been processed, skipping.', - batch_number, file_name) - already_processed = True - return has_errors, already_processed - ejv_file.feedback_file_ref = file_name elif is_batch_header: return_code = line[7:11] return_message = line[11:161] @@ -155,7 +169,7 @@ def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:disable=t has_errors = _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number) db.session.commit() - return has_errors, already_processed + return has_errors def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number): # pylint:disable=too-many-locals @@ -174,6 +188,7 @@ def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number): # invoice_return_message = line[319:469] # If the JV process failed, then mark the GL code against the invoice to be stopped # for further JV process for the credit GL. + logger.info('Is Credit or Debit %s - %s', line[104:105], ejv_file.file_type) current_app.logger.info('Is Credit or Debit %s - %s', line[104:105], ejv_file.file_type) if line[104:105] == 'C' and ejv_file.file_type == EjvFileType.DISBURSEMENT.value: disbursement_status = _get_disbursement_status(invoice_return_code) @@ -251,7 +266,7 @@ def _fix_invoice_line(line): # Check for zeros within 300->315 range. Bump them over with spaces. if (zero_position := line[300:315].find('0')) > -1: spaces_to_insert = 15 - zero_position - return line[:300+zero_position] + (' ' * spaces_to_insert) + line[300+zero_position:] + return line[:300 + zero_position] + (' ' * spaces_to_insert) + line[300 + zero_position:] return line @@ -316,7 +331,7 @@ def _publish_mailer_events(file_name: str, minio_location: str): gcp_queue_publisher.publish_to_queue( QueueMessage( source=QueueSources.PAY_QUEUE.value, - message_type=MessageType.EJV_FAILED.value, + message_type=QueueMessageTypes.EJV_FAILED.value, payload=payload, topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') ) diff --git a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py index dd0774b1b..8ff86f2b9 100644 --- a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py +++ b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py @@ -64,8 +64,9 @@ def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-many-loc eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( EFTFileModel.file_ref == file_name).one_or_none() - if eft_file_model and eft_file_model.status_code == EFTProcessStatus.COMPLETED.value: - current_app.logger.info('File: %s already completed processing on %s.', file_name, eft_file_model.completed_on) + if eft_file_model and eft_file_model.status_code in \ + [EFTProcessStatus.IN_PROGRESS.value, EFTProcessStatus.COMPLETED.value]: + current_app.logger.info('File: %s already %s.', file_name, str(eft_file_model.status_code)) return # There is no existing EFT File record - instantiate one diff --git a/pay-queue/src/pay_queue/services/payment_reconciliations.py b/pay-queue/src/pay_queue/services/payment_reconciliations.py index de5cefeb7..82105187d 100644 --- a/pay-queue/src/pay_queue/services/payment_reconciliations.py +++ b/pay-queue/src/pay_queue/services/payment_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,9 +37,9 @@ from pay_api.services.non_sufficient_funds import NonSufficientFundsService from pay_api.services.payment_transaction import PaymentTransaction as PaymentTransactionService from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, MessageType, PaymentMethod, PaymentStatus, - QueueSources) + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, PaymentStatus, QueueSources) from pay_api.utils.util import get_topic_for_corp_type +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from pay_queue import config @@ -191,12 +191,9 @@ def reconcile_payments(msg: Dict[str, any]): cas_settlement: CasSettlementModel = db.session.query(CasSettlementModel) \ .filter(CasSettlementModel.file_name == file_name).one_or_none() - if cas_settlement and not cas_settlement.processed_on: - current_app.logger.info('File: %s has attempted to be processed before.', file_name) - elif cas_settlement and cas_settlement.processed_on: - current_app.logger.info('File: %s already processed on: %s. Skipping file.', - file_name, cas_settlement.processed_on) - return + if cas_settlement: + current_app.logger.info('File: %s has been processed or processing in progress. Skipping file. ' + 'Removing this row will allow processing to be restarted.', file_name) else: current_app.logger.info('Creating cas_settlement record for file: %s', file_name) cas_settlement = _create_cas_settlement(file_name) @@ -277,15 +274,13 @@ def _process_consolidated_invoices(row): level='error') return _process_paid_invoices(inv_references, row) - if not APP_CONFIG.DISABLE_PAD_SUCCESS_EMAIL: - _publish_mailer_events(MessageType.PAD_PAYMENT_SUCCESS.value, payment_account, row) elif target_txn_status.lower() == Status.NOT_PAID.value.lower() \ or record_type in (RecordType.PADR.value, RecordType.PAYR.value): current_app.logger.info('NOT PAID. NSF identified.') # NSF Condition. Publish to account events for NSF. if _process_failed_payments(row): # Send mailer and account events to update status and send email notification - _publish_account_events(MessageType.NSF_LOCK_ACCOUNT.value, payment_account, row) + _publish_account_events(QueueMessageTypes.NSF_LOCK_ACCOUNT.value, payment_account, row) else: current_app.logger.error('Target Transaction Type is received as %s for PAD, and cannot process %s.', target_txn, row) @@ -593,7 +588,7 @@ def _publish_payment_event(inv: InvoiceModel): gcp_queue_publisher.publish_to_queue( QueueMessage( source=QueueSources.PAY_QUEUE.value, - message_type=MessageType.PAYMENT.value, + message_type=QueueMessageTypes.PAYMENT.value, payload=payload, topic=get_topic_for_corp_type(inv.corp_type_code) ) @@ -638,13 +633,13 @@ def _publish_online_banking_mailer_events(rows: List[Dict[str, str]], paid_amoun credit_amount: float = 0 if credit_rows: - message_type = 'bc.registry.payment.OverPaid' + message_type = QueueMessageTypes.ONLINE_BANKING_OVER_PAYMENT.value for row in credit_rows: credit_amount += float(_get_row_value(row, Column.APP_AMOUNT)) elif under_pay_rows: - message_type = 'bc.registry.payment.UnderPaid' + message_type = QueueMessageTypes.ONLINE_BANKING_UNDER_PAYMENT.value else: - message_type = 'bc.registry.payment.Payment' + message_type = QueueMessageTypes.ONLINE_BANKING_PAYMENT.value payload = { 'accountId': pay_account.auth_account_id, @@ -681,7 +676,7 @@ def _publish_account_events(message_type: str, pay_account: PaymentAccountModel, source=QueueSources.PAY_QUEUE.value, message_type=message_type, payload=payload, - topic=current_app.config.get('AUTH_QUEUE_TOPIC') + topic=current_app.config.get('AUTH_EVENT_TOPIC') ) ) except Exception as e: # NOQA pylint: disable=broad-except diff --git a/pay-queue/src/pay_queue/version.py b/pay-queue/src/pay_queue/version.py index 3b723bf04..c29b19b02 100644 --- a/pay-queue/src/pay_queue/version.py +++ b/pay-queue/src/pay_queue/version.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/__init__.py b/pay-queue/tests/__init__.py index 3e44a42f5..b2425e245 100644 --- a/pay-queue/tests/__init__.py +++ b/pay-queue/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/conftest.py b/pay-queue/tests/conftest.py index c691641c5..dd9dfd3f3 100644 --- a/pay-queue/tests/conftest.py +++ b/pay-queue/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,9 +30,6 @@ def app(): """Return a session-wide application configured in TEST mode.""" _app = create_app('testing') - _app.config['GCP_AUTH_KEY'] = 'xxxxx' - _app.config['AUDIENCE'] = 'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber' - _app.config['PUBLISHER_AUDIENCE'] = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher' return _app @@ -157,3 +154,19 @@ def initialize_pubsub(app): 'push_config': push_config, } ) + + +@pytest.fixture(autouse=True) +def mock_pub_sub_call(mocker): + """Mock pub sub call.""" + class PublisherMock: + """Publisher Mock.""" + + def __init__(self, *args, **kwargs): + pass + + def publish(self, *args, **kwargs): + """Publish mock.""" + raise CancelledError('This is a mock') + + mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) \ No newline at end of file diff --git a/pay-queue/tests/integration/__init__.py b/pay-queue/tests/integration/__init__.py index 44812fe3c..d95cabaff 100644 --- a/pay-queue/tests/integration/__init__.py +++ b/pay-queue/tests/integration/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/integration/factory.py b/pay-queue/tests/integration/factory.py index e7ccc4bdd..744cb377c 100644 --- a/pay-queue/tests/integration/factory.py +++ b/pay-queue/tests/integration/factory.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/integration/test_cgi_reconciliations.py b/pay-queue/tests/integration/test_cgi_reconciliations.py index f308d9f46..61ab286ac 100644 --- a/pay-queue/tests/integration/test_cgi_reconciliations.py +++ b/pay-queue/tests/integration/test_cgi_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ from pay_api.utils.enums import ( CfsAccountStatus, DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, RoutingSlipStatus) +from sbc_common_components.utils.enums import QueueMessageTypes from tests.integration.utils import add_file_event_to_queue_and_process @@ -44,7 +45,7 @@ from .utils import upload_to_minio -def test_successful_partner_ejv_reconciliations(client): +def test_successful_partner_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -107,7 +108,7 @@ def test_successful_partner_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -149,7 +150,7 @@ def test_successful_partner_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -158,7 +159,7 @@ def test_successful_partner_ejv_reconciliations(client): assert invoice.disbursement_status_code == DisbursementStatus.COMPLETED.value -def test_failed_partner_ejv_reconciliations(client): +def test_failed_partner_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -223,7 +224,7 @@ def test_failed_partner_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -265,7 +266,7 @@ def test_failed_partner_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -276,7 +277,7 @@ def test_failed_partner_ejv_reconciliations(client): assert disbursement_distribution_code.stop_ejv -def test_successful_partner_reversal_ejv_reconciliations(client): +def test_successful_partner_reversal_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -342,7 +343,7 @@ def test_successful_partner_reversal_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -384,7 +385,7 @@ def test_successful_partner_reversal_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -394,7 +395,7 @@ def test_successful_partner_reversal_ejv_reconciliations(client): assert invoice.disbursement_date == datetime(2023, 5, 29) -def test_succesful_payment_ejv_reconciliations(client): +def test_succesful_payment_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create EJV payment accounts # 2. Create invoice and related records @@ -511,7 +512,7 @@ def test_succesful_payment_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -527,7 +528,7 @@ def test_succesful_payment_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -555,7 +556,7 @@ def test_succesful_payment_ejv_reconciliations(client): assert payment[0][0].paid_amount == inv_total_amount -def test_succesful_payment_reversal_ejv_reconciliations(client): +def test_succesful_payment_reversal_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create EJV payment accounts # 2. Create invoice and related records @@ -669,7 +670,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -685,7 +686,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -711,7 +712,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): assert payment[0][0].paid_amount == inv_total_amount -def test_successful_refund_reconciliations(client): +def test_successful_refund_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create a routing slip. # 2. Mark the routing slip for refund. @@ -759,7 +760,7 @@ def test_successful_refund_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -834,7 +835,7 @@ def test_successful_refund_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -844,7 +845,7 @@ def test_successful_refund_reconciliations(client): assert routing_slip.status == RoutingSlipStatus.REFUND_COMPLETED.value -def test_failed_refund_reconciliations(client): +def test_failed_refund_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create a routing slip. # 2. Mark the routing slip for refund. @@ -892,7 +893,7 @@ def test_failed_refund_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -968,7 +969,7 @@ def test_failed_refund_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -980,7 +981,7 @@ def test_failed_refund_reconciliations(client): assert routing_slip_2.status == RoutingSlipStatus.REFUND_REJECTED.value -def test_prevent_duplicate_ack(client): +def test_prevent_duplicate_ack(session, app, client): """Assert processing completes when existing ack.""" file_ref = f'INBOX.{datetime.now()}' # Upload an acknowledgement file @@ -994,18 +995,18 @@ def test_prevent_duplicate_ack(client): jv_file.write('') jv_file.close() - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value # Nothing should change, because it's already processed this ACK. ejv.disbursement_status_code = DisbursementStatus.UPLOADED.value - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.UPLOADED.value -def test_successful_ap_disbursement(client): +def test_successful_ap_disbursement(session, app, client): """Test Reconciliations worker for ap disbursement.""" # 1. Create invoice. # 2. Create a AP reconciliation file. @@ -1065,7 +1066,7 @@ def test_successful_ap_disbursement(client): upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1138,7 +1139,7 @@ def test_successful_ap_disbursement(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value @@ -1154,7 +1155,7 @@ def test_successful_ap_disbursement(client): assert refund.gl_posted is not None -def test_failure_ap_disbursement(client): +def test_failure_ap_disbursement(session, app, client): """Test Reconciliations worker for ap disbursement.""" # 1. Create invoice. # 2. Create a AP reconciliation file. @@ -1212,7 +1213,7 @@ def test_failure_ap_disbursement(client): upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1288,7 +1289,7 @@ def test_failure_ap_disbursement(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value diff --git a/pay-queue/tests/integration/test_eft_reconciliation.py b/pay-queue/tests/integration/test_eft_reconciliation.py index 114ae89e2..64ba097c7 100644 --- a/pay-queue/tests/integration/test_eft_reconciliation.py +++ b/pay-queue/tests/integration/test_eft_reconciliation.py @@ -19,6 +19,7 @@ from datetime import datetime from typing import List +from flask import current_app from pay_api import db from pay_api.models import EFTCredit as EFTCreditModel from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel @@ -29,6 +30,7 @@ from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, MessageType, PaymentMethod +from sbc_common_components.utils.enums import QueueMessageTypes from pay_queue.services.eft.eft_enums import EFTConstants from tests.integration.factory import factory_create_eft_account, factory_invoice @@ -36,7 +38,7 @@ from tests.utilities.factory_utils import factory_eft_header, factory_eft_record, factory_eft_trailer -def test_eft_tdi17_fail_header(client): +def test_eft_tdi17_fail_header(session, app, client): """Test EFT Reconciliations properly fails for a bad EFT header.""" # Generate file with invalid header file_name: str = 'test_eft_tdi17.txt' @@ -45,7 +47,7 @@ def test_eft_tdi17_fail_header(client): create_and_upload_eft_file(file_name, [header]) - add_file_event_to_queue_and_process(client, file_name, MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name, QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -88,7 +90,7 @@ def test_eft_tdi17_fail_header(client): assert not bool(eft_transactions) -def test_eft_tdi17_fail_trailer(client): +def test_eft_tdi17_fail_trailer(session, app, client): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' @@ -99,7 +101,9 @@ def test_eft_tdi17_fail_trailer(client): create_and_upload_eft_file(file_name, [header, trailer]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -142,7 +146,7 @@ def test_eft_tdi17_fail_trailer(client): assert not bool(eft_transactions) -def test_eft_tdi17_fail_transactions(client): +def test_eft_tdi17_fail_transactions(session, app, client): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' @@ -161,7 +165,9 @@ def test_eft_tdi17_fail_transactions(client): create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -199,13 +205,15 @@ def test_eft_tdi17_fail_transactions(client): assert eft_transactions[0].error_messages[0] == 'Invalid transaction deposit amount CAD.' -def test_eft_tdi17_basic_process(client): +def test_eft_tdi17_basic_process(session, app, client): """Test EFT Reconciliations worker is able to create basic EFT processing records.""" # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_basic_tdi17_file(file_name) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -281,14 +289,20 @@ def test_eft_tdi17_basic_process(client): assert not eft_credit_invoice_links -def test_eft_tdi17_process(client): +def test_eft_tdi17_process(session, app, client): """Test EFT Reconciliations worker.""" payment_account, eft_shortname, invoice = create_test_data() + + assert payment_account is not None + assert eft_shortname is not None + assert invoice is not None # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_tdi17_file(file_name) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -342,8 +356,7 @@ def test_eft_tdi17_process(client): assert eft_shortnames[1].short_name == 'ABC123' # NOTE THIS NEEDS TO BE RE-WRITTEN INSIDE OF THE JOB. - # today = datetime.now().date() - + today = datetime.now().date() # # Assert Invoice is paid # invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) # expected_amount = 100 @@ -410,7 +423,7 @@ def test_eft_tdi17_process(client): # assert eft_credit_invoice_links[0].invoice_id == invoice.id -def test_eft_tdi17_rerun(client): +def test_eft_tdi17_rerun(session, app, client): """Test EFT Reconciliations can be re-executed with a corrected file.""" payment_account, eft_shortname, invoice = create_test_data() @@ -431,7 +444,9 @@ def test_eft_tdi17_rerun(client): create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -470,7 +485,9 @@ def test_eft_tdi17_rerun(client): jv_number='002425669', transaction_date='') create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Check file is completed after correction eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( diff --git a/pay-queue/tests/integration/test_payment_reconciliations.py b/pay-queue/tests/integration/test_payment_reconciliations.py index 8e6850413..ff12a1f6e 100644 --- a/pay-queue/tests/integration/test_payment_reconciliations.py +++ b/pay-queue/tests/integration/test_payment_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ from pay_api.models import Receipt as ReceiptModel from pay_api.utils.enums import ( CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus) +from sbc_common_components.utils.enums import QueueMessageTypes from pay_queue.enums import RecordType, SourceTransaction, Status, TargetTransaction @@ -37,7 +38,7 @@ from .utils import add_file_event_to_queue_and_process, create_and_upload_settlement_file -def test_online_banking_reconciliations(client): +def test_online_banking_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -67,7 +68,9 @@ def test_online_banking_reconciliations(client): TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -81,7 +84,7 @@ def test_online_banking_reconciliations(client): assert payment.invoice_number == invoice_number -def test_online_banking_reconciliations_over_payment(client): +def test_online_banking_reconciliations_over_payment(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -113,7 +116,9 @@ def test_online_banking_reconciliations_over_payment(client): over_payment_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, over_payment_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -127,7 +132,7 @@ def test_online_banking_reconciliations_over_payment(client): assert payment.invoice_number is None # No invoice_number if payment is not for 1 invoice -def test_online_banking_reconciliations_with_credit(client): +def test_online_banking_reconciliations_with_credit(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -159,7 +164,9 @@ def test_online_banking_reconciliations_with_credit(client): credit_row = [RecordType.ONAC.value, SourceTransaction.EFT_WIRE.value, '555566677', 100001, date, credit_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -173,7 +180,7 @@ def test_online_banking_reconciliations_with_credit(client): assert payment.invoice_number == invoice_number -def test_online_banking_reconciliations_overflows_credit(client): +def test_online_banking_reconciliations_overflows_credit(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -210,7 +217,9 @@ def test_online_banking_reconciliations_overflows_credit(client): Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row, onac_row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -224,7 +233,7 @@ def test_online_banking_reconciliations_overflows_credit(client): assert payment.invoice_number is None -def test_online_banking_under_payment(client): +def test_online_banking_under_payment(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -256,7 +265,9 @@ def test_online_banking_under_payment(client): TargetTransaction.INV.value, invoice_number, total, total - paid_amount, Status.PARTIAL.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id) @@ -271,7 +282,7 @@ def test_online_banking_under_payment(client): assert payment.invoice_number == invoice_number -def test_pad_reconciliations(client): +def test_pad_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoices and related records @@ -313,7 +324,9 @@ def test_pad_reconciliations(client): 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -335,7 +348,7 @@ def test_pad_reconciliations(client): assert rcpt1.receipt_date == rcpt2.receipt_date -def test_pad_reconciliations_with_credit_memo(client): +def test_pad_reconciliations_with_credit_memo(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoices and related records @@ -381,7 +394,9 @@ def test_pad_reconciliations_with_credit_memo(client): pad_row = [RecordType.PAD.value, SourceTransaction.PAD.value, receipt_number, 100001, date, total - credit_amount, cfs_account_number, 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [credit_row, pad_row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -403,7 +418,7 @@ def test_pad_reconciliations_with_credit_memo(client): assert rcpt1.receipt_date == rcpt2.receipt_date -def test_pad_nsf_reconciliations(client): +def test_pad_nsf_reconciliations(session, app, client): """Test Reconciliations worker for NSF.""" # 1. Create payment account # 2. Create invoices and related records @@ -446,7 +461,9 @@ def test_pad_nsf_reconciliations(client): 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -465,7 +482,7 @@ def test_pad_nsf_reconciliations(client): assert cfs_account.status == CfsAccountStatus.FREEZE.value -def test_pad_reversal_reconciliations(client): +def test_pad_reversal_reconciliations(session, app, client): """Test Reconciliations worker for NSF.""" # 1. Create payment account # 2. Create invoices and related records for a completed payment @@ -517,7 +534,9 @@ def test_pad_reversal_reconciliations(client): 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -541,7 +560,7 @@ def test_pad_reversal_reconciliations(client): @pytest.mark.asyncio -async def test_eft_wire_reconciliations(client): +async def test_eft_wire_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -582,7 +601,9 @@ async def test_eft_wire_reconciliations(client): row = [RecordType.EFTP.value, SourceTransaction.EFT_WIRE.value, eft_wire_receipt, 100001, date, total, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -595,7 +616,7 @@ async def test_eft_wire_reconciliations(client): @pytest.mark.asyncio -async def test_credits(client, monkeypatch): +async def test_credits(session, app, client, monkeypatch): """Test Reconciliations worker.""" # 1. Create payment account. # 2. Create EFT/WIRE payment db record. @@ -657,7 +678,9 @@ def mock_cms(cfs_account: CfsAccountModel, cfs_account_number, TargetTransaction.RECEIPT.value, eft_wire_receipt, onac_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # Look up credit file and make sure the credits are recorded. pay_account = PaymentAccountModel.find_by_id(pay_account_id) diff --git a/pay-queue/tests/integration/test_worker_queue.py b/pay-queue/tests/integration/test_worker_queue.py index 932aa48bd..79d1002e2 100644 --- a/pay-queue/tests/integration/test_worker_queue.py +++ b/pay-queue/tests/integration/test_worker_queue.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ from .utils import helper_add_identifier_event_to_queue -def test_update_payment(client): +def test_update_payment(session, app, client): """Assert that the update internal payment records works.""" # vars old_identifier = 'T000000000' diff --git a/pay-queue/tests/integration/utils.py b/pay-queue/tests/integration/utils.py index 58d190d4b..07b9e7c7e 100644 --- a/pay-queue/tests/integration/utils.py +++ b/pay-queue/tests/integration/utils.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,7 +27,8 @@ from minio import Minio from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage -from pay_api.utils.enums import MessageType, QueueSources +from pay_api.utils.enums import QueueSources +from sbc_common_components.utils.enums import QueueMessageTypes from simple_cloudevent import SimpleCloudEvent, to_queue_message @@ -98,7 +99,7 @@ def upload_to_minio(value_as_bytes, file_name: str): os.stat(file_name).st_size) -def forward_incoming_message_to_test_instance(client): +def forward_incoming_message_to_test_instance(session, app, client): """Forward incoming http message to test instance.""" # Note this is a bit different than how the queue could behave, it could send multiples. # This just receives one HTTP request and forwards it to the test instance. @@ -122,7 +123,7 @@ def forward_incoming_message_to_test_instance(client): assert tries > 0 -def add_file_event_to_queue_and_process(client, file_name: str, message_type: str, use_pubsub_emulator=True): +def add_file_event_to_queue_and_process(client, file_name: str, message_type: str, use_pubsub_emulator=False): """Add event to the Queue.""" queue_payload = { 'fileName': file_name, @@ -146,7 +147,7 @@ def add_file_event_to_queue_and_process(client, file_name: str, message_type: st def helper_add_identifier_event_to_queue(client, old_identifier: str = 'T1234567890', new_identifier: str = 'BC1234567890'): """Add event to the Queue.""" - message_type = MessageType.INCORPORATION.value + message_type = QueueMessageTypes.INCORPORATION.value queue_payload = { 'filing': { 'header': {'filingId': '12345678'}, diff --git a/report-api/requirements.txt b/report-api/requirements.txt index 3956cf9ad..4e81b8787 100644 --- a/report-api/requirements.txt +++ b/report-api/requirements.txt @@ -21,7 +21,7 @@ flask-restx==1.1.0 fonttools==4.43.0 gunicorn==20.1.0 html5lib==1.1 -idna==3.4 +idna==3.7 importlib-resources==5.12.0 itsdangerous==2.0.1 jaeger-client==4.8.0 From 522ed4997b03f8bb5d88864e739815548aa606be Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 30 May 2024 20:46:12 -0700 Subject: [PATCH 63/87] Fix linting issues. Make units tests pass for pay-api. --- pay-api/poetry.lock | 405 +++++++++--------- pay-api/pyproject.toml | 15 +- pay-api/src/pay_api/__init__.py | 1 - .../src/pay_api/services/payment_account.py | 6 +- .../pay_api/services/payment_transaction.py | 14 +- 5 files changed, 218 insertions(+), 223 deletions(-) diff --git a/pay-api/poetry.lock b/pay-api/poetry.lock index 49cd2bdc5..3253cd826 100644 --- a/pay-api/poetry.lock +++ b/pay-api/poetry.lock @@ -32,13 +32,13 @@ files = [ [[package]] name = "astroid" -version = "3.1.0" +version = "3.2.2" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, ] [[package]] @@ -62,13 +62,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.1.0" +version = "2.2.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false python-versions = ">=3.8" files = [ - {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, - {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, + {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, + {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, ] [package.dependencies] @@ -331,63 +331,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.4" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, - {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, - {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, - {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, - {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, - {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, - {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, - {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, - {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, - {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, - {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, - {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, - {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, - {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.extras] @@ -522,13 +522,13 @@ tests = ["coverage", "coveralls", "dill", "mock", "nose"] [[package]] name = "faker" -version = "24.3.0" +version = "24.14.1" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-24.3.0-py3-none-any.whl", hash = "sha256:9978025e765ba79f8bf6154c9630a9c2b7f9c9b0f175d4ad5e04b19a82a8d8d6"}, - {file = "Faker-24.3.0.tar.gz", hash = "sha256:5fb5aa9749d09971e04a41281ae3ceda9414f683d4810a694f8a8eebb8f9edec"}, + {file = "Faker-24.14.1-py3-none-any.whl", hash = "sha256:a5edba3aa17a1d689c8907e5b0cd1653079c2466a4807f083aa7b5f80a00225d"}, + {file = "Faker-24.14.1.tar.gz", hash = "sha256:380a3697e696ae4fcf50a93a3d9e0286fab7dfbf05a9caa4421fa4727c6b1e89"}, ] [package.dependencies] @@ -646,13 +646,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.1.0" +version = "2.3.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, - {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, ] [package.dependencies] @@ -674,25 +674,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask_jwt_oidc" -version = "0.3.0" -description = "Flask JWT OIDC" +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" optional = false -python-versions = "*" +python-versions = "^3.9" files = [] develop = false [package.dependencies] -cachelib = "*" -flask = "*" -python-jose = "*" -six = "*" +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" [package.source] type = "git" -url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" [[package]] name = "flask-marshmallow" @@ -793,27 +793,49 @@ sqlalchemy = ">=2.0.16" [[package]] name = "freezegun" -version = "1.4.0" +version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, - {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, ] [package.dependencies] python-dateutil = ">=2.7" +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + [[package]] name = "google-api-core" -version = "2.17.1" +version = "2.19.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, - {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, ] [package.dependencies] @@ -821,6 +843,7 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -831,13 +854,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.28.1" +version = "2.29.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, - {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, ] [package.dependencies] @@ -854,13 +877,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.20.0" +version = "2.21.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, - {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, ] [package.dependencies] @@ -982,84 +1005,76 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.62.1" +version = "1.64.0" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, - {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, - {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, - {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, - {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, - {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, - {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, - {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, - {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, - {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, - {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, - {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, - {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, - {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, - {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, - {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, - {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, - {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, - {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, - {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, - {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, - {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, - {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, - {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.62.1)"] +protobuf = ["grpcio-tools (>=1.64.0)"] [[package]] name = "grpcio-status" -version = "1.62.1" +version = "1.62.2" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, - {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.1" +grpcio = ">=1.62.2" protobuf = ">=4.21.6" [[package]] @@ -1214,28 +1229,26 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "9.2.2" +version = "8.2.1" description = "LaunchDarkly SDK for Python" optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, - {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" -launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.26.0,<3" +urllib3 = ">=1.22.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] -test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1429,43 +1442,44 @@ flake8 = ">=5.0.0" [[package]] name = "pg8000" -version = "1.30.5" +version = "1.31.2" description = "PostgreSQL interface library" optional = false python-versions = ">=3.8" files = [ - {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, - {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, + {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, + {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, ] [package.dependencies] python-dateutil = ">=2.8.2" -scramp = ">=1.4.4" +scramp = ">=1.4.5" [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -1695,17 +1709,17 @@ files = [ [[package]] name = "pylint" -version = "3.1.0" +version = "3.2.2" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, ] [package.dependencies] -astroid = ">=3.1.0,<=3.2.0-dev0" +astroid = ">=3.2.2,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -1801,23 +1815,23 @@ files = [ [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" @@ -1839,17 +1853,17 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.12.0" +version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, - {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, ] [package.dependencies] -pytest = ">=5.0" +pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] @@ -1969,18 +1983,18 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" subdirectory = "python" [[package]] name = "scramp" -version = "1.4.4" +version = "1.4.5" description = "An implementation of the SCRAM protocol." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, - {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, + {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, + {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, ] [package.dependencies] @@ -2044,19 +2058,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "69.2.0" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simple-cloudevent" @@ -2111,7 +2124,7 @@ develop = false type = "git" url = "https://github.com/bcgov/lear.git" reference = "feature-legal-name" -resolved_reference = "bb3209f8e8894c9b9f7be95a9fd871c644e2ec69" +resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" subdirectory = "python/common/sql-versioning" [[package]] @@ -2273,13 +2286,13 @@ twisted = ["twisted"] [[package]] name = "tomlkit" -version = "0.12.4" +version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] @@ -2350,4 +2363,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "6ed39989416242b0917aea66317b7c94e2d6bdc34e58c391ce23c15defbd55e6" +content-hash = "fbf5a52a364793793ad0d5944f9f222cc7fcc3af331f493284727ff4aa2cd5de" diff --git a/pay-api/pyproject.toml b/pay-api/pyproject.toml index c9639f753..b8335b7a8 100644 --- a/pay-api/pyproject.toml +++ b/pay-api/pyproject.toml @@ -7,7 +7,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" -flask-caching = "2.1.0" +flask-caching = "2.3.0" flask-cors = "4.0.0" flask-migrate = "4.0.7" flask-moment = "1.0.5" @@ -35,16 +35,10 @@ cryptography = "42.0.5" dpath = "2.1.6" ecdsa = "0.18.0" expiringdict = "1.2.2" -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} flask-marshmallow = "1.2.0" -google-api-core = "2.17.1" -google-auth = "2.28.1" -google-cloud-pubsub = "2.20.0" -googleapis-common-protos = "1.63.0" +gcp-queue = { git = "https://github.com/seeker25/sbc-connect-common.git", subdirectory = "python/gcp-queue", branch = "main" } greenlet = "3.0.3" -grpc-google-iam-v1 = "0.13.0" -grpcio-status = "1.62.1" -grpcio = "1.62.1" gunicorn = "21.2.0" holidays = "0.37" idna = "3.6" @@ -52,7 +46,7 @@ itsdangerous = "2.1.2" jaeger-client = "4.8.0" jsonschema = "4.17.3" launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "9.2.2" +launchdarkly-server-sdk = "8.2.1" marshmallow-sqlalchemy = "1.0.0" marshmallow = "3.21.1" opentracing = "2.4.0" @@ -80,7 +74,6 @@ thrift = "0.16.0" tornado = "6.4" typing-extensions = "4.10.0" urllib3 = "2.2.1" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} pg8000 = "^1.30.5" sql-versioning = { git = "https://github.com/bcgov/lear.git", subdirectory = "python/common/sql-versioning", branch = "feature-legal-name" } diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index 950fd5e65..c5216cd3e 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -36,7 +36,6 @@ from pay_api.utils.cache import cache from pay_api.utils.logging import setup_logging from pay_api.utils.run_version import get_run_version -from .services.gcp_queue import queue setup_logging(os.path.join(_Config.PROJECT_ROOT, 'logging.conf')) diff --git a/pay-api/src/pay_api/services/payment_account.py b/pay-api/src/pay_api/services/payment_account.py index 6f7348590..d67bdca16 100644 --- a/pay-api/src/pay_api/services/payment_account.py +++ b/pay-api/src/pay_api/services/payment_account.py @@ -47,7 +47,7 @@ from pay_api.services.statement_settings import StatementSettings from pay_api.utils.enums import ( AuthHeaderType, CfsAccountStatus, ContentType, InvoiceStatus, PaymentMethod, PaymentSystem, QueueSources, - QueueSources, StatementFrequency) + StatementFrequency) from pay_api.utils.errors import Error from pay_api.utils.user_context import UserContext, user_context from pay_api.utils.util import ( @@ -822,7 +822,7 @@ def _publish_queue_message(self, payload: dict, message_type: str): """Publish to account mailer to send out confirmation email or notification email.""" try: gcp_queue_publisher.publish_to_queue( - gcp_queue_publisher.QueueMessage( + QueueMessage( source=QueueSources.PAY_API.value, message_type=message_type, payload=payload, @@ -884,7 +884,7 @@ def unlock_frozen_accounts(payment: Payment): try: gcp_queue_publisher.publish_to_queue( - gcp_queue_publisher.QueueMessage( + QueueMessage( source=QueueSources.PAY_API.value, message_type=QueueMessageTypes.NSF_UNLOCK_ACCOUNT.value, payload=payload, diff --git a/pay-api/src/pay_api/services/payment_transaction.py b/pay-api/src/pay_api/services/payment_transaction.py index bfd19f6db..3fa1cea81 100644 --- a/pay-api/src/pay_api/services/payment_transaction.py +++ b/pay-api/src/pay_api/services/payment_transaction.py @@ -16,9 +16,9 @@ from __future__ import annotations import uuid -from dataclasses import asdict, dataclass +from dataclasses import asdict from datetime import datetime -from typing import Dict, List, Optional +from typing import Dict, List import humps from flask import current_app @@ -45,16 +45,6 @@ from .payment import Payment -@dataclass -class PaymentToken: - """Payment Token payload common interface for LEAR and Names.""" - - id: Optional[str] = None - status_code: Optional[str] = None - filing_identifier: Optional[str] = None - corp_type_code: Optional[str] = None - - class PaymentTransaction: # pylint: disable=too-many-instance-attributes, too-many-public-methods """Service to manage Payment transaction operations.""" From 7411bde70b1664887bbced197999d53379ba1213 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 30 May 2024 20:54:28 -0700 Subject: [PATCH 64/87] Fix lint and test for ftp-poller --- jobs/ftp-poller/invoke_jobs.py | 3 +- .../openshift/ftp-poller-build.json | 139 ---- .../openshift/ftp-poller-deploy.json | 417 ------------ jobs/ftp-poller/poetry.lock | 622 ++++++++++-------- jobs/ftp-poller/pyproject.toml | 6 +- 5 files changed, 366 insertions(+), 821 deletions(-) delete mode 100644 jobs/ftp-poller/openshift/ftp-poller-build.json delete mode 100644 jobs/ftp-poller/openshift/ftp-poller-deploy.json diff --git a/jobs/ftp-poller/invoke_jobs.py b/jobs/ftp-poller/invoke_jobs.py index eddfc319f..875ff01e8 100755 --- a/jobs/ftp-poller/invoke_jobs.py +++ b/jobs/ftp-poller/invoke_jobs.py @@ -44,9 +44,8 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): dsn=app.config.get('SENTRY_DSN'), integrations=[FlaskIntegration()] ) - app.logger.info(f'<<<< Starting Ftp Poller Job >>>>') + app.logger.info('<<<< Starting Ftp Poller Job >>>>') queue.init_app(app) - db.init_app(app) ma.init_app(app) register_shellcontext(app) diff --git a/jobs/ftp-poller/openshift/ftp-poller-build.json b/jobs/ftp-poller/openshift/ftp-poller-build.json deleted file mode 100644 index 8713416c5..000000000 --- a/jobs/ftp-poller/openshift/ftp-poller-build.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a FTP Poller job.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build-template" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "ftp-poller" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "jobs/ftp-poller" - }, - { - "name": "SOURCE_IMAGE_KIND", - "displayName": "Source Image Kind", - "required": true, - "description": "The 'kind' (type) of the source image; typically ImageStreamTag, or DockerImage.", - "value": "ImageStreamTag" - }, - { - "name": "SOURCE_IMAGE_NAME_SPACE", - "displayName": "Source Image Name Space", - "required": true, - "description": "The name space of the source image.", - "value": "d7eovc-tools" - }, - { - "name": "SOURCE_IMAGE_NAME", - "displayName": "Source Image Name", - "required": true, - "description": "The name of the source image.", - "value": "python" - }, - { - "name": "SOURCE_IMAGE_TAG", - "displayName": "Source Image Tag", - "required": true, - "description": "The tag of the source image.", - "value": "3.7" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/jobs/ftp-poller/openshift/ftp-poller-deploy.json b/jobs/ftp-poller/openshift/ftp-poller-deploy.json deleted file mode 100644 index 87dd37b9e..000000000 --- a/jobs/ftp-poller/openshift/ftp-poller-deploy.json +++ /dev/null @@ -1,417 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a ftp poller job.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": 1, - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "volumes": [ - { - "name": "cron-config", - "configMap": { - "name": "${NAME}-${TAG_NAME}-cron-configuration", - "defaultMode": 420 - } - }, - { - "name": "sftp-private-key", - "configMap": { - "name": "${NAME}-${TAG_NAME}-sftp-configuration", - "defaultMode": 420 - } - } - ], - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "volumeMounts": [ - { - "name": "cron-config", - "readOnly": true, - "mountPath": "/ftp-poller/cron/" - }, - { - "name": "sftp-private-key", - "readOnly": true, - "mountPath": "/ftp-poller/key/" - } - ], - "env": [ - { - "name": "DATABASE_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_USER" - } - } - }, - { - "name": "DATABASE_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PASSWORD" - } - } - }, - { - "name": "DATABASE_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_NAME" - } - } - }, - { - "name": "DATABASE_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_HOST" - } - } - }, - { - "name": "DATABASE_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_PORT" - } - } - }, - { - "name": "DATABASE_TEST_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_USER" - } - } - }, - { - "name": "DATABASE_TEST_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_TEST_PASSWORD" - } - } - }, - { - "name": "DATABASE_TEST_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_NAME" - } - } - }, - { - "name": "DATABASE_TEST_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_HOST" - } - } - }, - { - "name": "DATABASE_TEST_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_PORT" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_REF_NUMBER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_REF_NUMBER" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_API_KEY", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_API_KEY" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_BASE_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_BASE_URL" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_ID" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_SECRET" - } - } - }, - { - "name": "NOTIFY_API_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "NOTIFY_API_URL" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_ID" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_SECRET" - } - } - }, - { - "name": "JWT_OIDC_ISSUER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "JWT_OIDC_ISSUER" - } - } - }, - { - "name": "AUTH_WEB_PAY_TRANSACTION_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "AUTH_WEB_PAY_TRANSACTION_URL" - } - } - } - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "creationTimestamp": null, - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "ftp-poller" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} \ No newline at end of file diff --git a/jobs/ftp-poller/poetry.lock b/jobs/ftp-poller/poetry.lock index 7aa6aec79..befc94f7c 100644 --- a/jobs/ftp-poller/poetry.lock +++ b/jobs/ftp-poller/poetry.lock @@ -90,15 +90,26 @@ cffi = ">=1.0.1" dev = ["cogapp", "pre-commit", "pytest", "wheel"] tests = ["pytest"] +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + [[package]] name = "astroid" -version = "3.1.0" +version = "3.2.2" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, ] [[package]] @@ -122,52 +133,52 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.0.4" +version = "2.2.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, - {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, + {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, + {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, ] [package.dependencies] -pycodestyle = ">=2.10.0" +pycodestyle = ">=2.11.0" [[package]] name = "bcrypt" -version = "4.1.2" +version = "4.1.3" description = "Modern password hashing for your software and your servers" optional = false python-versions = ">=3.7" files = [ - {file = "bcrypt-4.1.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e"}, - {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1"}, - {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326"}, - {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c"}, - {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966"}, - {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2"}, - {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c"}, - {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5"}, - {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0"}, - {file = "bcrypt-4.1.2-cp37-abi3-win32.whl", hash = "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"}, - {file = "bcrypt-4.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551"}, - {file = "bcrypt-4.1.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63"}, - {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483"}, - {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc"}, - {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7"}, - {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb"}, - {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1"}, - {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4"}, - {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c"}, - {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a"}, - {file = "bcrypt-4.1.2-cp39-abi3-win32.whl", hash = "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f"}, - {file = "bcrypt-4.1.2-cp39-abi3-win_amd64.whl", hash = "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42"}, - {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946"}, - {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d"}, - {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab"}, - {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb"}, - {file = "bcrypt-4.1.2.tar.gz", hash = "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258"}, + {file = "bcrypt-4.1.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:48429c83292b57bf4af6ab75809f8f4daf52aa5d480632e53707805cc1ce9b74"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8bea4c152b91fd8319fef4c6a790da5c07840421c2b785084989bf8bbb7455"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d3b317050a9a711a5c7214bf04e28333cf528e0ed0ec9a4e55ba628d0f07c1a"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:094fd31e08c2b102a14880ee5b3d09913ecf334cd604af27e1013c76831f7b05"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4fb253d65da30d9269e0a6f4b0de32bd657a0208a6f4e43d3e645774fb5457f3"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:193bb49eeeb9c1e2db9ba65d09dc6384edd5608d9d672b4125e9320af9153a15"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:8cbb119267068c2581ae38790e0d1fbae65d0725247a930fc9900c285d95725d"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6cac78a8d42f9d120b3987f82252bdbeb7e6e900a5e1ba37f6be6fe4e3848286"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01746eb2c4299dd0ae1670234bf77704f581dd72cc180f444bfe74eb80495b64"}, + {file = "bcrypt-4.1.3-cp37-abi3-win32.whl", hash = "sha256:037c5bf7c196a63dcce75545c8874610c600809d5d82c305dd327cd4969995bf"}, + {file = "bcrypt-4.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:8a893d192dfb7c8e883c4576813bf18bb9d59e2cfd88b68b725990f033f1b978"}, + {file = "bcrypt-4.1.3-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d4cf6ef1525f79255ef048b3489602868c47aea61f375377f0d00514fe4a78c"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5698ce5292a4e4b9e5861f7e53b1d89242ad39d54c3da451a93cac17b61921a"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec3c2e1ca3e5c4b9edb94290b356d082b721f3f50758bce7cce11d8a7c89ce84"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3a5be252fef513363fe281bafc596c31b552cf81d04c5085bc5dac29670faa08"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5f7cd3399fbc4ec290378b541b0cf3d4398e4737a65d0f938c7c0f9d5e686611"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:c4c8d9b3e97209dd7111bf726e79f638ad9224b4691d1c7cfefa571a09b1b2d6"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:31adb9cbb8737a581a843e13df22ffb7c84638342de3708a98d5c986770f2834"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:551b320396e1d05e49cc18dd77d970accd52b322441628aca04801bbd1d52a73"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6717543d2c110a155e6821ce5670c1f512f602eabb77dba95717ca76af79867d"}, + {file = "bcrypt-4.1.3-cp39-abi3-win32.whl", hash = "sha256:6004f5229b50f8493c49232b8e75726b568535fd300e5039e255d919fc3a07f2"}, + {file = "bcrypt-4.1.3-cp39-abi3-win_amd64.whl", hash = "sha256:2505b54afb074627111b5a8dc9b6ae69d0f01fea65c2fcaea403448c503d3991"}, + {file = "bcrypt-4.1.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:cb9c707c10bddaf9e5ba7cdb769f3e889e60b7d4fea22834b261f51ca2b89fed"}, + {file = "bcrypt-4.1.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9f8ea645eb94fb6e7bea0cf4ba121c07a3a182ac52876493870033141aa687bc"}, + {file = "bcrypt-4.1.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f44a97780677e7ac0ca393bd7982b19dbbd8d7228c1afe10b128fd9550eef5f1"}, + {file = "bcrypt-4.1.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d84702adb8f2798d813b17d8187d27076cca3cd52fe3686bb07a9083930ce650"}, + {file = "bcrypt-4.1.3.tar.gz", hash = "sha256:2ee15dd749f5952fe3f0430d0ff6b74082e159c50332a1413d51b5689cf06623"}, ] [package.extras] @@ -431,63 +442,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.3" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, - {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, - {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, - {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, - {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, - {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, - {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, - {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, - {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, - {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, - {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, - {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, - {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, - {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.extras] @@ -732,13 +743,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.1.0" +version = "2.3.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, - {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, ] [package.dependencies] @@ -760,25 +771,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask_jwt_oidc" -version = "0.3.0" -description = "Flask JWT OIDC" +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" optional = false -python-versions = "*" +python-versions = "^3.9" files = [] develop = false [package.dependencies] -cachelib = "*" -flask = "*" -python-jose = "*" -six = "*" +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" [package.source] type = "git" -url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" [[package]] name = "flask-marshmallow" @@ -900,15 +911,37 @@ files = [ flask = ">=2.2.5" sqlalchemy = ">=2.0.16" +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + [[package]] name = "google-api-core" -version = "2.17.1" +version = "2.19.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, - {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, ] [package.dependencies] @@ -916,6 +949,7 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -926,13 +960,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.28.1" +version = "2.29.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, - {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, ] [package.dependencies] @@ -949,13 +983,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.20.0" +version = "2.21.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, - {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, ] [package.dependencies] @@ -1077,84 +1111,76 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.62.1" +version = "1.64.0" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, - {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, - {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, - {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, - {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, - {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, - {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, - {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, - {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, - {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, - {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, - {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, - {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, - {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, - {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, - {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, - {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, - {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, - {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, - {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, - {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, - {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, - {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, - {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.62.1)"] +protobuf = ["grpcio-tools (>=1.64.0)"] [[package]] name = "grpcio-status" -version = "1.62.1" +version = "1.62.2" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, - {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.1" +grpcio = ">=1.62.2" protobuf = ">=4.21.6" [[package]] @@ -1309,28 +1335,26 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "9.2.2" +version = "8.2.1" description = "LaunchDarkly SDK for Python" optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, - {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" -launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.26.0,<3" +urllib3 = ">=1.22.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] -test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1486,13 +1510,13 @@ files = [ [[package]] name = "minio" -version = "7.2.5" +version = "7.2.7" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = "*" files = [ - {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, - {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, + {file = "minio-7.2.7-py3-none-any.whl", hash = "sha256:59d1f255d852fe7104018db75b3bebbd987e538690e680f7c5de835e422de837"}, + {file = "minio-7.2.7.tar.gz", hash = "sha256:473d5d53d79f340f3cd632054d0c82d2f93177ce1af2eac34a235bea55708d98"}, ] [package.dependencies] @@ -1548,52 +1572,91 @@ gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] invoke = ["invoke (>=2.0)"] [[package]] -name = "pay_api" -version = "0.0.0" -description = "A short description of the project" +name = "pay-api" +version = "0.1.0" +description = "" optional = false -python-versions = ">=3.12" +python-versions = "^3.12" files = [] develop = false [package.dependencies] -cattrs = "*" -croniter = "*" -cryptography = "*" -dpath = "*" -Flask = "*" -Flask-Caching = "*" -Flask-Cors = "*" -flask-jwt-oidc = "*" -flask-marshmallow = "*" -Flask-Migrate = "*" -Flask-Moment = "*" -Flask-Script = "*" -Flask-SQLAlchemy = "*" -google-auth = "2.28.1" -google-cloud-pubsub = "2.20.0" -gunicorn = "*" +alembic = "1.13.1" +attrs = "23.2.0" +blinker = "1.7.0" +cachelib = "0.9.0" +cachetools = "5.3.3" +cattrs = "23.2.3" +certifi = "2024.2.2" +cffi = "1.16.0" +charset-normalizer = "3.3.2" +click = "8.1.7" +croniter = "2.0.2" +cryptography = "42.0.5" +dpath = "2.1.6" +ecdsa = "0.18.0" +expiringdict = "1.2.2" +flask = "3.0.2" +flask-caching = "2.3.0" +flask-cors = "4.0.0" +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} +flask-marshmallow = "1.2.0" +flask-migrate = "4.0.7" +flask-moment = "1.0.5" +flask-script = "2.0.6" +flask-sqlalchemy = "3.1.1" +gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} +greenlet = "3.0.3" +gunicorn = "21.2.0" holidays = "0.37" -itsdangerous = "*" -jaeger-client = "*" -Jinja2 = "*" +idna = "3.6" +itsdangerous = "2.1.2" +jaeger-client = "4.8.0" +jinja2 = "3.1.3" jsonschema = "4.17.3" -launchdarkly-server-sdk = "*" -marshmallow-sqlalchemy = "*" -psycopg2-binary = "*" -pyhumps = "*" -python-dotenv = "*" -requests = "*" -sentry-sdk = {version = "*", extras = ["flask"]} -sqlalchemy = "*" -sqlalchemy_utils = "*" -Werkzeug = "*" +launchdarkly-eventsource = "1.1.1" +launchdarkly-server-sdk = "8.2.1" +mako = "1.3.2" +markupsafe = "2.1.5" +marshmallow = "3.21.1" +marshmallow-sqlalchemy = "1.0.0" +opentracing = "2.4.0" +packaging = "24.0" +pg8000 = "^1.30.5" +proto-plus = "1.23.0" +protobuf = "4.25.3" +psycopg2-binary = "2.9.9" +pyasn1 = "0.5.1" +pyasn1-modules = "0.3.0" +pycparser = "2.21" +pyhumps = "3.8.0" +pyrfc3339 = "1.1" +pyrsistent = "0.20.0" +python-dateutil = "2.9.0.post0" +python-dotenv = "1.0.1" +python-jose = "3.3.0" +pytz = "2024.1" +requests = "2.31.0" +rsa = "4.9" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +semver = "3.0.2" +sentry-sdk = "1.41.0" +six = "1.16.0" +sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} +sqlalchemy = "2.0.28" +sqlalchemy-utils = "0.41.1" +threadloop = "1.0.2" +thrift = "0.16.0" +tornado = "6.4" +typing-extensions = "4.10.0" +urllib3 = "2.2.1" +werkzeug = "3.0.1" [package.source] type = "git" url = "https://github.com/seeker25/sbc-pay.git" -reference = "18263" -resolved_reference = "fe42bf81c86c77946a1df238f69f12ad6b83d804" +reference = "sync-python-to-main" +resolved_reference = "522ed4997b03f8bb5d88864e739815548aa606be" subdirectory = "pay-api" [[package]] @@ -1610,30 +1673,46 @@ files = [ [package.dependencies] flake8 = ">=5.0.0" +[[package]] +name = "pg8000" +version = "1.31.2" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, + {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.5" + [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -1904,17 +1983,17 @@ files = [ [[package]] name = "pylint" -version = "3.1.0" +version = "3.2.2" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, ] [package.dependencies] -astroid = ">=3.1.0,<=3.2.0-dev0" +astroid = ">=3.2.2,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -2049,33 +2128,33 @@ paramiko = ">=1.17" [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" -version = "0.23.5.post1" +version = "0.23.7" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, - {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, + {file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"}, + {file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"}, ] [package.dependencies] @@ -2105,17 +2184,17 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.12.0" +version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, - {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, ] [package.dependencies] -pytest = ">=5.0" +pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] @@ -2235,9 +2314,23 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "94986110a7f6c7ba4f57ed8b038101ba7d864a94" +resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" subdirectory = "python" +[[package]] +name = "scramp" +version = "1.4.5" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.8" +files = [ + {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, + {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + [[package]] name = "semver" version = "3.0.2" @@ -2251,20 +2344,17 @@ files = [ [[package]] name = "sentry-sdk" -version = "1.42.0" +version = "1.41.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, - {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, + {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, + {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, ] [package.dependencies] -blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} certifi = "*" -flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} -markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} [package.extras] @@ -2284,7 +2374,6 @@ grpcio = ["grpcio (>=1.21.1)"] httpx = ["httpx (>=0.16.0)"] huey = ["huey (>=2)"] loguru = ["loguru (>=0.5)"] -openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] opentelemetry = ["opentelemetry-distro (>=0.35b0)"] opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] pure-eval = ["asttokens", "executing", "pure-eval"] @@ -2300,19 +2389,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "69.2.0" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simple-cloudevent" @@ -2354,6 +2442,22 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] +[[package]] +name = "sql-versioning" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "feature-legal-name" +resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" +subdirectory = "python/common/sql-versioning" + [[package]] name = "sqlalchemy" version = "2.0.28" @@ -2513,13 +2617,13 @@ twisted = ["twisted"] [[package]] name = "tomlkit" -version = "0.12.4" +version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] @@ -2590,4 +2694,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "f048f3fd232a6fb3c61c8d1f8651e55f81e6c283799dfde0ed451cea62a6e3a6" +content-hash = "3aeb528030f868f3ab60ec2d31aab0dd38f09872fe7ba62082c45715aaeed206" diff --git a/jobs/ftp-poller/pyproject.toml b/jobs/ftp-poller/pyproject.toml index cf312a280..34ecaccca 100644 --- a/jobs/ftp-poller/pyproject.toml +++ b/jobs/ftp-poller/pyproject.toml @@ -21,11 +21,9 @@ jaeger-client = "^4.8.0" itsdangerous = "^2.1.2" jinja2 = "^3.1.3" protobuf = "4.25.3" -launchdarkly-server-sdk = "^9.2.2" +launchdarkly-server-sdk = "^8.2.1" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} -pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "18263", subdirectory = "pay-api"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "sync-python-to-main", subdirectory = "pay-api"} [tool.poetry.group.dev.dependencies] From ff882e5d28c46ea0a0466b7bf3bf11ff0853855f Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 30 May 2024 21:24:54 -0700 Subject: [PATCH 65/87] pay-queue lint, ci passing --- jobs/ftp-poller/Makefile | 2 +- pay-queue/Makefile | 2 +- pay-queue/poetry.lock | 97 +++++++++++-------- pay-queue/pyproject.toml | 6 +- .../pay_queue/services/cgi_reconciliations.py | 6 +- pay-queue/tests/conftest.py | 5 +- .../integration/test_cgi_reconciliations.py | 19 ++-- .../integration/test_eft_reconciliation.py | 5 +- .../test_payment_reconciliations.py | 3 +- 9 files changed, 77 insertions(+), 68 deletions(-) diff --git a/jobs/ftp-poller/Makefile b/jobs/ftp-poller/Makefile index 46dd918d4..715e83f48 100644 --- a/jobs/ftp-poller/Makefile +++ b/jobs/ftp-poller/Makefile @@ -115,7 +115,7 @@ tag: push ## tag image ################################################################################# run: ## Run the project in local - . venv/bin/activate && python app.py + echo "unimplememted use docker" ################################################################################# # Self Documenting Commands # diff --git a/pay-queue/Makefile b/pay-queue/Makefile index 101a2de04..55b54a7d7 100644 --- a/pay-queue/Makefile +++ b/pay-queue/Makefile @@ -115,7 +115,7 @@ tag: push ## tag image ################################################################################# run: ## Run the project in local - . venv/bin/activate && python app.py + poetry run flask run -p 5001 ################################################################################# # Self Documenting Commands # diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index 28dfadd78..a5e397b91 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -709,13 +709,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.1.0" +version = "2.3.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, - {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, ] [package.dependencies] @@ -737,25 +737,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask_jwt_oidc" -version = "0.5.0" -description = "Flask JWT OIDC" +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" optional = false -python-versions = "*" +python-versions = "^3.9" files = [] develop = false [package.dependencies] -cachelib = "*" -flask = "*" -python-jose = "*" -six = "*" +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" [package.source] type = "git" -url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "fc60d430a69e0d59e1d659d01aff78151ba498d0" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" [[package]] name = "flask-marshmallow" @@ -868,6 +868,28 @@ files = [ [package.dependencies] python-dateutil = ">=2.7" +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + [[package]] name = "google-api-core" version = "2.17.1" @@ -894,13 +916,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.28.1" +version = "2.29.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, - {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, ] [package.dependencies] @@ -917,13 +939,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.20.0" +version = "2.21.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, - {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, ] [package.dependencies] @@ -1277,28 +1299,26 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "9.2.2" +version = "8.2.1" description = "LaunchDarkly SDK for Python" optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, - {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" -launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.26.0,<3" +urllib3 = ">=1.22.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] -test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1585,22 +1605,16 @@ dpath = "2.1.6" ecdsa = "0.18.0" expiringdict = "1.2.2" flask = "3.0.2" -flask-caching = "2.1.0" +flask-caching = "2.3.0" flask-cors = "4.0.0" -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} flask-marshmallow = "1.2.0" flask-migrate = "4.0.7" flask-moment = "1.0.5" flask-script = "2.0.6" flask-sqlalchemy = "3.1.1" -google-api-core = "2.17.1" -google-auth = "2.28.1" -google-cloud-pubsub = "2.20.0" -googleapis-common-protos = "1.63.0" +gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} greenlet = "3.0.3" -grpc-google-iam-v1 = "0.13.0" -grpcio = "1.62.1" -grpcio-status = "1.62.1" gunicorn = "21.2.0" holidays = "0.37" idna = "3.6" @@ -1609,7 +1623,7 @@ jaeger-client = "4.8.0" jinja2 = "3.1.3" jsonschema = "4.17.3" launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "9.2.2" +launchdarkly-server-sdk = "8.2.1" mako = "1.3.2" markupsafe = "2.1.5" marshmallow = "3.21.1" @@ -1635,7 +1649,6 @@ rsa = "4.9" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} semver = "3.0.2" sentry-sdk = "1.41.0" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} six = "1.16.0" sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} sqlalchemy = "2.0.28" @@ -1649,9 +1662,9 @@ werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/bcgov/sbc-pay.git" -reference = "feature-queue-python-upgrade" -resolved_reference = "031104546edc3c471f0d00bdbbf9c2025d468557" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "sync-python-to-main" +resolved_reference = "7411bde70b1664887bbced197999d53379ba1213" subdirectory = "pay-api" [[package]] @@ -2665,4 +2678,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "f7e978de8d7c39e508ff106d26b6a9fedac7896203b705955ed5b3a5d481c775" +content-hash = "8969f66a8435f9818257f133cecc641bc0cf33a3ba2712da06aae3b6453cc0b5" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml index 299044c9d..5a3688840 100644 --- a/pay-queue/pyproject.toml +++ b/pay-queue/pyproject.toml @@ -19,12 +19,10 @@ sqlalchemy = "^2.0.28" itsdangerous = "^2.1.2" jinja2 = "^3.1.3" protobuf = "4.25.3" -launchdarkly-server-sdk = "^9.2.2" +launchdarkly-server-sdk = "^8.2.1" cachecontrol = "^0.14.0" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/bcgov/sbc-pay.git", branch = "feature-queue-python-upgrade", subdirectory = "pay-api"} -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", branch = "sync-python-to-main", subdirectory = "pay-api"} pg8000 = "^1.30.5" diff --git a/pay-queue/src/pay_queue/services/cgi_reconciliations.py b/pay-queue/src/pay_queue/services/cgi_reconciliations.py index e47945b69..7ec308a8d 100644 --- a/pay-queue/src/pay_queue/services/cgi_reconciliations.py +++ b/pay-queue/src/pay_queue/services/cgi_reconciliations.py @@ -32,8 +32,9 @@ from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.utils.enums import ( - DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, - PaymentStatus, PaymentSystem, QueueSources, RoutingSlipStatus) + DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, + PaymentSystem, QueueSources, RoutingSlipStatus) +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from pay_queue import config @@ -188,7 +189,6 @@ def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number): # invoice_return_message = line[319:469] # If the JV process failed, then mark the GL code against the invoice to be stopped # for further JV process for the credit GL. - logger.info('Is Credit or Debit %s - %s', line[104:105], ejv_file.file_type) current_app.logger.info('Is Credit or Debit %s - %s', line[104:105], ejv_file.file_type) if line[104:105] == 'C' and ejv_file.file_type == EjvFileType.DISBURSEMENT.value: disbursement_status = _get_disbursement_status(invoice_return_code) diff --git a/pay-queue/tests/conftest.py b/pay-queue/tests/conftest.py index dd9dfd3f3..8c5fdebba 100644 --- a/pay-queue/tests/conftest.py +++ b/pay-queue/tests/conftest.py @@ -13,6 +13,7 @@ # limitations under the License. """Common setup and fixtures for the pytest suite used by this service.""" import os +from concurrent.futures import CancelledError import pytest from flask_migrate import Migrate, upgrade @@ -142,7 +143,7 @@ def initialize_pubsub(app): except NotFound: pass publisher.create_topic(name=topic_path) - subscription_path = subscriber.subscription_path(project, f'{topic}_subscription') + subscription_path = subscriber.subscription_path(project, f'{topic}_subscription') try: subscriber.delete_subscription(subscription=subscription_path) except NotFound: @@ -169,4 +170,4 @@ def publish(self, *args, **kwargs): """Publish mock.""" raise CancelledError('This is a mock') - mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) \ No newline at end of file + mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) diff --git a/pay-queue/tests/integration/test_cgi_reconciliations.py b/pay-queue/tests/integration/test_cgi_reconciliations.py index 61ab286ac..19ac6a16e 100644 --- a/pay-queue/tests/integration/test_cgi_reconciliations.py +++ b/pay-queue/tests/integration/test_cgi_reconciliations.py @@ -33,7 +33,7 @@ from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db from pay_api.utils.enums import ( - CfsAccountStatus, DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, MessageType, + CfsAccountStatus, DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, RoutingSlipStatus) from sbc_common_components.utils.enums import QueueMessageTypes @@ -462,10 +462,9 @@ def test_succesful_payment_ejv_reconciliations(session, app, client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() - EjvLinkModel( - link_id=inv.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value - ).save() + EjvLinkModel(link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) service_fee_amount = f'{line.service_fees:.2f}'.zfill(15) @@ -622,8 +621,8 @@ def test_succesful_payment_reversal_ejv_reconciliations(session, app, client): ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() EjvLinkModel( - link_id=inv.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) @@ -1049,12 +1048,12 @@ def test_successful_ap_disbursement(session, app, client): ejv_file_id=ejv_file.id, payment_account_id=account.id).save() EjvLinkModel( - link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() EjvLinkModel( - link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, + link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() diff --git a/pay-queue/tests/integration/test_eft_reconciliation.py b/pay-queue/tests/integration/test_eft_reconciliation.py index 64ba097c7..97fb9520c 100644 --- a/pay-queue/tests/integration/test_eft_reconciliation.py +++ b/pay-queue/tests/integration/test_eft_reconciliation.py @@ -19,7 +19,6 @@ from datetime import datetime from typing import List -from flask import current_app from pay_api import db from pay_api.models import EFTCredit as EFTCreditModel from pay_api.models import EFTCreditInvoiceLink as EFTCreditInvoiceLinkModel @@ -29,7 +28,7 @@ from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, MessageType, PaymentMethod +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, PaymentMethod from sbc_common_components.utils.enums import QueueMessageTypes from pay_queue.services.eft.eft_enums import EFTConstants @@ -356,7 +355,7 @@ def test_eft_tdi17_process(session, app, client): assert eft_shortnames[1].short_name == 'ABC123' # NOTE THIS NEEDS TO BE RE-WRITTEN INSIDE OF THE JOB. - today = datetime.now().date() + # today = datetime.now().date() # # Assert Invoice is paid # invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) # expected_amount = 100 diff --git a/pay-queue/tests/integration/test_payment_reconciliations.py b/pay-queue/tests/integration/test_payment_reconciliations.py index ff12a1f6e..d63a3d4aa 100644 --- a/pay-queue/tests/integration/test_payment_reconciliations.py +++ b/pay-queue/tests/integration/test_payment_reconciliations.py @@ -26,8 +26,7 @@ from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus) +from pay_api.utils.enums import CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus from sbc_common_components.utils.enums import QueueMessageTypes from pay_queue.enums import RecordType, SourceTransaction, Status, TargetTransaction From 1dae0d28e241a7bcdfc43dcaee1a36e35e77c31c Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 30 May 2024 21:28:40 -0700 Subject: [PATCH 66/87] update sbc-common-components, also remove because pay-api already includes it. --- pay-queue/poetry.lock | 306 +++++++++++++++++++-------------------- pay-queue/pyproject.toml | 1 - 2 files changed, 149 insertions(+), 158 deletions(-) diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index a5e397b91..ba45aacf1 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -89,13 +89,13 @@ files = [ [[package]] name = "astroid" -version = "3.1.0" +version = "3.2.2" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, ] [[package]] @@ -119,13 +119,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.1.0" +version = "2.2.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false python-versions = ">=3.8" files = [ - {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, - {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, + {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, + {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, ] [package.dependencies] @@ -408,63 +408,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.0" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, - {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, - {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, - {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, - {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, - {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, - {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, - {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, - {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, - {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, - {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, - {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, - {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, - {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.extras] @@ -856,13 +856,13 @@ sqlalchemy = ">=2.0.16" [[package]] name = "freezegun" -version = "1.5.0" +version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.5.0-py3-none-any.whl", hash = "sha256:ec3f4ba030e34eb6cf7e1e257308aee2c60c3d038ff35996d7475760c9ff3719"}, - {file = "freezegun-1.5.0.tar.gz", hash = "sha256:200a64359b363aa3653d8aac289584078386c7c3da77339d257e46a01fb5c77c"}, + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, ] [package.dependencies] @@ -892,13 +892,13 @@ subdirectory = "python/gcp-queue" [[package]] name = "google-api-core" -version = "2.17.1" +version = "2.19.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, - {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, ] [package.dependencies] @@ -906,6 +906,7 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -1067,84 +1068,76 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.62.1" +version = "1.64.0" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, - {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, - {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, - {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, - {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, - {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, - {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, - {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, - {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, - {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, - {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, - {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, - {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, - {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, - {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, - {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, - {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, - {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, - {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, - {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, - {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, - {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, - {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, - {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.62.1)"] +protobuf = ["grpcio-tools (>=1.64.0)"] [[package]] name = "grpcio-status" -version = "1.62.1" +version = "1.62.2" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, - {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.1" +grpcio = ">=1.62.2" protobuf = ">=4.21.6" [[package]] @@ -1474,13 +1467,13 @@ files = [ [[package]] name = "minio" -version = "7.2.5" +version = "7.2.7" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = "*" files = [ - {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, - {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, + {file = "minio-7.2.7-py3-none-any.whl", hash = "sha256:59d1f255d852fe7104018db75b3bebbd987e538690e680f7c5de835e422de837"}, + {file = "minio-7.2.7.tar.gz", hash = "sha256:473d5d53d79f340f3cd632054d0c82d2f93177ce1af2eac34a235bea55708d98"}, ] [package.dependencies] @@ -1664,7 +1657,7 @@ werkzeug = "3.0.1" type = "git" url = "https://github.com/seeker25/sbc-pay.git" reference = "sync-python-to-main" -resolved_reference = "7411bde70b1664887bbced197999d53379ba1213" +resolved_reference = "ff882e5d28c46ea0a0466b7bf3bf11ff0853855f" subdirectory = "pay-api" [[package]] @@ -1683,28 +1676,28 @@ flake8 = ">=5.0.0" [[package]] name = "pg8000" -version = "1.31.1" +version = "1.31.2" description = "PostgreSQL interface library" optional = false python-versions = ">=3.8" files = [ - {file = "pg8000-1.31.1-py3-none-any.whl", hash = "sha256:69aac9dba4114c9c8d0408232d54eaf7d06d271df7765caeed39960e057800e4"}, - {file = "pg8000-1.31.1.tar.gz", hash = "sha256:b11130d4c615dd3062ea8fed8143064a7978b7fe6d44f14b72261d43c8e27087"}, + {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, + {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, ] [package.dependencies] python-dateutil = ">=2.8.2" -scramp = ">=1.4.4" +scramp = ">=1.4.5" [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -2002,17 +1995,17 @@ files = [ [[package]] name = "pylint" -version = "3.1.0" +version = "3.2.2" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, ] [package.dependencies] -astroid = ">=3.1.0,<=3.2.0-dev0" +astroid = ">=3.2.2,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -2108,23 +2101,23 @@ files = [ [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" @@ -2294,7 +2287,7 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "8871ffcce8cc2232a5d7a3adb6103dfaf0d7689f" +resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" subdirectory = "python" [[package]] @@ -2372,19 +2365,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "69.5.1" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simple-cloudevent" @@ -2601,13 +2593,13 @@ twisted = ["twisted"] [[package]] name = "tomlkit" -version = "0.12.4" +version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] @@ -2678,4 +2670,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "8969f66a8435f9818257f133cecc641bc0cf33a3ba2712da06aae3b6453cc0b5" +content-hash = "91f9fa21463afa27ba2b73d8efcd0b07bac434743792659ba832a588fc331d50" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml index 5a3688840..21af5378e 100644 --- a/pay-queue/pyproject.toml +++ b/pay-queue/pyproject.toml @@ -21,7 +21,6 @@ jinja2 = "^3.1.3" protobuf = "4.25.3" launchdarkly-server-sdk = "^8.2.1" cachecontrol = "^0.14.0" -sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} pay-api = {git = "https://github.com/seeker25/sbc-pay.git", branch = "sync-python-to-main", subdirectory = "pay-api"} pg8000 = "^1.30.5" From 3bc8b6a16558665d50b3afb69dc8f2a95da0b72b Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 30 May 2024 21:29:48 -0700 Subject: [PATCH 67/87] remove requirements.txt for pay-admin --- pay-admin/requirements.txt | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 pay-admin/requirements.txt diff --git a/pay-admin/requirements.txt b/pay-admin/requirements.txt deleted file mode 100644 index a0d4f1ac5..000000000 --- a/pay-admin/requirements.txt +++ /dev/null @@ -1,34 +0,0 @@ -Authlib==1.3.0 -Flask-Admin==1.6.1 -Flask-SQLAlchemy==3.1.1 -Flask==3.0.2 -Jinja2==3.1.3 -MarkupSafe==2.1.5 -SQLAlchemy==2.0.28 -WTForms==3.1.2 -Werkzeug==3.0.1 -blinker==1.7.0 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -cryptography==42.0.5 -flask-oidc==2.1.1 -greenlet==3.0.3 -gunicorn==21.2.0 -httplib2==0.22.0 -idna==3.6 -itsdangerous==2.1.2 -packaging==23.2 -psycopg2-binary==2.9.9 -pycparser==2.21 -pyparsing==3.1.2 -python-dotenv==1.0.1 -requests-futures==1.0.1 -requests==2.31.0 -typing_extensions==4.10.0 -urllib3==2.2.1 --e git+https://github.com/bcgov/sbc-pay.git@feature-queue-python-upgrade#egg=pay-api&subdirectory=pay-api --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -git+https://github.com/daxiom/simple-cloudevent.py.git -git+https://github.com/thorwolpert/flask-jwt-oidc.git From 3cc0024430f298a460f4780bf8a55f6c84e1ec8e Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 30 May 2024 21:31:22 -0700 Subject: [PATCH 68/87] poetry update pay-api --- pay-queue/poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index ba45aacf1..f9715fdb2 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -1657,7 +1657,7 @@ werkzeug = "3.0.1" type = "git" url = "https://github.com/seeker25/sbc-pay.git" reference = "sync-python-to-main" -resolved_reference = "ff882e5d28c46ea0a0466b7bf3bf11ff0853855f" +resolved_reference = "1dae0d28e241a7bcdfc43dcaee1a36e35e77c31c" subdirectory = "pay-api" [[package]] From 34c905b46cd5922cf19cddf8a1c809edd1484c30 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Thu, 30 May 2024 21:46:14 -0700 Subject: [PATCH 69/87] Fix lint plus unit tests for payment-jobs --- jobs/payment-jobs/config.py | 1 + jobs/payment-jobs/poetry.lock | 101 ++++++++++-------- jobs/payment-jobs/pyproject.toml | 7 +- .../tasks/cfs_create_account_task.py | 2 +- .../tasks/cfs_create_invoice_task.py | 4 +- jobs/payment-jobs/tests/jobs/conftest.py | 26 +++++ 6 files changed, 89 insertions(+), 52 deletions(-) diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index 891a99927..a18c89791 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -248,6 +248,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods CGI_SFTP_PORT = 2222 CGI_SFTP_DIRECTORY = '/data/' CGI_SFTP_HOST = 'localhost' + GCP_AUTH_KEY = None class ProdConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock index ff1195ca7..0a123832e 100644 --- a/jobs/payment-jobs/poetry.lock +++ b/jobs/payment-jobs/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "alembic" @@ -784,13 +784,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.1.0" +version = "2.3.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, - {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, ] [package.dependencies] @@ -812,25 +812,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask_jwt_oidc" -version = "0.3.0" -description = "Flask JWT OIDC" +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" optional = false -python-versions = "*" +python-versions = "^3.9" files = [] develop = false [package.dependencies] -cachelib = "*" -flask = "*" -python-jose = "*" -six = "*" +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" [package.source] type = "git" -url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" [[package]] name = "flask-marshmallow" @@ -943,6 +943,28 @@ files = [ [package.dependencies] python-dateutil = ">=2.7" +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + [[package]] name = "google-api-core" version = "2.17.1" @@ -969,13 +991,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.28.1" +version = "2.29.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, - {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, ] [package.dependencies] @@ -992,13 +1014,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.20.0" +version = "2.21.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, - {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, ] [package.dependencies] @@ -1352,28 +1374,26 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "9.2.2" +version = "8.2.1" description = "LaunchDarkly SDK for Python" optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, - {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" -launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.26.0,<3" +urllib3 = ">=1.22.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] -test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1627,22 +1647,16 @@ dpath = "2.1.6" ecdsa = "0.18.0" expiringdict = "1.2.2" flask = "3.0.2" -flask-caching = "2.1.0" +flask-caching = "2.3.0" flask-cors = "4.0.0" -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} flask-marshmallow = "1.2.0" flask-migrate = "4.0.7" flask-moment = "1.0.5" flask-script = "2.0.6" flask-sqlalchemy = "3.1.1" -google-api-core = "2.17.1" -google-auth = "2.28.1" -google-cloud-pubsub = "2.20.0" -googleapis-common-protos = "1.63.0" +gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} greenlet = "3.0.3" -grpc-google-iam-v1 = "0.13.0" -grpcio = "1.62.1" -grpcio-status = "1.62.1" gunicorn = "21.2.0" holidays = "0.37" idna = "3.6" @@ -1651,7 +1665,7 @@ jaeger-client = "4.8.0" jinja2 = "3.1.3" jsonschema = "4.17.3" launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "9.2.2" +launchdarkly-server-sdk = "8.2.1" mako = "1.3.2" markupsafe = "2.1.5" marshmallow = "3.21.1" @@ -1677,7 +1691,6 @@ rsa = "4.9" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} semver = "3.0.2" sentry-sdk = "1.41.0" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} six = "1.16.0" sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} sqlalchemy = "2.0.28" @@ -1691,9 +1704,9 @@ werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/bcgov/sbc-pay.git" -reference = "feature-queue-python-upgrade" -resolved_reference = "ba20cecf7e65065fa22dbfedb0f0bb5c1ee7ec94" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "sync-python-to-main" +resolved_reference = "3cc0024430f298a460f4780bf8a55f6c84e1ec8e" subdirectory = "pay-api" [[package]] @@ -2350,7 +2363,7 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +resolved_reference = "22978d810dc4e85c51c3129936686b0a17124e64" subdirectory = "python" [[package]] @@ -2731,4 +2744,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "eaa76036fa004456010050a8138aa7b147b9f3f8f614708710e146224d71b8a4" +content-hash = "b16efd0364fcd29610e0eae102b66eda5771f66116953175e2d4e54550537cfa" diff --git a/jobs/payment-jobs/pyproject.toml b/jobs/payment-jobs/pyproject.toml index 91f347013..402cfb107 100644 --- a/jobs/payment-jobs/pyproject.toml +++ b/jobs/payment-jobs/pyproject.toml @@ -7,10 +7,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" -sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/bcgov/sbc-pay.git", branch = "feature-queue-python-upgrade", subdirectory = "pay-api"} -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", branch = "sync-python-to-main", subdirectory = "pay-api"} gunicorn = "^21.2.0" flask = "^3.0.2" flask-sqlalchemy = "^3.1.1" @@ -28,7 +25,7 @@ pysftp = "^0.2.9" flask-migrate = "^4.0.7" itsdangerous = "^2.1.2" dataclass-wizard = "^0.22.3" -launchdarkly-server-sdk = "^9.2.2" +launchdarkly-server-sdk = "^8.2.1" cx-oracle = "^8.3.0" more-itertools = "^10.2.0" pg8000 = "^1.30.5" diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index d19682ed1..55dbf497e 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -22,7 +22,7 @@ from pay_api.services.cfs_service import CFSService from pay_api.services.oauth_service import OAuthService from pay_api.utils.constants import RECEIPT_METHOD_EFT_MONTHLY, RECEIPT_METHOD_PAD_DAILY -from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, MessageType, PaymentMethod +from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, PaymentMethod from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from services import routing_slip diff --git a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py index 052b69ea0..862d2fce7 100644 --- a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py @@ -32,7 +32,7 @@ from pay_api.services.payment import Payment from pay_api.services.payment_account import PaymentAccount as PaymentAccountService from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, PaymentSystem) + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, PaymentSystem) from pay_api.utils.util import generate_transaction_number from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message @@ -454,7 +454,7 @@ def _create_eft_invoices(cls): current_app.logger.error(e) continue - mailer.publish_mailer_events(MessageType.EFT_INVOICE_CREATED, payment_account, { + mailer.publish_mailer_events(QueueMessageTypes.EFT_INVOICE_CREATED.value, payment_account, { 'invoice_total': float(invoice_total), 'invoice_process_date': f'{datetime.now(tz=timezone.utc)}' }) diff --git a/jobs/payment-jobs/tests/jobs/conftest.py b/jobs/payment-jobs/tests/jobs/conftest.py index 7287b03ce..832ed837f 100644 --- a/jobs/payment-jobs/tests/jobs/conftest.py +++ b/jobs/payment-jobs/tests/jobs/conftest.py @@ -27,16 +27,42 @@ from utils.logger import setup_logging +@pytest.fixture(autouse=True) +def mock_pub_sub_call(mocker): + """Mock pub sub call.""" + class Expando(object): + """Expando class.""" + + class PublisherMock: + """Publisher Mock.""" + + def __init__(self, *args, **kwargs): + def result(): + """Return true for mock.""" + return True + self.result = result + + def publish(self, *args, **kwargs): + """Publish mock.""" + ex = Expando() + ex.result = self.result + return ex + + mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) + + @pytest.fixture(scope='session') def app(): """Return a session-wide application configured in TEST mode.""" return create_app('testing') + @pytest.fixture(scope='function') def app_request(): """Return a session-wide application configured in TEST mode.""" return create_app('testing') + @pytest.fixture(scope='session') def client(app): # pylint: disable=redefined-outer-name """Return a session-wide Flask test client.""" From 23fed0f2445acf1c3fddbb6e412ad5ea92f4858a Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 09:06:04 -0700 Subject: [PATCH 70/87] 21464 - Resync EFT branch with main (#1547) * Merge branch 'main' of https://github.com/bcgov/sbc-pay into feature-queue-python-upgrade * Fix linting issues. Make units tests pass for pay-api. * Fix lint and test for ftp-poller * pay-queue lint, ci passing * update sbc-common-components, also remove because pay-api already includes it. * remove requirements.txt for pay-admin * poetry update pay-api * Fix lint plus unit tests for payment-jobs --- .github/workflows/pay-queue-cd.yml | 118 +++- .github/workflows/pay-queue-ci.yml | 1 - .github/workflows/pay-queue-gcp-cd.yml | 33 + .gitignore | 3 + bcol-api/requirements.txt | 2 +- jobs/ftp-poller/Makefile | 2 +- jobs/ftp-poller/config.py | 8 +- jobs/ftp-poller/devops/vaults.json | 7 + jobs/ftp-poller/invoke_jobs.py | 4 +- .../openshift/ftp-poller-build.json | 139 ---- .../openshift/ftp-poller-deploy.json | 417 ------------ jobs/ftp-poller/poetry.lock | 622 ++++++++++-------- jobs/ftp-poller/pyproject.toml | 6 +- .../tasks/cgi_feeder_poller_task.py | 6 +- jobs/ftp-poller/tasks/eft_poller_ftp.py | 4 +- jobs/ftp-poller/tests/jobs/test_sftp.py | 15 +- jobs/ftp-poller/utils/utils.py | 14 +- jobs/notebook-report/requirements.txt | 2 +- jobs/notebook-report/requirements/prod.txt | 4 +- jobs/payment-jobs/config.py | 6 + jobs/payment-jobs/devops/vaults.json | 70 ++ jobs/payment-jobs/invoke_jobs.py | 2 + jobs/payment-jobs/poetry.lock | 101 +-- jobs/payment-jobs/pyproject.toml | 7 +- .../tasks/activate_pad_account_task.py | 5 +- jobs/payment-jobs/tasks/ap_task.py | 4 +- .../tasks/cfs_create_account_task.py | 5 +- .../tasks/cfs_create_invoice_task.py | 8 +- .../tasks/unpaid_invoice_notify_task.py | 6 +- jobs/payment-jobs/tests/jobs/conftest.py | 24 + .../tests/jobs/test_statement_due_task.py | 1 + jobs/payment-jobs/utils/mailer.py | 28 +- pay-admin/requirements.txt | 34 - pay-api/devops/vaults.json | 59 ++ pay-api/gunicorn_config.py | 2 +- pay-api/manage.py | 2 +- pay-api/poetry.lock | 405 ++++++------ pay-api/pyproject.toml | 15 +- pay-api/scripts/verify_license_headers.sh | 4 +- pay-api/setup.cfg | 2 +- pay-api/setup.py | 2 +- pay-api/src/pay_api/__init__.py | 3 +- pay-api/src/pay_api/config.py | 16 +- pay-api/src/pay_api/exceptions/__init__.py | 2 +- pay-api/src/pay_api/factory/__init__.py | 2 +- .../pay_api/factory/payment_system_factory.py | 2 +- pay-api/src/pay_api/models/__init__.py | 2 +- pay-api/src/pay_api/models/account_fee.py | 2 +- pay-api/src/pay_api/models/audit.py | 2 +- pay-api/src/pay_api/models/base_model.py | 2 +- pay-api/src/pay_api/models/base_schema.py | 2 +- pay-api/src/pay_api/models/cfs_account.py | 2 +- .../pay_api/models/cfs_account_status_code.py | 2 +- pay-api/src/pay_api/models/code_table.py | 2 +- pay-api/src/pay_api/models/comment.py | 2 +- pay-api/src/pay_api/models/corp_type.py | 2 +- pay-api/src/pay_api/models/credit.py | 2 +- pay-api/src/pay_api/models/db.py | 2 +- .../models/disbursement_status_code.py | 2 +- pay-api/src/pay_api/models/ejv_file.py | 2 +- pay-api/src/pay_api/models/ejv_header.py | 2 +- pay-api/src/pay_api/models/ejv_link.py | 2 +- pay-api/src/pay_api/models/error_code.py | 2 +- pay-api/src/pay_api/models/fee_code.py | 2 +- pay-api/src/pay_api/models/fee_schedule.py | 2 +- pay-api/src/pay_api/models/filing_type.py | 2 +- pay-api/src/pay_api/models/invoice.py | 2 +- pay-api/src/pay_api/models/invoice_batch.py | 2 +- .../src/pay_api/models/invoice_batch_link.py | 2 +- .../src/pay_api/models/invoice_reference.py | 2 +- .../models/invoice_reference_status_code.py | 2 +- .../src/pay_api/models/invoice_status_code.py | 2 +- .../pay_api/models/line_item_status_code.py | 2 +- .../pay_api/models/non_sufficient_funds.py | 2 +- .../models/notification_status_code.py | 2 +- pay-api/src/pay_api/models/payment.py | 2 +- pay-api/src/pay_api/models/payment_account.py | 2 +- .../src/pay_api/models/payment_line_item.py | 2 +- pay-api/src/pay_api/models/payment_method.py | 2 +- .../src/pay_api/models/payment_status_code.py | 2 +- pay-api/src/pay_api/models/payment_system.py | 2 +- .../src/pay_api/models/payment_transaction.py | 2 +- pay-api/src/pay_api/models/receipt.py | 2 +- pay-api/src/pay_api/models/refund.py | 2 +- pay-api/src/pay_api/models/routing_slip.py | 2 +- .../models/routing_slip_status_code.py | 2 +- pay-api/src/pay_api/models/statement.py | 2 +- .../src/pay_api/models/statement_invoices.py | 2 +- .../pay_api/models/statement_recipients.py | 2 +- .../src/pay_api/models/statement_settings.py | 2 +- .../pay_api/models/transaction_status_code.py | 2 +- pay-api/src/pay_api/resources/__init__.py | 2 +- pay-api/src/pay_api/resources/ops.py | 2 +- pay-api/src/pay_api/resources/v1/account.py | 2 +- .../resources/v1/account_statements.py | 2 +- .../v1/account_statements_notifications.py | 2 +- .../v1/account_statements_settings.py | 2 +- .../src/pay_api/resources/v1/bank_accounts.py | 2 +- pay-api/src/pay_api/resources/v1/code.py | 2 +- .../src/pay_api/resources/v1/distributions.py | 2 +- .../src/pay_api/resources/v1/fas/__init__.py | 2 +- .../src/pay_api/resources/v1/fas/refund.py | 2 +- .../pay_api/resources/v1/fas/routing_slip.py | 3 +- pay-api/src/pay_api/resources/v1/fee.py | 2 +- .../src/pay_api/resources/v1/fee_schedule.py | 2 +- pay-api/src/pay_api/resources/v1/invoice.py | 2 +- .../pay_api/resources/v1/invoice_receipt.py | 2 +- pay-api/src/pay_api/resources/v1/invoices.py | 2 +- pay-api/src/pay_api/resources/v1/meta.py | 2 +- .../resources/v1/non_sufficient_funds.py | 2 +- pay-api/src/pay_api/resources/v1/payment.py | 3 +- pay-api/src/pay_api/resources/v1/refund.py | 2 +- .../src/pay_api/resources/v1/transaction.py | 3 +- pay-api/src/pay_api/schemas/__init__.py | 2 +- pay-api/src/pay_api/schemas/utils.py | 2 +- pay-api/src/pay_api/services/__init__.py | 4 +- pay-api/src/pay_api/services/auth.py | 2 +- .../pay_api/services/base_payment_system.py | 15 +- pay-api/src/pay_api/services/bcol_service.py | 2 +- pay-api/src/pay_api/services/cfs_service.py | 2 +- pay-api/src/pay_api/services/code.py | 2 +- .../src/pay_api/services/deposit_service.py | 6 +- .../pay_api/services/direct_pay_service.py | 2 +- .../src/pay_api/services/distribution_code.py | 2 +- pay-api/src/pay_api/services/eft_service.py | 2 +- .../src/pay_api/services/ejv_pay_service.py | 2 +- pay-api/src/pay_api/services/fas/__init__.py | 2 +- pay-api/src/pay_api/services/fas/comment.py | 2 +- .../src/pay_api/services/fas/routing_slip.py | 2 +- .../routing_slip_status_transition_service.py | 2 +- pay-api/src/pay_api/services/fee_schedule.py | 2 +- .../pay_api/services/gcp_queue/__init__.py | 2 +- .../pay_api/services/gcp_queue_publisher.py | 14 +- pay-api/src/pay_api/services/hashing.py | 2 +- .../pay_api/services/internal_pay_service.py | 2 +- pay-api/src/pay_api/services/invoice.py | 2 +- .../src/pay_api/services/invoice_reference.py | 2 +- .../pay_api/services/non_sufficient_funds.py | 2 +- pay-api/src/pay_api/services/oauth_service.py | 2 +- .../services/online_banking_service.py | 2 +- pay-api/src/pay_api/services/pad_service.py | 2 +- pay-api/src/pay_api/services/paybc_service.py | 2 +- pay-api/src/pay_api/services/payment.py | 2 +- .../src/pay_api/services/payment_account.py | 27 +- .../src/pay_api/services/payment_line_item.py | 2 +- .../src/pay_api/services/payment_service.py | 2 +- .../pay_api/services/payment_transaction.py | 22 +- pay-api/src/pay_api/services/receipt.py | 2 +- pay-api/src/pay_api/services/refund.py | 2 +- pay-api/src/pay_api/services/statement.py | 2 +- .../pay_api/services/statement_recipients.py | 2 +- .../pay_api/services/statement_settings.py | 2 +- pay-api/src/pay_api/services/wire_service.py | 2 +- pay-api/src/pay_api/utils/__init__.py | 2 +- pay-api/src/pay_api/utils/auth.py | 2 +- pay-api/src/pay_api/utils/cache.py | 2 +- pay-api/src/pay_api/utils/constants.py | 2 +- pay-api/src/pay_api/utils/enums.py | 32 +- pay-api/src/pay_api/utils/errors.py | 2 +- pay-api/src/pay_api/utils/logging.py | 2 +- .../utils/paybc_transaction_error_message.py | 2 +- pay-api/src/pay_api/utils/run_version.py | 2 +- pay-api/src/pay_api/utils/user_context.py | 2 +- pay-api/src/pay_api/utils/util.py | 20 +- pay-api/src/pay_api/version.py | 2 +- pay-api/tests/__init__.py | 2 +- pay-api/tests/conftest.py | 27 +- pay-api/tests/unit/__init__.py | 2 +- pay-api/tests/unit/api/__init__.py | 2 +- pay-api/tests/unit/api/fas/__init__.py | 2 +- pay-api/tests/unit/api/fas/test_refund.py | 2 +- .../tests/unit/api/fas/test_routing_slip.py | 2 +- pay-api/tests/unit/api/test_account.py | 2 +- pay-api/tests/unit/api/test_bank_accounts.py | 2 +- pay-api/tests/unit/api/test_code.py | 2 +- pay-api/tests/unit/api/test_distributions.py | 2 +- pay-api/tests/unit/api/test_fee.py | 2 +- pay-api/tests/unit/api/test_fee_schedule.py | 2 +- pay-api/tests/unit/api/test_invoice.py | 2 +- pay-api/tests/unit/api/test_meta.py | 2 +- .../unit/api/test_non_sufficient_funds.py | 2 +- pay-api/tests/unit/api/test_ops.py | 2 +- pay-api/tests/unit/api/test_payment.py | 2 +- .../tests/unit/api/test_payment_request.py | 2 +- pay-api/tests/unit/api/test_receipt.py | 2 +- pay-api/tests/unit/api/test_refund.py | 2 +- pay-api/tests/unit/api/test_statement.py | 2 +- .../tests/unit/api/test_statement_settings.py | 2 +- pay-api/tests/unit/api/test_transaction.py | 2 +- pay-api/tests/unit/conf/__init__.py | 2 +- pay-api/tests/unit/conf/test_configuration.py | 2 +- pay-api/tests/unit/conf/test_version.py | 2 +- pay-api/tests/unit/factory/__init__.py | 2 +- .../factory/test_payment_system_factory.py | 2 +- pay-api/tests/unit/models/__init__.py | 2 +- pay-api/tests/unit/models/test_comment.py | 2 +- pay-api/tests/unit/models/test_corp_type.py | 2 +- pay-api/tests/unit/models/test_fee_code.py | 2 +- .../tests/unit/models/test_fee_schedule.py | 2 +- pay-api/tests/unit/models/test_filing_type.py | 2 +- pay-api/tests/unit/models/test_invoice.py | 2 +- .../unit/models/test_non_sufficient_funds.py | 2 +- pay-api/tests/unit/models/test_payment.py | 2 +- .../tests/unit/models/test_payment_account.py | 2 +- .../tests/unit/models/test_payment_method.py | 2 +- .../tests/unit/models/test_payment_system.py | 2 +- .../unit/models/test_payment_transaction.py | 2 +- pay-api/tests/unit/models/test_receipt.py | 2 +- .../tests/unit/models/test_routing_slip.py | 2 +- pay-api/tests/unit/models/test_status_code.py | 2 +- pay-api/tests/unit/services/__init__.py | 2 +- pay-api/tests/unit/services/test_auth.py | 2 +- .../tests/unit/services/test_bcol_service.py | 2 +- .../tests/unit/services/test_cfs_service.py | 2 +- pay-api/tests/unit/services/test_code.py | 2 +- pay-api/tests/unit/services/test_comment.py | 2 +- .../unit/services/test_distribution_code.py | 2 +- .../tests/unit/services/test_eft_service.py | 2 +- .../tests/unit/services/test_fee_schedule.py | 2 +- pay-api/tests/unit/services/test_flags.py | 2 +- pay-api/tests/unit/services/test_gcp_queue.py | 67 +- .../unit/services/test_hashing_service.py | 2 +- pay-api/tests/unit/services/test_invoice.py | 2 +- .../unit/services/test_invoice_reference.py | 2 +- .../services/test_non_sufficient_funds.py | 2 +- .../tests/unit/services/test_oauth_service.py | 2 +- .../tests/unit/services/test_pad_service.py | 2 +- pay-api/tests/unit/services/test_payment.py | 2 +- .../unit/services/test_payment_account.py | 2 +- .../unit/services/test_payment_line_item.py | 2 +- .../unit/services/test_payment_service.py | 2 +- .../services/test_payment_system_service.py | 2 +- .../unit/services/test_payment_transaction.py | 2 +- pay-api/tests/unit/services/test_receipt.py | 2 +- pay-api/tests/unit/services/test_refund.py | 2 +- .../services/test_routing_slip_service.py | 2 +- pay-api/tests/unit/services/test_statement.py | 2 +- .../unit/services/test_statement_settings.py | 2 +- .../tests/unit/services/test_wire_service.py | 2 +- pay-api/tests/unit/utils/__init__.py | 2 +- pay-api/tests/unit/utils/test_error.py | 2 +- pay-api/tests/unit/utils/test_logging.py | 2 +- pay-api/tests/unit/utils/test_util.py | 2 +- pay-api/tests/unit/utils/test_util_cors.py | 2 +- pay-api/tests/utilities/__init__.py | 2 +- pay-api/tests/utilities/base_test.py | 2 +- pay-api/tests/utilities/decorators.py | 2 +- pay-api/tests/utilities/schema_assertions.py | 2 +- pay-api/wsgi.py | 2 +- pay-queue/Makefile | 2 +- pay-queue/README.md | 4 +- pay-queue/app.py | 9 +- pay-queue/devops/vaults.gcp.env | 6 +- pay-queue/devops/vaults.json | 58 ++ pay-queue/logging.conf | 34 - pay-queue/poetry.lock | 399 +++++------ pay-queue/pyproject.toml | 7 +- pay-queue/scripts/verify_license_headers.sh | 4 +- pay-queue/setup.cfg | 9 +- pay-queue/setup.py | 4 +- pay-queue/src/pay_queue/__init__.py | 30 +- pay-queue/src/pay_queue/config.py | 27 +- pay-queue/src/pay_queue/enums.py | 2 +- pay-queue/src/pay_queue/external/__init__.py | 14 + pay-queue/src/pay_queue/external/gcp_auth.py | 20 +- pay-queue/src/pay_queue/minio.py | 2 +- pay-queue/src/pay_queue/resources/worker.py | 35 +- pay-queue/src/pay_queue/services/__init__.py | 5 - .../pay_queue/services/cgi_reconciliations.py | 57 +- .../services/eft/eft_reconciliation.py | 5 +- .../services/payment_reconciliations.py | 29 +- pay-queue/src/pay_queue/version.py | 2 +- pay-queue/tests/__init__.py | 2 +- pay-queue/tests/conftest.py | 24 +- pay-queue/tests/integration/__init__.py | 2 +- pay-queue/tests/integration/factory.py | 2 +- .../integration/test_cgi_reconciliations.py | 82 +-- .../integration/test_eft_reconciliation.py | 46 +- .../test_payment_reconciliations.py | 72 +- .../tests/integration/test_worker_queue.py | 4 +- pay-queue/tests/integration/utils.py | 11 +- report-api/requirements.txt | 2 +- 282 files changed, 1908 insertions(+), 1950 deletions(-) create mode 100644 .github/workflows/pay-queue-gcp-cd.yml delete mode 100644 jobs/ftp-poller/openshift/ftp-poller-build.json delete mode 100644 jobs/ftp-poller/openshift/ftp-poller-deploy.json create mode 100644 jobs/payment-jobs/devops/vaults.json delete mode 100644 pay-admin/requirements.txt create mode 100644 pay-api/devops/vaults.json create mode 100644 pay-queue/devops/vaults.json delete mode 100644 pay-queue/logging.conf create mode 100644 pay-queue/src/pay_queue/external/__init__.py diff --git a/.github/workflows/pay-queue-cd.yml b/.github/workflows/pay-queue-cd.yml index 73e2b0518..96124a711 100644 --- a/.github/workflows/pay-queue-cd.yml +++ b/.github/workflows/pay-queue-cd.yml @@ -4,30 +4,112 @@ on: push: branches: - main - - feature* paths: - "pay-queue/**" - "pay-api/src/pay_api/models/**" - "pay-api/src/pay_api/services/cfs_service.py" workflow_dispatch: inputs: - target: - description: "Deploy To" + environment: + description: "Environment (dev/test/prod)" required: true - type: choice - options: - - dev - - test - - sandbox - - prod + default: "dev" + +defaults: + run: + shell: bash + working-directory: ./pay-queue + +env: + APP_NAME: "pay-queue" + TAG_NAME: "dev" jobs: - pay-queue-cd: - uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main - with: - target: ${{ inputs.target }} - app_name: "pay-queue" - working_directory: "./pay-queue" - secrets: - WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file + pay-queue-cd-by-push: + runs-on: ubuntu-20.04 + + if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' + environment: + name: "dev" + + steps: + - uses: actions/checkout@v3 + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Payment Reconciliations Queue Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} + + pay-queue-cd-by-dispatch: + runs-on: ubuntu-20.04 + + if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' + environment: + name: "${{ github.event.inputs.environment }}" + + steps: + - uses: actions/checkout@v3 + - name: Set env by input + run: | + echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Payment Reconciliations Queue Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pay-queue-ci.yml b/.github/workflows/pay-queue-ci.yml index 567c4d23b..238da6343 100644 --- a/.github/workflows/pay-queue-ci.yml +++ b/.github/workflows/pay-queue-ci.yml @@ -14,7 +14,6 @@ defaults: run: shell: bash working-directory: ./pay-queue - jobs: setup-job: runs-on: ubuntu-20.04 diff --git a/.github/workflows/pay-queue-gcp-cd.yml b/.github/workflows/pay-queue-gcp-cd.yml new file mode 100644 index 000000000..73e2b0518 --- /dev/null +++ b/.github/workflows/pay-queue-gcp-cd.yml @@ -0,0 +1,33 @@ +name: Pay Queue CD + +on: + push: + branches: + - main + - feature* + paths: + - "pay-queue/**" + - "pay-api/src/pay_api/models/**" + - "pay-api/src/pay_api/services/cfs_service.py" + workflow_dispatch: + inputs: + target: + description: "Deploy To" + required: true + type: choice + options: + - dev + - test + - sandbox + - prod + +jobs: + pay-queue-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-queue" + working_directory: "./pay-queue" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index b3f42e947..ba3b879e1 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,6 @@ ACK.INBOX.F12022020202 pay-queue/ACK.* pay-queue/FEEDBACK.* jobs/notebook-report/data/ +test_eft_tdi17.txt +ACK.INBOX* +FEEDBACK.INBOX* diff --git a/bcol-api/requirements.txt b/bcol-api/requirements.txt index 44c6e1330..f9b2fad15 100644 --- a/bcol-api/requirements.txt +++ b/bcol-api/requirements.txt @@ -36,7 +36,7 @@ python-ldap==3.4.4 pytz==2024.1 requests-file==2.0.0 requests-toolbelt==1.0.0 -requests==2.31.0 +requests==2.32.2 rsa==4.9 sentry-sdk==1.41.0 six==1.16.0 diff --git a/jobs/ftp-poller/Makefile b/jobs/ftp-poller/Makefile index 46dd918d4..715e83f48 100644 --- a/jobs/ftp-poller/Makefile +++ b/jobs/ftp-poller/Makefile @@ -115,7 +115,7 @@ tag: push ## tag image ################################################################################# run: ## Run the project in local - . venv/bin/activate && python app.py + echo "unimplememted use docker" ################################################################################# # Self Documenting Commands # diff --git a/jobs/ftp-poller/config.py b/jobs/ftp-poller/config.py index 81a331765..12082de1e 100644 --- a/jobs/ftp-poller/config.py +++ b/jobs/ftp-poller/config.py @@ -136,11 +136,9 @@ class _Config(object): # pylint: disable=too-few-public-methods SENTRY_ENABLE = os.getenv('SENTRY_ENABLE', 'False') SENTRY_DSN = os.getenv('SENTRY_DSN', None) - # GCP PubSub - AUDIENCE = os.getenv('AUDIENCE', None) - GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) - PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) - FTP_POLLER_TOPIC = os.getenv('FTP_POLLER_TOPIC', None) + # PUB/SUB - PUB: ftp-poller-payment-reconciliation-dev + FTP_POLLER_TOPIC = os.getenv('FTP_POLLER_TOPIC', 'ftp-poller-payment-reconciliation-dev') + GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) TESTING = False DEBUG = True diff --git a/jobs/ftp-poller/devops/vaults.json b/jobs/ftp-poller/devops/vaults.json index 433ed9cbd..209734136 100644 --- a/jobs/ftp-poller/devops/vaults.json +++ b/jobs/ftp-poller/devops/vaults.json @@ -19,5 +19,12 @@ "application": [ "relationship-api" ] + }, + { + "vault": "gcp-queue", + "application": [ + "gtksf3", + "topics" + ] } ] diff --git a/jobs/ftp-poller/invoke_jobs.py b/jobs/ftp-poller/invoke_jobs.py index 5e18dcccb..875ff01e8 100755 --- a/jobs/ftp-poller/invoke_jobs.py +++ b/jobs/ftp-poller/invoke_jobs.py @@ -21,6 +21,7 @@ import sentry_sdk from flask import Flask from sentry_sdk.integrations.flask import FlaskIntegration +from pay_api.services.gcp_queue import queue import config from utils.logger import setup_logging @@ -43,7 +44,8 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): dsn=app.config.get('SENTRY_DSN'), integrations=[FlaskIntegration()] ) - app.logger.info(f'<<<< Starting Ftp Poller Job >>>>') + app.logger.info('<<<< Starting Ftp Poller Job >>>>') + queue.init_app(app) ma.init_app(app) register_shellcontext(app) diff --git a/jobs/ftp-poller/openshift/ftp-poller-build.json b/jobs/ftp-poller/openshift/ftp-poller-build.json deleted file mode 100644 index 8713416c5..000000000 --- a/jobs/ftp-poller/openshift/ftp-poller-build.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Build template for a FTP Poller job.", - "tags": "flask", - "iconClass": "icon-python" - }, - "name": "${NAME}-build-template" - }, - "objects": [ - { - "kind": "ImageStream", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}" - } - }, - { - "kind": "BuildConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}", - "labels": { - "app": "${NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-build" - } - }, - "spec": { - "source": { - "type": "Git", - "git": { - "uri": "${GIT_REPO_URL}", - "ref": "${GIT_REF}" - }, - "contextDir": "${SOURCE_CONTEXT_DIR}" - }, - "strategy": { - "type": "Docker", - "dockerStrategy": { - "dockerfilePath": "${DOCKER_FILE_PATH}" - } - }, - "output": { - "to": { - "kind": "ImageStreamTag", - "name": "${NAME}:${OUTPUT_IMAGE_TAG}" - } - }, - "triggers": [ - { - "type": "ConfigChange" - } - ] - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", - "required": true, - "value": "ftp-poller" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "GIT_REPO_URL", - "displayName": "Git Repo URL", - "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", - "required": true, - "value": "https://github.com/bcgov/sbc-pay.git" - }, - { - "name": "GIT_REF", - "displayName": "Git Reference", - "description": "The git reference or branch.", - "required": true, - "value": "development" - }, - { - "name": "SOURCE_CONTEXT_DIR", - "displayName": "Source Context Directory", - "description": "The source context directory.", - "required": true, - "value": "jobs/ftp-poller" - }, - { - "name": "SOURCE_IMAGE_KIND", - "displayName": "Source Image Kind", - "required": true, - "description": "The 'kind' (type) of the source image; typically ImageStreamTag, or DockerImage.", - "value": "ImageStreamTag" - }, - { - "name": "SOURCE_IMAGE_NAME_SPACE", - "displayName": "Source Image Name Space", - "required": true, - "description": "The name space of the source image.", - "value": "d7eovc-tools" - }, - { - "name": "SOURCE_IMAGE_NAME", - "displayName": "Source Image Name", - "required": true, - "description": "The name of the source image.", - "value": "python" - }, - { - "name": "SOURCE_IMAGE_TAG", - "displayName": "Source Image Tag", - "required": true, - "description": "The tag of the source image.", - "value": "3.7" - }, - { - "name": "OUTPUT_IMAGE_TAG", - "displayName": "Output Image Tag", - "description": "The tag given to the built image.", - "required": true, - "value": "latest" - }, - { - "name": "DOCKER_FILE_PATH", - "displayName": "Docker File Path", - "description": "The path to the docker file defining the build.", - "required": false, - "value": "Dockerfile" - } - ] -} diff --git a/jobs/ftp-poller/openshift/ftp-poller-deploy.json b/jobs/ftp-poller/openshift/ftp-poller-deploy.json deleted file mode 100644 index 87dd37b9e..000000000 --- a/jobs/ftp-poller/openshift/ftp-poller-deploy.json +++ /dev/null @@ -1,417 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "v1", - "metadata": { - "annotations": { - "description": "Deployment template for a ftp poller job.", - "tags": "${NAME}-${TAG_NAME}" - }, - "name": "${NAME}-${TAG_NAME}-deploy" - }, - "objects": [ - { - "kind": "DeploymentConfig", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "strategy": { - "type": "Rolling", - "rollingParams": { - "updatePeriodSeconds": 1, - "intervalSeconds": 1, - "timeoutSeconds": 600, - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "${NAME}-${TAG_NAME}" - ], - "from": { - "kind": "ImageStreamTag", - "namespace": "${IMAGE_NAMESPACE}", - "name": "${NAME}:${TAG_NAME}" - } - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": 1, - "test": false, - "selector": { - "app": "${NAME}-${TAG_NAME}", - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "template": { - "metadata": { - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "deploymentconfig": "${NAME}-${TAG_NAME}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "volumes": [ - { - "name": "cron-config", - "configMap": { - "name": "${NAME}-${TAG_NAME}-cron-configuration", - "defaultMode": 420 - } - }, - { - "name": "sftp-private-key", - "configMap": { - "name": "${NAME}-${TAG_NAME}-sftp-configuration", - "defaultMode": 420 - } - } - ], - "containers": [ - { - "name": "${NAME}-${TAG_NAME}", - "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", - "ports": [ - { - "containerPort": 8080, - "protocol": "TCP" - } - ], - "volumeMounts": [ - { - "name": "cron-config", - "readOnly": true, - "mountPath": "/ftp-poller/cron/" - }, - { - "name": "sftp-private-key", - "readOnly": true, - "mountPath": "/ftp-poller/key/" - } - ], - "env": [ - { - "name": "DATABASE_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_USER" - } - } - }, - { - "name": "DATABASE_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_PASSWORD" - } - } - }, - { - "name": "DATABASE_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_NAME" - } - } - }, - { - "name": "DATABASE_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_HOST" - } - } - }, - { - "name": "DATABASE_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_PORT" - } - } - }, - { - "name": "DATABASE_TEST_USERNAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_USER" - } - } - }, - { - "name": "DATABASE_TEST_PASSWORD", - "valueFrom": { - "secretKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-secret", - "key": "DATABASE_TEST_PASSWORD" - } - } - }, - { - "name": "DATABASE_TEST_NAME", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_NAME" - } - } - }, - { - "name": "DATABASE_TEST_HOST", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_HOST" - } - } - }, - { - "name": "DATABASE_TEST_PORT", - "valueFrom": { - "configMapKeyRef": { - "name": "${DATABASE_NAME}-${TAG_NAME}-config", - "key": "DATABASE_TEST_PORT" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_REF_NUMBER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_REF_NUMBER" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_API_KEY", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_API_KEY" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_BASE_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_BASE_URL" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_ID" - } - } - }, - { - "name": "PAYBC_DIRECT_PAY_CLIENT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "PAYBC_DIRECT_PAY_CLIENT_SECRET" - } - } - }, - { - "name": "NOTIFY_API_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "NOTIFY_API_URL" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_ID" - } - } - }, - { - "name": "KEYCLOAK_SERVICE_ACCOUNT_SECRET", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "KEYCLOAK_SERVICE_ACCOUNT_SECRET" - } - } - }, - { - "name": "JWT_OIDC_ISSUER", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "JWT_OIDC_ISSUER" - } - } - }, - { - "name": "AUTH_WEB_PAY_TRANSACTION_URL", - "valueFrom": { - "secretKeyRef": { - "name": "pay-api-${TAG_NAME}-secret", - "key": "AUTH_WEB_PAY_TRANSACTION_URL" - } - } - } - ], - "resources": { - "requests": { - "cpu": "${CPU_REQUEST}", - "memory": "${MEMORY_REQUEST}" - }, - "limits": { - "cpu": "${CPU_LIMIT}", - "memory": "${MEMORY_LIMIT}" - } - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - } - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${NAME}-${TAG_NAME}", - "creationTimestamp": null, - "labels": { - "app": "${NAME}-${TAG_NAME}", - "app-group": "${APP_GROUP}", - "template": "${NAME}-deploy" - } - }, - "spec": { - "ports": [ - { - "name": "8080-tcp", - "protocol": "TCP", - "port": 8080, - "targetPort": 8080 - } - ], - "selector": { - "deploymentconfig": "${NAME}-${TAG_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - } - ], - "parameters": [ - { - "name": "NAME", - "displayName": "Name", - "description": "The name assigned to all of the OpenShift resources associated to the server instance.", - "required": true, - "value": "ftp-poller" - }, - { - "name": "APP_GROUP", - "displayName": "App Group", - "description": "The name assigned to all of the deployments in this project.", - "required": true, - "value": "sbc-pay" - }, - { - "name": "IMAGE_NAMESPACE", - "displayName": "Image Namespace", - "required": true, - "description": "The namespace of the OpenShift project containing the imagestream for the application.", - "value": "l4ygcl-tools" - }, - { - "name": "TAG_NAME", - "displayName": "Environment TAG name", - "description": "The TAG name for this environment, e.g., dev, test, prod", - "required": true, - "value": "dev" - }, - { - "name": "DATABASE_NAME", - "displayName": "Database App Name", - "description": "A valid database app name used by the service.", - "required": true, - "value": "postgresql" - }, - { - "name": "CPU_REQUEST", - "displayName": "Resources CPU Request", - "description": "The resources CPU request (in cores) for this build.", - "required": true, - "value": "100m" - }, - { - "name": "CPU_LIMIT", - "displayName": "Resources CPU Limit", - "description": "The resources CPU limit (in cores) for this build.", - "required": true, - "value": "750m" - }, - { - "name": "MEMORY_REQUEST", - "displayName": "Resources Memory Request", - "description": "The resources Memory request (in Mi, Gi, etc) for this build.", - "required": true, - "value": "100Mi" - }, - { - "name": "MEMORY_LIMIT", - "displayName": "Resources Memory Limit", - "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", - "required": true, - "value": "2Gi" - }, - { - "name": "REPLICAS", - "displayName": "The number of replicas to run", - "description": "The number of replicas to run in this environment.", - "required": true, - "value": "1" - } - ] -} \ No newline at end of file diff --git a/jobs/ftp-poller/poetry.lock b/jobs/ftp-poller/poetry.lock index c49f113da..befc94f7c 100644 --- a/jobs/ftp-poller/poetry.lock +++ b/jobs/ftp-poller/poetry.lock @@ -90,15 +90,26 @@ cffi = ">=1.0.1" dev = ["cogapp", "pre-commit", "pytest", "wheel"] tests = ["pytest"] +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + [[package]] name = "astroid" -version = "3.1.0" +version = "3.2.2" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, ] [[package]] @@ -122,52 +133,52 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.0.4" +version = "2.2.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, - {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, + {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, + {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, ] [package.dependencies] -pycodestyle = ">=2.10.0" +pycodestyle = ">=2.11.0" [[package]] name = "bcrypt" -version = "4.1.2" +version = "4.1.3" description = "Modern password hashing for your software and your servers" optional = false python-versions = ">=3.7" files = [ - {file = "bcrypt-4.1.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e"}, - {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1"}, - {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326"}, - {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c"}, - {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966"}, - {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2"}, - {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c"}, - {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5"}, - {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0"}, - {file = "bcrypt-4.1.2-cp37-abi3-win32.whl", hash = "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"}, - {file = "bcrypt-4.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551"}, - {file = "bcrypt-4.1.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63"}, - {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483"}, - {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc"}, - {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7"}, - {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb"}, - {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1"}, - {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4"}, - {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c"}, - {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a"}, - {file = "bcrypt-4.1.2-cp39-abi3-win32.whl", hash = "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f"}, - {file = "bcrypt-4.1.2-cp39-abi3-win_amd64.whl", hash = "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42"}, - {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946"}, - {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d"}, - {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab"}, - {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb"}, - {file = "bcrypt-4.1.2.tar.gz", hash = "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258"}, + {file = "bcrypt-4.1.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:48429c83292b57bf4af6ab75809f8f4daf52aa5d480632e53707805cc1ce9b74"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8bea4c152b91fd8319fef4c6a790da5c07840421c2b785084989bf8bbb7455"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d3b317050a9a711a5c7214bf04e28333cf528e0ed0ec9a4e55ba628d0f07c1a"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:094fd31e08c2b102a14880ee5b3d09913ecf334cd604af27e1013c76831f7b05"}, + {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4fb253d65da30d9269e0a6f4b0de32bd657a0208a6f4e43d3e645774fb5457f3"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:193bb49eeeb9c1e2db9ba65d09dc6384edd5608d9d672b4125e9320af9153a15"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:8cbb119267068c2581ae38790e0d1fbae65d0725247a930fc9900c285d95725d"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6cac78a8d42f9d120b3987f82252bdbeb7e6e900a5e1ba37f6be6fe4e3848286"}, + {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01746eb2c4299dd0ae1670234bf77704f581dd72cc180f444bfe74eb80495b64"}, + {file = "bcrypt-4.1.3-cp37-abi3-win32.whl", hash = "sha256:037c5bf7c196a63dcce75545c8874610c600809d5d82c305dd327cd4969995bf"}, + {file = "bcrypt-4.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:8a893d192dfb7c8e883c4576813bf18bb9d59e2cfd88b68b725990f033f1b978"}, + {file = "bcrypt-4.1.3-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d4cf6ef1525f79255ef048b3489602868c47aea61f375377f0d00514fe4a78c"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5698ce5292a4e4b9e5861f7e53b1d89242ad39d54c3da451a93cac17b61921a"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec3c2e1ca3e5c4b9edb94290b356d082b721f3f50758bce7cce11d8a7c89ce84"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3a5be252fef513363fe281bafc596c31b552cf81d04c5085bc5dac29670faa08"}, + {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5f7cd3399fbc4ec290378b541b0cf3d4398e4737a65d0f938c7c0f9d5e686611"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:c4c8d9b3e97209dd7111bf726e79f638ad9224b4691d1c7cfefa571a09b1b2d6"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:31adb9cbb8737a581a843e13df22ffb7c84638342de3708a98d5c986770f2834"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:551b320396e1d05e49cc18dd77d970accd52b322441628aca04801bbd1d52a73"}, + {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6717543d2c110a155e6821ce5670c1f512f602eabb77dba95717ca76af79867d"}, + {file = "bcrypt-4.1.3-cp39-abi3-win32.whl", hash = "sha256:6004f5229b50f8493c49232b8e75726b568535fd300e5039e255d919fc3a07f2"}, + {file = "bcrypt-4.1.3-cp39-abi3-win_amd64.whl", hash = "sha256:2505b54afb074627111b5a8dc9b6ae69d0f01fea65c2fcaea403448c503d3991"}, + {file = "bcrypt-4.1.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:cb9c707c10bddaf9e5ba7cdb769f3e889e60b7d4fea22834b261f51ca2b89fed"}, + {file = "bcrypt-4.1.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9f8ea645eb94fb6e7bea0cf4ba121c07a3a182ac52876493870033141aa687bc"}, + {file = "bcrypt-4.1.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f44a97780677e7ac0ca393bd7982b19dbbd8d7228c1afe10b128fd9550eef5f1"}, + {file = "bcrypt-4.1.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d84702adb8f2798d813b17d8187d27076cca3cd52fe3686bb07a9083930ce650"}, + {file = "bcrypt-4.1.3.tar.gz", hash = "sha256:2ee15dd749f5952fe3f0430d0ff6b74082e159c50332a1413d51b5689cf06623"}, ] [package.extras] @@ -431,63 +442,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.3" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, - {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, - {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, - {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, - {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, - {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, - {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, - {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, - {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, - {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, - {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, - {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, - {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, - {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.extras] @@ -732,13 +743,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.1.0" +version = "2.3.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, - {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, ] [package.dependencies] @@ -760,25 +771,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask_jwt_oidc" -version = "0.3.0" -description = "Flask JWT OIDC" +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" optional = false -python-versions = "*" +python-versions = "^3.9" files = [] develop = false [package.dependencies] -cachelib = "*" -flask = "*" -python-jose = "*" -six = "*" +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" [package.source] type = "git" -url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" [[package]] name = "flask-marshmallow" @@ -900,15 +911,37 @@ files = [ flask = ">=2.2.5" sqlalchemy = ">=2.0.16" +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + [[package]] name = "google-api-core" -version = "2.17.1" +version = "2.19.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, - {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, ] [package.dependencies] @@ -916,6 +949,7 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -926,13 +960,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.28.1" +version = "2.29.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, - {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, ] [package.dependencies] @@ -949,13 +983,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.20.0" +version = "2.21.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, - {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, ] [package.dependencies] @@ -1077,84 +1111,76 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.62.1" +version = "1.64.0" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, - {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, - {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, - {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, - {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, - {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, - {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, - {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, - {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, - {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, - {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, - {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, - {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, - {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, - {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, - {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, - {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, - {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, - {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, - {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, - {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, - {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, - {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, - {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.62.1)"] +protobuf = ["grpcio-tools (>=1.64.0)"] [[package]] name = "grpcio-status" -version = "1.62.1" +version = "1.62.2" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, - {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.1" +grpcio = ">=1.62.2" protobuf = ">=4.21.6" [[package]] @@ -1309,28 +1335,26 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "9.2.2" +version = "8.2.1" description = "LaunchDarkly SDK for Python" optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, - {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" -launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.26.0,<3" +urllib3 = ">=1.22.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] -test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1486,13 +1510,13 @@ files = [ [[package]] name = "minio" -version = "7.2.5" +version = "7.2.7" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = "*" files = [ - {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, - {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, + {file = "minio-7.2.7-py3-none-any.whl", hash = "sha256:59d1f255d852fe7104018db75b3bebbd987e538690e680f7c5de835e422de837"}, + {file = "minio-7.2.7.tar.gz", hash = "sha256:473d5d53d79f340f3cd632054d0c82d2f93177ce1af2eac34a235bea55708d98"}, ] [package.dependencies] @@ -1548,52 +1572,91 @@ gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] invoke = ["invoke (>=2.0)"] [[package]] -name = "pay_api" -version = "0.0.0" -description = "A short description of the project" +name = "pay-api" +version = "0.1.0" +description = "" optional = false -python-versions = ">=3.12" +python-versions = "^3.12" files = [] develop = false [package.dependencies] -cattrs = "*" -croniter = "*" -cryptography = "*" -dpath = "*" -Flask = "*" -Flask-Caching = "*" -Flask-Cors = "*" -flask-jwt-oidc = "*" -flask-marshmallow = "*" -Flask-Migrate = "*" -Flask-Moment = "*" -Flask-Script = "*" -Flask-SQLAlchemy = "*" -google-auth = "2.28.1" -google-cloud-pubsub = "2.20.0" -gunicorn = "*" +alembic = "1.13.1" +attrs = "23.2.0" +blinker = "1.7.0" +cachelib = "0.9.0" +cachetools = "5.3.3" +cattrs = "23.2.3" +certifi = "2024.2.2" +cffi = "1.16.0" +charset-normalizer = "3.3.2" +click = "8.1.7" +croniter = "2.0.2" +cryptography = "42.0.5" +dpath = "2.1.6" +ecdsa = "0.18.0" +expiringdict = "1.2.2" +flask = "3.0.2" +flask-caching = "2.3.0" +flask-cors = "4.0.0" +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} +flask-marshmallow = "1.2.0" +flask-migrate = "4.0.7" +flask-moment = "1.0.5" +flask-script = "2.0.6" +flask-sqlalchemy = "3.1.1" +gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} +greenlet = "3.0.3" +gunicorn = "21.2.0" holidays = "0.37" -itsdangerous = "*" -jaeger-client = "*" -Jinja2 = "*" +idna = "3.6" +itsdangerous = "2.1.2" +jaeger-client = "4.8.0" +jinja2 = "3.1.3" jsonschema = "4.17.3" -launchdarkly-server-sdk = "*" -marshmallow-sqlalchemy = "*" -psycopg2-binary = "*" -pyhumps = "*" -python-dotenv = "*" -requests = "*" -sentry-sdk = {version = "*", extras = ["flask"]} -sqlalchemy = "*" -sqlalchemy_utils = "*" -Werkzeug = "*" +launchdarkly-eventsource = "1.1.1" +launchdarkly-server-sdk = "8.2.1" +mako = "1.3.2" +markupsafe = "2.1.5" +marshmallow = "3.21.1" +marshmallow-sqlalchemy = "1.0.0" +opentracing = "2.4.0" +packaging = "24.0" +pg8000 = "^1.30.5" +proto-plus = "1.23.0" +protobuf = "4.25.3" +psycopg2-binary = "2.9.9" +pyasn1 = "0.5.1" +pyasn1-modules = "0.3.0" +pycparser = "2.21" +pyhumps = "3.8.0" +pyrfc3339 = "1.1" +pyrsistent = "0.20.0" +python-dateutil = "2.9.0.post0" +python-dotenv = "1.0.1" +python-jose = "3.3.0" +pytz = "2024.1" +requests = "2.31.0" +rsa = "4.9" +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +semver = "3.0.2" +sentry-sdk = "1.41.0" +six = "1.16.0" +sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} +sqlalchemy = "2.0.28" +sqlalchemy-utils = "0.41.1" +threadloop = "1.0.2" +thrift = "0.16.0" +tornado = "6.4" +typing-extensions = "4.10.0" +urllib3 = "2.2.1" +werkzeug = "3.0.1" [package.source] type = "git" url = "https://github.com/seeker25/sbc-pay.git" -reference = "18263" -resolved_reference = "fe42bf81c86c77946a1df238f69f12ad6b83d804" +reference = "sync-python-to-main" +resolved_reference = "522ed4997b03f8bb5d88864e739815548aa606be" subdirectory = "pay-api" [[package]] @@ -1610,30 +1673,46 @@ files = [ [package.dependencies] flake8 = ">=5.0.0" +[[package]] +name = "pg8000" +version = "1.31.2" +description = "PostgreSQL interface library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, + {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.2" +scramp = ">=1.4.5" + [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -1904,17 +1983,17 @@ files = [ [[package]] name = "pylint" -version = "3.1.0" +version = "3.2.2" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, ] [package.dependencies] -astroid = ">=3.1.0,<=3.2.0-dev0" +astroid = ">=3.2.2,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -2049,33 +2128,33 @@ paramiko = ">=1.17" [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" -version = "0.23.5.post1" +version = "0.23.7" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, - {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, + {file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"}, + {file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"}, ] [package.dependencies] @@ -2105,17 +2184,17 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.12.0" +version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, - {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, ] [package.dependencies] -pytest = ">=5.0" +pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] @@ -2235,9 +2314,23 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" subdirectory = "python" +[[package]] +name = "scramp" +version = "1.4.5" +description = "An implementation of the SCRAM protocol." +optional = false +python-versions = ">=3.8" +files = [ + {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, + {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, +] + +[package.dependencies] +asn1crypto = ">=1.5.1" + [[package]] name = "semver" version = "3.0.2" @@ -2251,20 +2344,17 @@ files = [ [[package]] name = "sentry-sdk" -version = "1.42.0" +version = "1.41.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, - {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, + {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, + {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, ] [package.dependencies] -blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} certifi = "*" -flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} -markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} [package.extras] @@ -2284,7 +2374,6 @@ grpcio = ["grpcio (>=1.21.1)"] httpx = ["httpx (>=0.16.0)"] huey = ["huey (>=2)"] loguru = ["loguru (>=0.5)"] -openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] opentelemetry = ["opentelemetry-distro (>=0.35b0)"] opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] pure-eval = ["asttokens", "executing", "pure-eval"] @@ -2300,19 +2389,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "69.2.0" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simple-cloudevent" @@ -2354,6 +2442,22 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] +[[package]] +name = "sql-versioning" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "feature-legal-name" +resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" +subdirectory = "python/common/sql-versioning" + [[package]] name = "sqlalchemy" version = "2.0.28" @@ -2513,13 +2617,13 @@ twisted = ["twisted"] [[package]] name = "tomlkit" -version = "0.12.4" +version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] @@ -2590,4 +2694,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "f048f3fd232a6fb3c61c8d1f8651e55f81e6c283799dfde0ed451cea62a6e3a6" +content-hash = "3aeb528030f868f3ab60ec2d31aab0dd38f09872fe7ba62082c45715aaeed206" diff --git a/jobs/ftp-poller/pyproject.toml b/jobs/ftp-poller/pyproject.toml index cf312a280..34ecaccca 100644 --- a/jobs/ftp-poller/pyproject.toml +++ b/jobs/ftp-poller/pyproject.toml @@ -21,11 +21,9 @@ jaeger-client = "^4.8.0" itsdangerous = "^2.1.2" jinja2 = "^3.1.3" protobuf = "4.25.3" -launchdarkly-server-sdk = "^9.2.2" +launchdarkly-server-sdk = "^8.2.1" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} -pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "18263", subdirectory = "pay-api"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "sync-python-to-main", subdirectory = "pay-api"} [tool.poetry.group.dev.dependencies] diff --git a/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py b/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py index 7542a4d84..ba79e2347 100644 --- a/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py +++ b/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py @@ -16,8 +16,8 @@ from flask import current_app from paramiko.sftp_attr import SFTPAttributes +from sbc_common_components.utils.enums import QueueMessageTypes -from pay_api.utils.enums import MessageType from services.sftp import SFTPService from utils import utils @@ -48,12 +48,12 @@ def poll_ftp(cls): f'Skipping directory {file_name}.') continue if cls._is_ack_file(file_name): - utils.publish_to_queue([file_name], MessageType.CGI_ACK_RECEIVED.value) + utils.publish_to_queue([file_name], QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) cls._move_file_to_backup(sftp_client, [file_name]) elif cls._is_feedback_file(file_name): bucket_name = current_app.config.get('MINIO_CGI_BUCKET_NAME') utils.upload_to_minio(file, file_full_name, sftp_client, bucket_name) - utils.publish_to_queue([file_name], MessageType.CGI_FEEDBACK_RECEIVED.value, + utils.publish_to_queue([file_name], QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value, location=bucket_name) cls._move_file_to_backup(sftp_client, [file_name]) elif cls._is_a_trigger_file(file_name): diff --git a/jobs/ftp-poller/tasks/eft_poller_ftp.py b/jobs/ftp-poller/tasks/eft_poller_ftp.py index 10f59a4f4..ff6b3dc52 100644 --- a/jobs/ftp-poller/tasks/eft_poller_ftp.py +++ b/jobs/ftp-poller/tasks/eft_poller_ftp.py @@ -17,7 +17,7 @@ from flask import current_app from paramiko.sftp_attr import SFTPAttributes -from pay_api.utils.enums import MessageType +from sbc_common_components.utils.enums import QueueMessageTypes from services.sftp import SFTPService from utils.utils import publish_to_queue, upload_to_minio @@ -66,7 +66,7 @@ def _post_process(cls, sftp_client, payment_file_list: List[str]): 2.Send a message to queue """ cls._move_file_to_backup(sftp_client, payment_file_list) - publish_to_queue(payment_file_list, MessageType.EFT_FILE_UPLOADED.value, + publish_to_queue(payment_file_list, QueueMessageTypes.EFT_FILE_UPLOADED.value, location=current_app.config.get('MINIO_EFT_BUCKET_NAME')) @classmethod diff --git a/jobs/ftp-poller/tests/jobs/test_sftp.py b/jobs/ftp-poller/tests/jobs/test_sftp.py index d88470a8d..07dc79d5b 100644 --- a/jobs/ftp-poller/tests/jobs/test_sftp.py +++ b/jobs/ftp-poller/tests/jobs/test_sftp.py @@ -19,6 +19,8 @@ import pytest from flask import current_app +from sbc_common_components.utils.enums import QueueMessageTypes + from services.sftp import SFTPService from utils.utils import publish_to_queue @@ -38,7 +40,14 @@ def test_poll_ftp_task(): assert len(files) == 1, 'Files exist in FTP folder' -@pytest.mark.skip(reason='leave this to manually verify pubsub connection; needs env vars') -def test_queue_message(): +@pytest.mark.skip(reason='leave this to manually verify pubsub connection;' + 'needs env vars, disable def mock_queue_publish(monkeypatch):') +def test_queue_message(session): # pylint:disable=unused-argument """Test publishing to topic.""" - publish_to_queue(['file1.csv']) + file_name = 'file1.csv' + publish_to_queue([file_name]) + publish_to_queue([file_name], QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + publish_to_queue([file_name], QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value, + location=current_app.config.get('MINIO_CGI_BUCKET_NAME')) + publish_to_queue([file_name], QueueMessageTypes.EFT_FILE_UPLOADED.value, + location=current_app.config.get('MINIO_EFT_BUCKET_NAME')) diff --git a/jobs/ftp-poller/utils/utils.py b/jobs/ftp-poller/utils/utils.py index aa452f5c4..afc22828a 100644 --- a/jobs/ftp-poller/utils/utils.py +++ b/jobs/ftp-poller/utils/utils.py @@ -12,18 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. """Service to manage PAYBC services.""" +from time import time from typing import List from flask import current_app from paramiko import SFTPFile from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage -from pay_api.utils.enums import MessageType, QueueSources +from pay_api.utils.enums import QueueSources +from sbc_common_components.utils.enums import QueueMessageTypes from utils.minio import put_object -def publish_to_queue(payment_file_list: List[str], message_type=MessageType.CAS_UPLOADED.value, location: str = ''): +def publish_to_queue(payment_file_list: List[str], message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value, + location: str = ''): """Publish message to the Queue, saying file has been uploaded. Using the event spec.""" queue_data = { 'fileSource': 'MINIO', @@ -38,11 +41,11 @@ def publish_to_queue(payment_file_list: List[str], message_type=MessageType.CAS_ source=QueueSources.FTP_POLLER.value, message_type=message_type, payload=queue_data, - topic=current_app.config.get('FTP_POLLER_TOPIC') + topic=current_app.config.get('FTP_POLLER_TOPIC'), + ordering_key=str(time()) ) ) except Exception as e: # NOQA # pylint: disable=broad-except - current_app.logger.error(e) current_app.logger.warning( f'Notification to Queue failed for the file {file_name}', e) @@ -57,7 +60,6 @@ def upload_to_minio(file, file_full_name, sftp_client, bucket_name): value_as_bytes = f.read() try: put_object(value_as_bytes, file.filename, bucket_name, file.st_size, ) - except Exception as e: # NOQA # pylint: disable=broad-except - current_app.logger.error(e) + except Exception: # NOQA # pylint: disable=broad-except current_app.logger.error(f'upload to minio failed for the file: {file_full_name}') raise diff --git a/jobs/notebook-report/requirements.txt b/jobs/notebook-report/requirements.txt index bb27f8d33..4a095802e 100644 --- a/jobs/notebook-report/requirements.txt +++ b/jobs/notebook-report/requirements.txt @@ -30,5 +30,5 @@ marshmallow==2.20.5 Werkzeug==0.16.1 certifi==2023.7.22 urllib3==1.26.17 -idna==2.9 +idna==3.7 pylint diff --git a/jobs/notebook-report/requirements/prod.txt b/jobs/notebook-report/requirements/prod.txt index 1f8cbae23..6d869f683 100644 --- a/jobs/notebook-report/requirements/prod.txt +++ b/jobs/notebook-report/requirements/prod.txt @@ -25,9 +25,9 @@ pyrsistent==0.16.0 Flask==1.1.2 Click==7.1.2 python-dotenv==0.13.0 -requests==2.31.0 +requests==2.32.2 marshmallow==2.20.5 Werkzeug==0.16.1 certifi==2023.7.22 urllib3==1.26.17 -idna==2.9 +idna==3.7 diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index cede16606..a18c89791 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -116,6 +116,11 @@ class _Config(object): # pylint: disable=too-few-public-methods AUTH_WEB_STATEMENT_URL = os.getenv('AUTH_WEB_STATEMENT_URL', 'account/orgId/settings/statements') REGISTRIES_LOGO_IMAGE_NAME = os.getenv('REGISTRIES_LOGO_IMAGE_NAME', 'bc_logo_for_email.png') + # PUB/SUB- PUB: account-mailer-dev + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') + GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) + + CFS_ACCOUNT_DESCRIPTION = os.getenv('CFS_ACCOUNT_DESCRIPTION', 'BCR') CFS_INVOICE_PREFIX = os.getenv('CFS_INVOICE_PREFIX', 'REG') CFS_STOP_PAD_ACCOUNT_CREATION = os.getenv('CFS_STOP_PAD_ACCOUNT_CREATION', 'false').lower() == 'true' @@ -243,6 +248,7 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods CGI_SFTP_PORT = 2222 CGI_SFTP_DIRECTORY = '/data/' CGI_SFTP_HOST = 'localhost' + GCP_AUTH_KEY = None class ProdConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/jobs/payment-jobs/devops/vaults.json b/jobs/payment-jobs/devops/vaults.json new file mode 100644 index 000000000..420b0bbe9 --- /dev/null +++ b/jobs/payment-jobs/devops/vaults.json @@ -0,0 +1,70 @@ +[ + { + "vault": "shared", + "application": [ + "api-endpoints", + "encryption-key" + ] + }, + { + "vault": "keycloak", + "application": [ + "jwt-base", + "sbc-auth-admin" + ] + }, + { + "vault": "payment-external-services", + "application": [ + "paybc", + "cfs" + ] + }, + { + "vault": "relationship", + "application": [ + "postgres-pay", + "pay-api", + "jwt", + "payment-jobs", + "ftp-poller" + ] + }, + { + "vault": "sentry", + "application": [ + "relationship-api" + ] + }, + { + "vault": "minio", + "application": [ + "payment-jobs" + ] + }, + { + "vault": "minio", + "application": [ + "base" + ] + }, + { + "vault": "launchdarkly", + "application": [ + "pay" + ] + }, + { + "vault": "entity", + "application": [ + "colin-api" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "gtksf3", + "topics" + ] + } +] diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index ebbb7d567..7636770a6 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -30,6 +30,7 @@ from utils.logger import setup_logging from pay_api.services import Flags +from pay_api.services.gcp_queue import queue setup_logging(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first @@ -51,6 +52,7 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production'), job_name='unk release=f'payment-jobs-{job_name}@-', ) app.logger.info('<<<< Starting Payment Jobs >>>>') + queue.init_app(app) db.init_app(app) if init_oracle: oracle_db.init_app(app) diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock index ff1195ca7..0a123832e 100644 --- a/jobs/payment-jobs/poetry.lock +++ b/jobs/payment-jobs/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "alembic" @@ -784,13 +784,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.1.0" +version = "2.3.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, - {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, ] [package.dependencies] @@ -812,25 +812,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask_jwt_oidc" -version = "0.3.0" -description = "Flask JWT OIDC" +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" optional = false -python-versions = "*" +python-versions = "^3.9" files = [] develop = false [package.dependencies] -cachelib = "*" -flask = "*" -python-jose = "*" -six = "*" +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" [package.source] type = "git" -url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" [[package]] name = "flask-marshmallow" @@ -943,6 +943,28 @@ files = [ [package.dependencies] python-dateutil = ">=2.7" +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + [[package]] name = "google-api-core" version = "2.17.1" @@ -969,13 +991,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.28.1" +version = "2.29.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, - {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, ] [package.dependencies] @@ -992,13 +1014,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.20.0" +version = "2.21.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, - {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, ] [package.dependencies] @@ -1352,28 +1374,26 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "9.2.2" +version = "8.2.1" description = "LaunchDarkly SDK for Python" optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, - {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" -launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.26.0,<3" +urllib3 = ">=1.22.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] -test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1627,22 +1647,16 @@ dpath = "2.1.6" ecdsa = "0.18.0" expiringdict = "1.2.2" flask = "3.0.2" -flask-caching = "2.1.0" +flask-caching = "2.3.0" flask-cors = "4.0.0" -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} flask-marshmallow = "1.2.0" flask-migrate = "4.0.7" flask-moment = "1.0.5" flask-script = "2.0.6" flask-sqlalchemy = "3.1.1" -google-api-core = "2.17.1" -google-auth = "2.28.1" -google-cloud-pubsub = "2.20.0" -googleapis-common-protos = "1.63.0" +gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} greenlet = "3.0.3" -grpc-google-iam-v1 = "0.13.0" -grpcio = "1.62.1" -grpcio-status = "1.62.1" gunicorn = "21.2.0" holidays = "0.37" idna = "3.6" @@ -1651,7 +1665,7 @@ jaeger-client = "4.8.0" jinja2 = "3.1.3" jsonschema = "4.17.3" launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "9.2.2" +launchdarkly-server-sdk = "8.2.1" mako = "1.3.2" markupsafe = "2.1.5" marshmallow = "3.21.1" @@ -1677,7 +1691,6 @@ rsa = "4.9" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} semver = "3.0.2" sentry-sdk = "1.41.0" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} six = "1.16.0" sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} sqlalchemy = "2.0.28" @@ -1691,9 +1704,9 @@ werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/bcgov/sbc-pay.git" -reference = "feature-queue-python-upgrade" -resolved_reference = "ba20cecf7e65065fa22dbfedb0f0bb5c1ee7ec94" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "sync-python-to-main" +resolved_reference = "3cc0024430f298a460f4780bf8a55f6c84e1ec8e" subdirectory = "pay-api" [[package]] @@ -2350,7 +2363,7 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +resolved_reference = "22978d810dc4e85c51c3129936686b0a17124e64" subdirectory = "python" [[package]] @@ -2731,4 +2744,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "eaa76036fa004456010050a8138aa7b147b9f3f8f614708710e146224d71b8a4" +content-hash = "b16efd0364fcd29610e0eae102b66eda5771f66116953175e2d4e54550537cfa" diff --git a/jobs/payment-jobs/pyproject.toml b/jobs/payment-jobs/pyproject.toml index 91f347013..402cfb107 100644 --- a/jobs/payment-jobs/pyproject.toml +++ b/jobs/payment-jobs/pyproject.toml @@ -7,10 +7,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" -sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/bcgov/sbc-pay.git", branch = "feature-queue-python-upgrade", subdirectory = "pay-api"} -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", branch = "sync-python-to-main", subdirectory = "pay-api"} gunicorn = "^21.2.0" flask = "^3.0.2" flask-sqlalchemy = "^3.1.1" @@ -28,7 +25,7 @@ pysftp = "^0.2.9" flask-migrate = "^4.0.7" itsdangerous = "^2.1.2" dataclass-wizard = "^0.22.3" -launchdarkly-server-sdk = "^9.2.2" +launchdarkly-server-sdk = "^8.2.1" cx-oracle = "^8.3.0" more-itertools = "^10.2.0" pg8000 = "^1.30.5" diff --git a/jobs/payment-jobs/tasks/activate_pad_account_task.py b/jobs/payment-jobs/tasks/activate_pad_account_task.py index d24127537..2659006aa 100644 --- a/jobs/payment-jobs/tasks/activate_pad_account_task.py +++ b/jobs/payment-jobs/tasks/activate_pad_account_task.py @@ -19,7 +19,8 @@ from flask import current_app from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.utils.enums import CfsAccountStatus, MessageType, PaymentMethod +from pay_api.utils.enums import CfsAccountStatus, PaymentMethod +from sbc_common_components.utils.enums import QueueMessageTypes from utils import mailer @@ -57,4 +58,4 @@ def activate_pad_accounts(cls): if pay_account.payment_method != PaymentMethod.PAD.value: pay_account.payment_method = PaymentMethod.PAD.value pay_account.save() - mailer.publish_mailer_events(MessageType.PAD_CONFIRMATION_PERIOD_OVER, pay_account) + mailer.publish_mailer_events(QueueMessageTypes.CONFIRMATION_PERIOD_OVER.value, pay_account) diff --git a/jobs/payment-jobs/tasks/ap_task.py b/jobs/payment-jobs/tasks/ap_task.py index 0633a8c3b..c317cb447 100644 --- a/jobs/payment-jobs/tasks/ap_task.py +++ b/jobs/payment-jobs/tasks/ap_task.py @@ -76,7 +76,7 @@ def _create_routing_slip_refund_file(cls): # pylint:disable=too-many-locals, to if not routing_slips_dao: return - for routing_slips in batched(routing_slips_dao, 250): + for routing_slips in list(batched(routing_slips_dao, 250)): ejv_file_model: EjvFileModel = EjvFileModel( file_type=cls.ap_type.value, file_ref=cls.get_file_name(), @@ -118,7 +118,7 @@ def _create_non_gov_disbursement_file(cls): # pylint:disable=too-many-locals return # 250 MAX is all the transactions the feeder can take per batch. - for invoices in batched(total_invoices, 250): + for invoices in list(batched(total_invoices, 250)): bca_distribution = cls._get_bca_distribution_string() ejv_file_model: EjvFileModel = EjvFileModel( file_type=cls.ap_type.value, diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index 4c075604c..55dbf497e 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -22,7 +22,8 @@ from pay_api.services.cfs_service import CFSService from pay_api.services.oauth_service import OAuthService from pay_api.utils.constants import RECEIPT_METHOD_EFT_MONTHLY, RECEIPT_METHOD_PAD_DAILY -from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, MessageType, PaymentMethod +from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, PaymentMethod +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from services import routing_slip from utils import mailer @@ -140,7 +141,7 @@ def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: Paym capture_message(f'User Input needed for creating CFS Account: account id={pay_account.id}, ' f'auth account : {pay_account.auth_account_id}, ERROR : Invalid Bank Details', level='error') - mailer.publish_mailer_events(MessageType.PAD_SETUP_FAILED, pay_account) + mailer.publish_mailer_events(QueueMessageTypes.PAD_SETUP_FAILED.value, pay_account) pending_account.status = CfsAccountStatus.INACTIVE.value pending_account.save() return diff --git a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py index bdc24ec4a..862d2fce7 100644 --- a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py @@ -32,8 +32,9 @@ from pay_api.services.payment import Payment from pay_api.services.payment_account import PaymentAccount as PaymentAccountService from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, PaymentSystem) + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, PaymentSystem) from pay_api.utils.util import generate_transaction_number +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from utils import mailer @@ -320,7 +321,8 @@ def _create_pad_invoices(cls): # pylint: disable=too-many-locals 'invoice_total': float(invoice_total), 'invoice_process_date': f'{datetime.now()}' } - mailer.publish_mailer_events(MessageType.PAD_INVOICE_CREATED, payment_account, additional_params) + mailer.publish_mailer_events(QueueMessageTypes.PAD_INVOICE_CREATED.value, payment_account, + additional_params) # Iterate invoice and create invoice reference records for invoice in account_invoices: invoice_reference = InvoiceReferenceModel( @@ -452,7 +454,7 @@ def _create_eft_invoices(cls): current_app.logger.error(e) continue - mailer.publish_mailer_events(MessageType.EFT_INVOICE_CREATED, payment_account, { + mailer.publish_mailer_events(QueueMessageTypes.EFT_INVOICE_CREATED.value, payment_account, { 'invoice_total': float(invoice_total), 'invoice_process_date': f'{datetime.now(tz=timezone.utc)}' }) diff --git a/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py b/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py index 2a04f3485..200f792c6 100644 --- a/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py +++ b/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py @@ -19,7 +19,8 @@ from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import db -from pay_api.utils.enums import InvoiceStatus, MessageType, PaymentMethod +from pay_api.utils.enums import InvoiceStatus, PaymentMethod +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from sqlalchemy import Date, and_, cast, func @@ -76,7 +77,8 @@ def _notify_for_ob(cls): # pylint: disable=too-many-locals 'cfsAccountId': cfs_account.cfs_account, 'authAccountId': pay_account.auth_account_id, } - mailer.publish_mailer_events(MessageType.ONLINE_BANKING_OUTSTANDING_INVOICE, pay_account, + mailer.publish_mailer_events(QueueMessageTypes.PAYMENT_PENDING.value, + pay_account, addition_params_to_mailer) except Exception as e: # NOQA # pylint: disable=broad-except capture_message(f'Error on notifying mailer OB Pending invoice: account id={pay_account.id}, ' diff --git a/jobs/payment-jobs/tests/jobs/conftest.py b/jobs/payment-jobs/tests/jobs/conftest.py index 0f6bcfeb3..832ed837f 100644 --- a/jobs/payment-jobs/tests/jobs/conftest.py +++ b/jobs/payment-jobs/tests/jobs/conftest.py @@ -27,6 +27,30 @@ from utils.logger import setup_logging +@pytest.fixture(autouse=True) +def mock_pub_sub_call(mocker): + """Mock pub sub call.""" + class Expando(object): + """Expando class.""" + + class PublisherMock: + """Publisher Mock.""" + + def __init__(self, *args, **kwargs): + def result(): + """Return true for mock.""" + return True + self.result = result + + def publish(self, *args, **kwargs): + """Publish mock.""" + ex = Expando() + ex.result = self.result + return ex + + mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) + + @pytest.fixture(scope='session') def app(): """Return a session-wide application configured in TEST mode.""" diff --git a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py index 9b3f596d8..7b4fd3960 100644 --- a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py +++ b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py @@ -45,6 +45,7 @@ app = None +# Travis Semple - in the future please remove this, this should be inside of conftest.py like the other fixtures. @pytest.fixture def setup(): """Initialize app with test env for testing.""" diff --git a/jobs/payment-jobs/utils/mailer.py b/jobs/payment-jobs/utils/mailer.py index 1379db03c..69cf1a6b9 100644 --- a/jobs/payment-jobs/utils/mailer.py +++ b/jobs/payment-jobs/utils/mailer.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -14,15 +14,14 @@ """Task to activate accounts with pending activation.Mostly for PAD with 3 day activation period.""" from dataclasses import dataclass from datetime import datetime -from typing import Dict from flask import current_app from pay_api.models import FeeSchedule as FeeScheduleModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Statement as StatementModel from pay_api.services import gcp_queue_publisher -from pay_api.services.gcp_queue_publisher import QueueMessage -from pay_api.utils.enums import QueueSources, MessageType +from pay_api.utils.enums import QueueSources +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message @@ -38,8 +37,7 @@ class StatementNotificationInfo: total_amount_owing: float -def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, - additional_params: Dict = {}): +def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, additional_params=None): """Publish payment message to the mailer queue.""" # Publish message to the Queue, saying account has been activated. Using the event spec. @@ -48,11 +46,11 @@ def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, payload = { 'accountId': pay_account.auth_account_id, 'nsfFee': float(fee_schedule.fee.amount), - **additional_params + **(additional_params or {}) } try: gcp_queue_publisher.publish_to_queue( - QueueMessage( + gcp_queue_publisher.QueueMessage( source=QueueSources.PAY_JOBS.value, message_type=message_type, payload=payload, @@ -71,6 +69,7 @@ def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, def publish_statement_notification(pay_account: PaymentAccountModel, statement: StatementModel, total_amount_owing: float, emails: str) -> bool: """Publish payment statement notification message to the mailer queue.""" + message_type = QueueMessageTypes.STATEMENT_NOTIFICATION.value payload = { 'emailAddresses': emails, 'accountId': pay_account.auth_account_id, @@ -81,9 +80,9 @@ def publish_statement_notification(pay_account: PaymentAccountModel, statement: } try: gcp_queue_publisher.publish_to_queue( - QueueMessage( + gcp_queue_publisher.QueueMessage( source=QueueSources.PAY_JOBS.value, - message_type=MessageType.STATEMENT_NOTIFICATION.value, + message_type=message_type, payload=payload, topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') ) @@ -103,8 +102,8 @@ def publish_statement_notification(pay_account: PaymentAccountModel, statement: def publish_payment_notification(info: StatementNotificationInfo) -> bool: """Publish payment notification message to the mailer queue.""" - notification_type = MessageType.STATEMENT_DUE_NOTIFICATION.value if info.is_due \ - else MessageType.STATEMENT_REMINDER_NOTIFICATION.value + message_type = QueueMessageTypes.PAYMENT_DUE_NOTIFICATION.value if info.is_due \ + else QueueMessageTypes.PAYMENT_REMINDER_NOTIFICATION.value payload = { 'emailAddresses': info.emails, @@ -115,9 +114,9 @@ def publish_payment_notification(info: StatementNotificationInfo) -> bool: } try: gcp_queue_publisher.publish_to_queue( - QueueMessage( + gcp_queue_publisher.QueueMessage( source=QueueSources.PAY_JOBS.value, - message_type=notification_type, + message_type=message_type, payload=payload, topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') ) @@ -133,4 +132,3 @@ def publish_payment_notification(info: StatementNotificationInfo) -> bool: return False return True - diff --git a/pay-admin/requirements.txt b/pay-admin/requirements.txt deleted file mode 100644 index a0d4f1ac5..000000000 --- a/pay-admin/requirements.txt +++ /dev/null @@ -1,34 +0,0 @@ -Authlib==1.3.0 -Flask-Admin==1.6.1 -Flask-SQLAlchemy==3.1.1 -Flask==3.0.2 -Jinja2==3.1.3 -MarkupSafe==2.1.5 -SQLAlchemy==2.0.28 -WTForms==3.1.2 -Werkzeug==3.0.1 -blinker==1.7.0 -certifi==2024.2.2 -cffi==1.16.0 -charset-normalizer==3.3.2 -click==8.1.7 -cryptography==42.0.5 -flask-oidc==2.1.1 -greenlet==3.0.3 -gunicorn==21.2.0 -httplib2==0.22.0 -idna==3.6 -itsdangerous==2.1.2 -packaging==23.2 -psycopg2-binary==2.9.9 -pycparser==2.21 -pyparsing==3.1.2 -python-dotenv==1.0.1 -requests-futures==1.0.1 -requests==2.31.0 -typing_extensions==4.10.0 -urllib3==2.2.1 --e git+https://github.com/bcgov/sbc-pay.git@feature-queue-python-upgrade#egg=pay-api&subdirectory=pay-api --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -git+https://github.com/daxiom/simple-cloudevent.py.git -git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-api/devops/vaults.json b/pay-api/devops/vaults.json new file mode 100644 index 000000000..5a1bc6bf6 --- /dev/null +++ b/pay-api/devops/vaults.json @@ -0,0 +1,59 @@ +[ + { + "vault": "shared", + "application": [ + "api-endpoints", + "encryption-key" + ] + }, + { + "vault": "keycloak", + "application": [ + "jwt-base", + "sbc-auth-admin" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "base", + "account-events-listener", + "payment", + "account-mailer" + ] + }, + { + "vault": "payment-external-services", + "application": [ + "paybc", + "cfs" + ] + }, + { + "vault": "relationship", + "application": [ + "postgres-pay", + "pay-api", + "jwt" + ] + }, + { + "vault": "sentry", + "application": [ + "relationship-api" + ] + }, + { + "vault": "launchdarkly", + "application": [ + "pay" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "gtksf3", + "topics" + ] + } +] diff --git a/pay-api/gunicorn_config.py b/pay-api/gunicorn_config.py index a3ab9633f..5a05fdb3b 100755 --- a/pay-api/gunicorn_config.py +++ b/pay-api/gunicorn_config.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/manage.py b/pay-api/manage.py index 8e1952a5c..7f786c35b 100755 --- a/pay-api/manage.py +++ b/pay-api/manage.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/poetry.lock b/pay-api/poetry.lock index 49cd2bdc5..3253cd826 100644 --- a/pay-api/poetry.lock +++ b/pay-api/poetry.lock @@ -32,13 +32,13 @@ files = [ [[package]] name = "astroid" -version = "3.1.0" +version = "3.2.2" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, ] [[package]] @@ -62,13 +62,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.1.0" +version = "2.2.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false python-versions = ">=3.8" files = [ - {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, - {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, + {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, + {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, ] [package.dependencies] @@ -331,63 +331,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.4" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, - {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, - {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, - {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, - {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, - {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, - {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, - {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, - {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, - {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, - {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, - {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, - {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, - {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.extras] @@ -522,13 +522,13 @@ tests = ["coverage", "coveralls", "dill", "mock", "nose"] [[package]] name = "faker" -version = "24.3.0" +version = "24.14.1" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-24.3.0-py3-none-any.whl", hash = "sha256:9978025e765ba79f8bf6154c9630a9c2b7f9c9b0f175d4ad5e04b19a82a8d8d6"}, - {file = "Faker-24.3.0.tar.gz", hash = "sha256:5fb5aa9749d09971e04a41281ae3ceda9414f683d4810a694f8a8eebb8f9edec"}, + {file = "Faker-24.14.1-py3-none-any.whl", hash = "sha256:a5edba3aa17a1d689c8907e5b0cd1653079c2466a4807f083aa7b5f80a00225d"}, + {file = "Faker-24.14.1.tar.gz", hash = "sha256:380a3697e696ae4fcf50a93a3d9e0286fab7dfbf05a9caa4421fa4727c6b1e89"}, ] [package.dependencies] @@ -646,13 +646,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.1.0" +version = "2.3.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, - {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, ] [package.dependencies] @@ -674,25 +674,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask_jwt_oidc" -version = "0.3.0" -description = "Flask JWT OIDC" +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" optional = false -python-versions = "*" +python-versions = "^3.9" files = [] develop = false [package.dependencies] -cachelib = "*" -flask = "*" -python-jose = "*" -six = "*" +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" [package.source] type = "git" -url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" [[package]] name = "flask-marshmallow" @@ -793,27 +793,49 @@ sqlalchemy = ">=2.0.16" [[package]] name = "freezegun" -version = "1.4.0" +version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, - {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, ] [package.dependencies] python-dateutil = ">=2.7" +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + [[package]] name = "google-api-core" -version = "2.17.1" +version = "2.19.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, - {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, ] [package.dependencies] @@ -821,6 +843,7 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -831,13 +854,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.28.1" +version = "2.29.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, - {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, ] [package.dependencies] @@ -854,13 +877,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.20.0" +version = "2.21.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, - {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, ] [package.dependencies] @@ -982,84 +1005,76 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.62.1" +version = "1.64.0" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, - {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, - {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, - {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, - {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, - {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, - {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, - {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, - {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, - {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, - {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, - {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, - {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, - {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, - {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, - {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, - {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, - {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, - {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, - {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, - {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, - {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, - {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, - {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.62.1)"] +protobuf = ["grpcio-tools (>=1.64.0)"] [[package]] name = "grpcio-status" -version = "1.62.1" +version = "1.62.2" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, - {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.1" +grpcio = ">=1.62.2" protobuf = ">=4.21.6" [[package]] @@ -1214,28 +1229,26 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "9.2.2" +version = "8.2.1" description = "LaunchDarkly SDK for Python" optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, - {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" -launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.26.0,<3" +urllib3 = ">=1.22.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] -test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1429,43 +1442,44 @@ flake8 = ">=5.0.0" [[package]] name = "pg8000" -version = "1.30.5" +version = "1.31.2" description = "PostgreSQL interface library" optional = false python-versions = ">=3.8" files = [ - {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, - {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, + {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, + {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, ] [package.dependencies] python-dateutil = ">=2.8.2" -scramp = ">=1.4.4" +scramp = ">=1.4.5" [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -1695,17 +1709,17 @@ files = [ [[package]] name = "pylint" -version = "3.1.0" +version = "3.2.2" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, ] [package.dependencies] -astroid = ">=3.1.0,<=3.2.0-dev0" +astroid = ">=3.2.2,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -1801,23 +1815,23 @@ files = [ [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" @@ -1839,17 +1853,17 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.12.0" +version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, - {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, ] [package.dependencies] -pytest = ">=5.0" +pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] @@ -1969,18 +1983,18 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" +resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" subdirectory = "python" [[package]] name = "scramp" -version = "1.4.4" +version = "1.4.5" description = "An implementation of the SCRAM protocol." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, - {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, + {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, + {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, ] [package.dependencies] @@ -2044,19 +2058,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "69.2.0" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simple-cloudevent" @@ -2111,7 +2124,7 @@ develop = false type = "git" url = "https://github.com/bcgov/lear.git" reference = "feature-legal-name" -resolved_reference = "bb3209f8e8894c9b9f7be95a9fd871c644e2ec69" +resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" subdirectory = "python/common/sql-versioning" [[package]] @@ -2273,13 +2286,13 @@ twisted = ["twisted"] [[package]] name = "tomlkit" -version = "0.12.4" +version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] @@ -2350,4 +2363,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "6ed39989416242b0917aea66317b7c94e2d6bdc34e58c391ce23c15defbd55e6" +content-hash = "fbf5a52a364793793ad0d5944f9f222cc7fcc3af331f493284727ff4aa2cd5de" diff --git a/pay-api/pyproject.toml b/pay-api/pyproject.toml index c9639f753..b8335b7a8 100644 --- a/pay-api/pyproject.toml +++ b/pay-api/pyproject.toml @@ -7,7 +7,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" -flask-caching = "2.1.0" +flask-caching = "2.3.0" flask-cors = "4.0.0" flask-migrate = "4.0.7" flask-moment = "1.0.5" @@ -35,16 +35,10 @@ cryptography = "42.0.5" dpath = "2.1.6" ecdsa = "0.18.0" expiringdict = "1.2.2" -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} flask-marshmallow = "1.2.0" -google-api-core = "2.17.1" -google-auth = "2.28.1" -google-cloud-pubsub = "2.20.0" -googleapis-common-protos = "1.63.0" +gcp-queue = { git = "https://github.com/seeker25/sbc-connect-common.git", subdirectory = "python/gcp-queue", branch = "main" } greenlet = "3.0.3" -grpc-google-iam-v1 = "0.13.0" -grpcio-status = "1.62.1" -grpcio = "1.62.1" gunicorn = "21.2.0" holidays = "0.37" idna = "3.6" @@ -52,7 +46,7 @@ itsdangerous = "2.1.2" jaeger-client = "4.8.0" jsonschema = "4.17.3" launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "9.2.2" +launchdarkly-server-sdk = "8.2.1" marshmallow-sqlalchemy = "1.0.0" marshmallow = "3.21.1" opentracing = "2.4.0" @@ -80,7 +74,6 @@ thrift = "0.16.0" tornado = "6.4" typing-extensions = "4.10.0" urllib3 = "2.2.1" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} pg8000 = "^1.30.5" sql-versioning = { git = "https://github.com/bcgov/lear.git", subdirectory = "python/common/sql-versioning", branch = "feature-legal-name" } diff --git a/pay-api/scripts/verify_license_headers.sh b/pay-api/scripts/verify_license_headers.sh index 028b95c63..a160d3ac8 100755 --- a/pay-api/scripts/verify_license_headers.sh +++ b/pay-api/scripts/verify_license_headers.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ # limitations under the License. -COPYRIGHT="Copyright © 2019 Province of British Columbia" +COPYRIGHT="Copyright © 2024 Province of British Columbia" RET=0 for file in $(find $@ -not \( -path */venv -prune \) -not \( -path */migrations -prune \) -not \( -path */tests -prune \) -not \( -path */.egg* -prune \) -name \*.py) diff --git a/pay-api/setup.cfg b/pay-api/setup.cfg index 4a29f38b9..ccbd8d7ce 100755 --- a/pay-api/setup.cfg +++ b/pay-api/setup.cfg @@ -9,7 +9,7 @@ classifiers = Topic :: Payment License :: OSI Approved :: Apache Software License Natural Language :: English - Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.12 license = Apache Software License Version 2.0 description = A short description of the project long_description = file: README.md diff --git a/pay-api/setup.py b/pay-api/setup.py index 9f0ddb399..59919617f 100755 --- a/pay-api/setup.py +++ b/pay-api/setup.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia. +# Copyright © 2024 Province of British Columbia. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index 611fdcba2..c5216cd3e 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -48,6 +48,7 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): app.config.from_object(config.CONFIGURATION[run_mode]) flags.init_app(app) + queue.init_app(app) db.init_app(app) queue.init_app(app) Migrate(app, db) diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 408484a69..5d2593124 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -132,14 +132,12 @@ class _Config: # pylint: disable=too-few-public-methods 'PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL' ) - # GCP PubSub - AUDIENCE = os.getenv('AUDIENCE', None) - GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) - PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher') - ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) - EVENT_LISTENER_TOPIC = os.getenv('EVENT_LISTENER_TOPIC', None) - NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', None) - BUSINESS_PAY_TOPIC = os.getenv('BUSINESS_PAY_TOPIC', None) + # PUB/SUB - PUB: auth-event-dev, account-mailer-dev, business-pay-dev, namex-pay-dev + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') + AUTH_EVENT_TOPIC = os.getenv('AUTH_EVENT_TOPIC', 'auth-event-dev') + BUSINESS_PAY_TOPIC = os.getenv('BUSINESS_PAY_TOPIC', 'business-pay-dev') + GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) + NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', 'namex-pay-dev') # API Endpoints AUTH_API_URL = os.getenv('AUTH_API_URL', '') diff --git a/pay-api/src/pay_api/exceptions/__init__.py b/pay-api/src/pay_api/exceptions/__init__.py index 03c47bcea..07dd45970 100755 --- a/pay-api/src/pay_api/exceptions/__init__.py +++ b/pay-api/src/pay_api/exceptions/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/factory/__init__.py b/pay-api/src/pay_api/factory/__init__.py index c24369f03..2f5095570 100644 --- a/pay-api/src/pay_api/factory/__init__.py +++ b/pay-api/src/pay_api/factory/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/factory/payment_system_factory.py b/pay-api/src/pay_api/factory/payment_system_factory.py index f5ffbb998..b9000b55f 100644 --- a/pay-api/src/pay_api/factory/payment_system_factory.py +++ b/pay-api/src/pay_api/factory/payment_system_factory.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/__init__.py b/pay-api/src/pay_api/models/__init__.py index 31ba7d754..c8f85920e 100755 --- a/pay-api/src/pay_api/models/__init__.py +++ b/pay-api/src/pay_api/models/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/account_fee.py b/pay-api/src/pay_api/models/account_fee.py index 7df2f5663..a23a340ef 100644 --- a/pay-api/src/pay_api/models/account_fee.py +++ b/pay-api/src/pay_api/models/account_fee.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/audit.py b/pay-api/src/pay_api/models/audit.py index 663ee514b..5845ac386 100644 --- a/pay-api/src/pay_api/models/audit.py +++ b/pay-api/src/pay_api/models/audit.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/base_model.py b/pay-api/src/pay_api/models/base_model.py index 22a10e467..7ad36bbac 100644 --- a/pay-api/src/pay_api/models/base_model.py +++ b/pay-api/src/pay_api/models/base_model.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/base_schema.py b/pay-api/src/pay_api/models/base_schema.py index e4255a85b..d628f1cb6 100644 --- a/pay-api/src/pay_api/models/base_schema.py +++ b/pay-api/src/pay_api/models/base_schema.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/cfs_account.py b/pay-api/src/pay_api/models/cfs_account.py index b16eb778b..2d13c78ac 100644 --- a/pay-api/src/pay_api/models/cfs_account.py +++ b/pay-api/src/pay_api/models/cfs_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/cfs_account_status_code.py b/pay-api/src/pay_api/models/cfs_account_status_code.py index 1507d6495..b13ba1a3c 100644 --- a/pay-api/src/pay_api/models/cfs_account_status_code.py +++ b/pay-api/src/pay_api/models/cfs_account_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/code_table.py b/pay-api/src/pay_api/models/code_table.py index aac84d3c1..9cb7afd11 100644 --- a/pay-api/src/pay_api/models/code_table.py +++ b/pay-api/src/pay_api/models/code_table.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/comment.py b/pay-api/src/pay_api/models/comment.py index eece02a87..1f57b9f43 100644 --- a/pay-api/src/pay_api/models/comment.py +++ b/pay-api/src/pay_api/models/comment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/corp_type.py b/pay-api/src/pay_api/models/corp_type.py index 0174857b7..587e59cc5 100644 --- a/pay-api/src/pay_api/models/corp_type.py +++ b/pay-api/src/pay_api/models/corp_type.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/credit.py b/pay-api/src/pay_api/models/credit.py index 1b010b921..d604f1e3c 100644 --- a/pay-api/src/pay_api/models/credit.py +++ b/pay-api/src/pay_api/models/credit.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/db.py b/pay-api/src/pay_api/models/db.py index dee2fa81c..9ee95c8fd 100755 --- a/pay-api/src/pay_api/models/db.py +++ b/pay-api/src/pay_api/models/db.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/disbursement_status_code.py b/pay-api/src/pay_api/models/disbursement_status_code.py index 7ffb5720c..bed1297e1 100644 --- a/pay-api/src/pay_api/models/disbursement_status_code.py +++ b/pay-api/src/pay_api/models/disbursement_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/ejv_file.py b/pay-api/src/pay_api/models/ejv_file.py index d0f5b4343..7c44c6d8e 100644 --- a/pay-api/src/pay_api/models/ejv_file.py +++ b/pay-api/src/pay_api/models/ejv_file.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/ejv_header.py b/pay-api/src/pay_api/models/ejv_header.py index aa514e4f8..c6fe07fa1 100644 --- a/pay-api/src/pay_api/models/ejv_header.py +++ b/pay-api/src/pay_api/models/ejv_header.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/ejv_link.py b/pay-api/src/pay_api/models/ejv_link.py index 4055df51d..210803a5a 100644 --- a/pay-api/src/pay_api/models/ejv_link.py +++ b/pay-api/src/pay_api/models/ejv_link.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/error_code.py b/pay-api/src/pay_api/models/error_code.py index c0190cbb7..630f0d32d 100644 --- a/pay-api/src/pay_api/models/error_code.py +++ b/pay-api/src/pay_api/models/error_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/fee_code.py b/pay-api/src/pay_api/models/fee_code.py index b0992c696..5c5854173 100644 --- a/pay-api/src/pay_api/models/fee_code.py +++ b/pay-api/src/pay_api/models/fee_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/fee_schedule.py b/pay-api/src/pay_api/models/fee_schedule.py index 70686b08f..044cc567a 100644 --- a/pay-api/src/pay_api/models/fee_schedule.py +++ b/pay-api/src/pay_api/models/fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/filing_type.py b/pay-api/src/pay_api/models/filing_type.py index 08f45fb84..08d8c010a 100644 --- a/pay-api/src/pay_api/models/filing_type.py +++ b/pay-api/src/pay_api/models/filing_type.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice.py b/pay-api/src/pay_api/models/invoice.py index cdafa08bb..1627d5fd4 100644 --- a/pay-api/src/pay_api/models/invoice.py +++ b/pay-api/src/pay_api/models/invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_batch.py b/pay-api/src/pay_api/models/invoice_batch.py index 7d769dc11..27ee84d97 100644 --- a/pay-api/src/pay_api/models/invoice_batch.py +++ b/pay-api/src/pay_api/models/invoice_batch.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_batch_link.py b/pay-api/src/pay_api/models/invoice_batch_link.py index 2c1451d6b..111a62bcc 100644 --- a/pay-api/src/pay_api/models/invoice_batch_link.py +++ b/pay-api/src/pay_api/models/invoice_batch_link.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_reference.py b/pay-api/src/pay_api/models/invoice_reference.py index fcfb903ca..dd2f8747c 100644 --- a/pay-api/src/pay_api/models/invoice_reference.py +++ b/pay-api/src/pay_api/models/invoice_reference.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_reference_status_code.py b/pay-api/src/pay_api/models/invoice_reference_status_code.py index ea707dd5e..998ae5892 100644 --- a/pay-api/src/pay_api/models/invoice_reference_status_code.py +++ b/pay-api/src/pay_api/models/invoice_reference_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_status_code.py b/pay-api/src/pay_api/models/invoice_status_code.py index ce92ef38c..696fa15d3 100644 --- a/pay-api/src/pay_api/models/invoice_status_code.py +++ b/pay-api/src/pay_api/models/invoice_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/line_item_status_code.py b/pay-api/src/pay_api/models/line_item_status_code.py index 4f7d86690..5fe9ea968 100644 --- a/pay-api/src/pay_api/models/line_item_status_code.py +++ b/pay-api/src/pay_api/models/line_item_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/non_sufficient_funds.py b/pay-api/src/pay_api/models/non_sufficient_funds.py index deb5169ef..21dc9bd13 100644 --- a/pay-api/src/pay_api/models/non_sufficient_funds.py +++ b/pay-api/src/pay_api/models/non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/notification_status_code.py b/pay-api/src/pay_api/models/notification_status_code.py index 856ae0aa6..b86942693 100644 --- a/pay-api/src/pay_api/models/notification_status_code.py +++ b/pay-api/src/pay_api/models/notification_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment.py b/pay-api/src/pay_api/models/payment.py index be3c35823..fc996bc5b 100644 --- a/pay-api/src/pay_api/models/payment.py +++ b/pay-api/src/pay_api/models/payment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_account.py b/pay-api/src/pay_api/models/payment_account.py index 3e2b7b72a..46bbc179f 100644 --- a/pay-api/src/pay_api/models/payment_account.py +++ b/pay-api/src/pay_api/models/payment_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_line_item.py b/pay-api/src/pay_api/models/payment_line_item.py index 85a5acfd7..62635f4a5 100644 --- a/pay-api/src/pay_api/models/payment_line_item.py +++ b/pay-api/src/pay_api/models/payment_line_item.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_method.py b/pay-api/src/pay_api/models/payment_method.py index 8f77ef949..ed9b4edb7 100644 --- a/pay-api/src/pay_api/models/payment_method.py +++ b/pay-api/src/pay_api/models/payment_method.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_status_code.py b/pay-api/src/pay_api/models/payment_status_code.py index 6fcf9da34..bb4f92378 100644 --- a/pay-api/src/pay_api/models/payment_status_code.py +++ b/pay-api/src/pay_api/models/payment_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_system.py b/pay-api/src/pay_api/models/payment_system.py index dd6f29074..91a0ab6b7 100644 --- a/pay-api/src/pay_api/models/payment_system.py +++ b/pay-api/src/pay_api/models/payment_system.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_transaction.py b/pay-api/src/pay_api/models/payment_transaction.py index ea1877c14..0e38d3966 100644 --- a/pay-api/src/pay_api/models/payment_transaction.py +++ b/pay-api/src/pay_api/models/payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/receipt.py b/pay-api/src/pay_api/models/receipt.py index 91b3faa27..a9f617f94 100644 --- a/pay-api/src/pay_api/models/receipt.py +++ b/pay-api/src/pay_api/models/receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/refund.py b/pay-api/src/pay_api/models/refund.py index c8db6e5ce..bba73faed 100644 --- a/pay-api/src/pay_api/models/refund.py +++ b/pay-api/src/pay_api/models/refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/routing_slip.py b/pay-api/src/pay_api/models/routing_slip.py index 8a5cd062c..a6c3163fe 100644 --- a/pay-api/src/pay_api/models/routing_slip.py +++ b/pay-api/src/pay_api/models/routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/routing_slip_status_code.py b/pay-api/src/pay_api/models/routing_slip_status_code.py index f1ae33e70..9f7273aed 100644 --- a/pay-api/src/pay_api/models/routing_slip_status_code.py +++ b/pay-api/src/pay_api/models/routing_slip_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement.py b/pay-api/src/pay_api/models/statement.py index 422bf1ffd..6899525bd 100644 --- a/pay-api/src/pay_api/models/statement.py +++ b/pay-api/src/pay_api/models/statement.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement_invoices.py b/pay-api/src/pay_api/models/statement_invoices.py index 5dea45eec..92060cb53 100644 --- a/pay-api/src/pay_api/models/statement_invoices.py +++ b/pay-api/src/pay_api/models/statement_invoices.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement_recipients.py b/pay-api/src/pay_api/models/statement_recipients.py index 5a89f4a4b..531fffd62 100644 --- a/pay-api/src/pay_api/models/statement_recipients.py +++ b/pay-api/src/pay_api/models/statement_recipients.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement_settings.py b/pay-api/src/pay_api/models/statement_settings.py index 3b4eacbbd..325b31b80 100644 --- a/pay-api/src/pay_api/models/statement_settings.py +++ b/pay-api/src/pay_api/models/statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/transaction_status_code.py b/pay-api/src/pay_api/models/transaction_status_code.py index c42128c72..615a1c9ae 100644 --- a/pay-api/src/pay_api/models/transaction_status_code.py +++ b/pay-api/src/pay_api/models/transaction_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/__init__.py b/pay-api/src/pay_api/resources/__init__.py index b9e0dcbb5..13c649a27 100644 --- a/pay-api/src/pay_api/resources/__init__.py +++ b/pay-api/src/pay_api/resources/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/ops.py b/pay-api/src/pay_api/resources/ops.py index 2926fa831..c399771a1 100755 --- a/pay-api/src/pay_api/resources/ops.py +++ b/pay-api/src/pay_api/resources/ops.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account.py b/pay-api/src/pay_api/resources/v1/account.py index 097a638ae..ad54e40ea 100644 --- a/pay-api/src/pay_api/resources/v1/account.py +++ b/pay-api/src/pay_api/resources/v1/account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account_statements.py b/pay-api/src/pay_api/resources/v1/account_statements.py index 32a348c61..8559819d7 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements.py +++ b/pay-api/src/pay_api/resources/v1/account_statements.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py index 59beb51af..0c8dba6ef 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account_statements_settings.py b/pay-api/src/pay_api/resources/v1/account_statements_settings.py index e1f4b42e7..60cdc4335 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_settings.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/bank_accounts.py b/pay-api/src/pay_api/resources/v1/bank_accounts.py index 9398164e8..2783c5855 100644 --- a/pay-api/src/pay_api/resources/v1/bank_accounts.py +++ b/pay-api/src/pay_api/resources/v1/bank_accounts.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/code.py b/pay-api/src/pay_api/resources/v1/code.py index d1f3d64d6..a578bd1e8 100644 --- a/pay-api/src/pay_api/resources/v1/code.py +++ b/pay-api/src/pay_api/resources/v1/code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/distributions.py b/pay-api/src/pay_api/resources/v1/distributions.py index 42e5b7b75..3f9c1a94b 100644 --- a/pay-api/src/pay_api/resources/v1/distributions.py +++ b/pay-api/src/pay_api/resources/v1/distributions.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/fas/__init__.py b/pay-api/src/pay_api/resources/v1/fas/__init__.py index 7ec3a8f2d..d248d9f54 100644 --- a/pay-api/src/pay_api/resources/v1/fas/__init__.py +++ b/pay-api/src/pay_api/resources/v1/fas/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/fas/refund.py b/pay-api/src/pay_api/resources/v1/fas/refund.py index 78e098f32..b6ca9cd39 100644 --- a/pay-api/src/pay_api/resources/v1/fas/refund.py +++ b/pay-api/src/pay_api/resources/v1/fas/refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py index aab02d8be..f8311e8f5 100644 --- a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py +++ b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -190,6 +190,7 @@ def get_routing_slip_comments(routing_slip_number: str): def post_routing_slip_comment(routing_slip_number: str): """Create comment for a slip.""" current_app.logger.info(' InvoiceReference: """Return a static invoice number for direct pay.""" current_app.logger.debug(' PaymentAccount: pay_account.save() pa_service = cls.find_by_id(pay_account.id) if not already_has_eft_enabled: - payload = pa_service.create_account_event_payload(MessageType.EFT_AVAILABLE_NOTIFICATION.value) - pa_service._publish_queue_message(payload, MessageType.EFT_AVAILABLE_NOTIFICATION.value) + payload = pa_service.create_account_event_payload(QueueMessageTypes.EFT_AVAILABLE_NOTIFICATION.value) + pa_service._publish_queue_message(payload, QueueMessageTypes.EFT_AVAILABLE_NOTIFICATION.value) return pa_service diff --git a/pay-api/src/pay_api/services/payment_line_item.py b/pay-api/src/pay_api/services/payment_line_item.py index 6c3b36d4f..9098efd7f 100644 --- a/pay-api/src/pay_api/services/payment_line_item.py +++ b/pay-api/src/pay_api/services/payment_line_item.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/payment_service.py b/pay-api/src/pay_api/services/payment_service.py index 6da906950..feecefa92 100644 --- a/pay-api/src/pay_api/services/payment_service.py +++ b/pay-api/src/pay_api/services/payment_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/payment_transaction.py b/pay-api/src/pay_api/services/payment_transaction.py index d93b02079..3fa1cea81 100644 --- a/pay-api/src/pay_api/services/payment_transaction.py +++ b/pay-api/src/pay_api/services/payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -16,13 +16,15 @@ from __future__ import annotations import uuid -from dataclasses import asdict, dataclass +from dataclasses import asdict from datetime import datetime -from typing import Dict, List, Optional +from typing import Dict, List import humps from flask import current_app from sentry_sdk import capture_message +from sbc_common_components.utils.dataclasses import PaymentToken +from sbc_common_components.utils.enums import QueueMessageTypes from pay_api.exceptions import BusinessException, ServiceUnavailableException from pay_api.factory.payment_system_factory import PaymentSystemFactory @@ -36,23 +38,13 @@ from pay_api.services.payment_account import PaymentAccount from pay_api.services.receipt import Receipt from pay_api.utils.enums import ( - InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, QueueSources, TransactionStatus) + InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, QueueSources, TransactionStatus) from pay_api.utils.errors import Error from pay_api.utils.util import get_topic_for_corp_type, is_valid_redirect_url from .payment import Payment -@dataclass -class PaymentToken: - """Payment Token payload common interface for LEAR and Names.""" - - id: Optional[str] = None - status_code: Optional[str] = None - filing_identifier: Optional[str] = None - corp_type_code: Optional[str] = None - - class PaymentTransaction: # pylint: disable=too-many-instance-attributes, too-many-public-methods """Service to manage Payment transaction operations.""" @@ -511,7 +503,7 @@ def publish_status(transaction_dao: PaymentTransactionModel, invoice: Invoice): gcp_queue_publisher.publish_to_queue( QueueMessage( source=QueueSources.PAY_API.value, - message_type=MessageType.PAYMENT.value, + message_type=QueueMessageTypes.PAYMENT.value, payload=PaymentTransaction.create_event_payload(invoice, status_code), topic=get_topic_for_corp_type(invoice.corp_type_code) ) diff --git a/pay-api/src/pay_api/services/receipt.py b/pay-api/src/pay_api/services/receipt.py index 3cc5a108e..7d1312de0 100644 --- a/pay-api/src/pay_api/services/receipt.py +++ b/pay-api/src/pay_api/services/receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/refund.py b/pay-api/src/pay_api/services/refund.py index d0d21b8e7..3fc23d9c3 100644 --- a/pay-api/src/pay_api/services/refund.py +++ b/pay-api/src/pay_api/services/refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/statement.py b/pay-api/src/pay_api/services/statement.py index 5a23b00c5..ab0f9e77f 100644 --- a/pay-api/src/pay_api/services/statement.py +++ b/pay-api/src/pay_api/services/statement.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/statement_recipients.py b/pay-api/src/pay_api/services/statement_recipients.py index 750577f68..8180cf7c7 100644 --- a/pay-api/src/pay_api/services/statement_recipients.py +++ b/pay-api/src/pay_api/services/statement_recipients.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/statement_settings.py b/pay-api/src/pay_api/services/statement_settings.py index 602c943ce..c233b96f3 100644 --- a/pay-api/src/pay_api/services/statement_settings.py +++ b/pay-api/src/pay_api/services/statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/wire_service.py b/pay-api/src/pay_api/services/wire_service.py index 1d4d20d91..20bfa2333 100644 --- a/pay-api/src/pay_api/services/wire_service.py +++ b/pay-api/src/pay_api/services/wire_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/__init__.py b/pay-api/src/pay_api/utils/__init__.py index 9b5291dad..8150902f6 100755 --- a/pay-api/src/pay_api/utils/__init__.py +++ b/pay-api/src/pay_api/utils/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/auth.py b/pay-api/src/pay_api/utils/auth.py index c900a8ab7..19435fc86 100644 --- a/pay-api/src/pay_api/utils/auth.py +++ b/pay-api/src/pay_api/utils/auth.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/cache.py b/pay-api/src/pay_api/utils/cache.py index c1011ca67..1f57a55cf 100644 --- a/pay-api/src/pay_api/utils/cache.py +++ b/pay-api/src/pay_api/utils/cache.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/constants.py b/pay-api/src/pay_api/utils/constants.py index a01f3d7c2..657e5793a 100644 --- a/pay-api/src/pay_api/utils/constants.py +++ b/pay-api/src/pay_api/utils/constants.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index 1437c2e02..7a91fd76a 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -195,6 +195,9 @@ class CfsAccountStatus(Enum): class CorpType(Enum): """Corp Type.""" + BTR = 'BTR' + ESRA = 'ESRA' + MHR = 'MHR' NRO = 'NRO' PPR = 'PPR' VS = 'VS' @@ -216,6 +219,7 @@ class Product(Enum): """Product.""" BUSINESS = 'BUSINESS' + NRO = 'NRO' class RoutingSlipStatus(Enum): @@ -349,32 +353,6 @@ class EFTShortnameStatus(Enum): PENDING = 'PENDING' -class MessageType(Enum): - """Queue Event Types.""" - - EFT_AVAILABLE_NOTIFICATION = 'eftAvailableNotification' - PAD_PAYMENT_SUCCESS = 'PAD.PaymentSuccess' - PAD_ACCOUNT_CREATE = 'padAccountCreate' - NSF_LOCK_ACCOUNT = 'lockAccount' - NSF_UNLOCK_ACCOUNT = 'unlockAccount' - STATEMENT_NOTIFICATION = 'statementNotification' - STATEMENT_DUE_NOTIFICATION = 'statementDueNotification' - STATEMENT_REMINDER_NOTIFICATION = 'statementReminderNotification' - PAYMENT = 'payment' - EJV_FAILED = 'ejvFailed' - CAS_UPLOADED = 'casSettlementUploaded' - INCORPORATION = 'incorporationApplication' - REGISTRATION = 'registration' - CGI_ACK_RECEIVED = 'ACKReceived' - CGI_FEEDBACK_RECEIVED = 'FEEDBACKReceived' - EFT_FILE_UPLOADED = 'eftFileUploaded' - EFT_INVOICE_CREATED = 'eft.invoiceCreated' - PAD_INVOICE_CREATED = 'pad.invoiceCreated' - PAD_SETUP_FAILED = 'PadSetupFailed' - PAD_CONFIRMATION_PERIOD_OVER = 'confirmationPeriodOver' - ONLINE_BANKING_OUTSTANDING_INVOICE = 'ob.outstandingInvoice' - - class PaymentDetailsGlStatus(Enum): """Payment details GL status.""" diff --git a/pay-api/src/pay_api/utils/errors.py b/pay-api/src/pay_api/utils/errors.py index 231c3c87c..2f7a747d4 100644 --- a/pay-api/src/pay_api/utils/errors.py +++ b/pay-api/src/pay_api/utils/errors.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/logging.py b/pay-api/src/pay_api/utils/logging.py index 8568f87dd..905300f5a 100755 --- a/pay-api/src/pay_api/utils/logging.py +++ b/pay-api/src/pay_api/utils/logging.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/paybc_transaction_error_message.py b/pay-api/src/pay_api/utils/paybc_transaction_error_message.py index c93067cbb..73eb1b628 100644 --- a/pay-api/src/pay_api/utils/paybc_transaction_error_message.py +++ b/pay-api/src/pay_api/utils/paybc_transaction_error_message.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/run_version.py b/pay-api/src/pay_api/utils/run_version.py index 41c42458b..f447beb71 100755 --- a/pay-api/src/pay_api/utils/run_version.py +++ b/pay-api/src/pay_api/utils/run_version.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/user_context.py b/pay-api/src/pay_api/utils/user_context.py index 2719e323c..b31b5a813 100644 --- a/pay-api/src/pay_api/utils/user_context.py +++ b/pay-api/src/pay_api/utils/user_context.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/util.py b/pay-api/src/pay_api/utils/util.py index a2640f985..631d31f8f 100755 --- a/pay-api/src/pay_api/utils/util.py +++ b/pay-api/src/pay_api/utils/util.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,9 +28,11 @@ from dpath import get as dpath_get from flask import current_app +from pay_api.services.code import Code as CodeService + from .constants import DT_SHORT_FORMAT from .converter import Converter -from .enums import CorpType, StatementFrequency +from .enums import Code, CorpType, Product, StatementFrequency def cors_preflight(methods: str = 'GET'): @@ -274,14 +276,12 @@ def cents_to_decimal(amount: int): def get_topic_for_corp_type(corp_type: str): """Return a topic to direct the queue message to.""" - match corp_type: - case CorpType.NRO.value: - return current_app.config.get('NAMEX_PAY_TOPIC') - # Unused for now, intentionally don't send a queue message for these. - case CorpType.PPR.value | CorpType.VS.value | CorpType.CSO.value: - return None - case _: - return current_app.config.get('BUSINESS_PAY_TOPIC') + if corp_type == CorpType.NRO.value: + return current_app.config.get('NAMEX_PAY_TOPIC') + product_code = CodeService.find_code_value_by_type_and_code(Code.CORP_TYPE.value, corp_type).get('product') + if product_code == Product.BUSINESS.value: + return current_app.config.get('BUSINESS_PAY_TOPIC') + return None def unstructure_schema_items(schema, items): diff --git a/pay-api/src/pay_api/version.py b/pay-api/src/pay_api/version.py index 7003370ab..f80e8c8d1 100644 --- a/pay-api/src/pay_api/version.py +++ b/pay-api/src/pay_api/version.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/__init__.py b/pay-api/tests/__init__.py index 5b2e2f73f..212195959 100755 --- a/pay-api/tests/__init__.py +++ b/pay-api/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/conftest.py b/pay-api/tests/conftest.py index 0e56f8990..e6b606c2e 100755 --- a/pay-api/tests/conftest.py +++ b/pay-api/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,10 +35,27 @@ def app(): @pytest.fixture(autouse=True) -def mock_queue_publish(monkeypatch): - """Mock queue publish.""" - # TODO: so it can be used like this from gcp_queue_publisher import publish_to_queue - monkeypatch.setattr('pay_api.services.gcp_queue_publisher.publish_to_queue', lambda *args, **kwargs: None) +def mock_pub_sub_call(mocker): + """Mock pub sub call.""" + class Expando(object): + """Expando class.""" + + class PublisherMock: + """Publisher Mock.""" + + def __init__(self, *args, **kwargs): + def result(): + """Return true for mock.""" + return True + self.result = result + + def publish(self, *args, **kwargs): + """Publish mock.""" + ex = Expando() + ex.result = self.result + return ex + + mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) @pytest.fixture(scope='function') diff --git a/pay-api/tests/unit/__init__.py b/pay-api/tests/unit/__init__.py index d755f6a1d..65e08c540 100755 --- a/pay-api/tests/unit/__init__.py +++ b/pay-api/tests/unit/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/__init__.py b/pay-api/tests/unit/api/__init__.py index 02d5354ad..b8fd2aced 100755 --- a/pay-api/tests/unit/api/__init__.py +++ b/pay-api/tests/unit/api/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/fas/__init__.py b/pay-api/tests/unit/api/fas/__init__.py index 5bef134ec..c538a08a1 100644 --- a/pay-api/tests/unit/api/fas/__init__.py +++ b/pay-api/tests/unit/api/fas/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/fas/test_refund.py b/pay-api/tests/unit/api/fas/test_refund.py index 18cfc28f2..de9f3530f 100644 --- a/pay-api/tests/unit/api/fas/test_refund.py +++ b/pay-api/tests/unit/api/fas/test_refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/fas/test_routing_slip.py b/pay-api/tests/unit/api/fas/test_routing_slip.py index de829e550..64eab26fe 100755 --- a/pay-api/tests/unit/api/fas/test_routing_slip.py +++ b/pay-api/tests/unit/api/fas/test_routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_account.py b/pay-api/tests/unit/api/test_account.py index d417df26b..0aeda1e47 100755 --- a/pay-api/tests/unit/api/test_account.py +++ b/pay-api/tests/unit/api/test_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_bank_accounts.py b/pay-api/tests/unit/api/test_bank_accounts.py index ee49c5145..ccab4f65b 100755 --- a/pay-api/tests/unit/api/test_bank_accounts.py +++ b/pay-api/tests/unit/api/test_bank_accounts.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_code.py b/pay-api/tests/unit/api/test_code.py index e8d1020a2..5006fce95 100755 --- a/pay-api/tests/unit/api/test_code.py +++ b/pay-api/tests/unit/api/test_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_distributions.py b/pay-api/tests/unit/api/test_distributions.py index 2b3f0d799..7e2b08673 100755 --- a/pay-api/tests/unit/api/test_distributions.py +++ b/pay-api/tests/unit/api/test_distributions.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_fee.py b/pay-api/tests/unit/api/test_fee.py index 594aaa503..f8cd3928e 100755 --- a/pay-api/tests/unit/api/test_fee.py +++ b/pay-api/tests/unit/api/test_fee.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_fee_schedule.py b/pay-api/tests/unit/api/test_fee_schedule.py index 704517346..c6bc5638b 100755 --- a/pay-api/tests/unit/api/test_fee_schedule.py +++ b/pay-api/tests/unit/api/test_fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_invoice.py b/pay-api/tests/unit/api/test_invoice.py index a8fbe8d6a..253cb3fe7 100755 --- a/pay-api/tests/unit/api/test_invoice.py +++ b/pay-api/tests/unit/api/test_invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_meta.py b/pay-api/tests/unit/api/test_meta.py index 52df42ed2..02757760d 100755 --- a/pay-api/tests/unit/api/test_meta.py +++ b/pay-api/tests/unit/api/test_meta.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_non_sufficient_funds.py b/pay-api/tests/unit/api/test_non_sufficient_funds.py index 74dad35f0..ba7638669 100644 --- a/pay-api/tests/unit/api/test_non_sufficient_funds.py +++ b/pay-api/tests/unit/api/test_non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_ops.py b/pay-api/tests/unit/api/test_ops.py index ca4473f74..c081b9aa5 100755 --- a/pay-api/tests/unit/api/test_ops.py +++ b/pay-api/tests/unit/api/test_ops.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_payment.py b/pay-api/tests/unit/api/test_payment.py index 7aceb0dac..114125990 100755 --- a/pay-api/tests/unit/api/test_payment.py +++ b/pay-api/tests/unit/api/test_payment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_payment_request.py b/pay-api/tests/unit/api/test_payment_request.py index b7348b6e5..aa06aa51f 100755 --- a/pay-api/tests/unit/api/test_payment_request.py +++ b/pay-api/tests/unit/api/test_payment_request.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_receipt.py b/pay-api/tests/unit/api/test_receipt.py index f4ad0a0ee..a5ba50b64 100644 --- a/pay-api/tests/unit/api/test_receipt.py +++ b/pay-api/tests/unit/api/test_receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_refund.py b/pay-api/tests/unit/api/test_refund.py index 69b15993c..d92c23bb1 100644 --- a/pay-api/tests/unit/api/test_refund.py +++ b/pay-api/tests/unit/api/test_refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_statement.py b/pay-api/tests/unit/api/test_statement.py index f29c80eb1..d8e7463c6 100755 --- a/pay-api/tests/unit/api/test_statement.py +++ b/pay-api/tests/unit/api/test_statement.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_statement_settings.py b/pay-api/tests/unit/api/test_statement_settings.py index 3bacd7f0c..83c6bc3df 100755 --- a/pay-api/tests/unit/api/test_statement_settings.py +++ b/pay-api/tests/unit/api/test_statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_transaction.py b/pay-api/tests/unit/api/test_transaction.py index f1315518a..3d2a83d04 100755 --- a/pay-api/tests/unit/api/test_transaction.py +++ b/pay-api/tests/unit/api/test_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/conf/__init__.py b/pay-api/tests/unit/conf/__init__.py index 47a9b4f52..e369ad9a8 100755 --- a/pay-api/tests/unit/conf/__init__.py +++ b/pay-api/tests/unit/conf/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/conf/test_configuration.py b/pay-api/tests/unit/conf/test_configuration.py index fe7515de9..4e3dcf323 100755 --- a/pay-api/tests/unit/conf/test_configuration.py +++ b/pay-api/tests/unit/conf/test_configuration.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia. +# Copyright © 2024 Province of British Columbia. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/conf/test_version.py b/pay-api/tests/unit/conf/test_version.py index c21d371ee..9e6571073 100755 --- a/pay-api/tests/unit/conf/test_version.py +++ b/pay-api/tests/unit/conf/test_version.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/factory/__init__.py b/pay-api/tests/unit/factory/__init__.py index 02d5354ad..b8fd2aced 100644 --- a/pay-api/tests/unit/factory/__init__.py +++ b/pay-api/tests/unit/factory/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/factory/test_payment_system_factory.py b/pay-api/tests/unit/factory/test_payment_system_factory.py index 3e52c2b4f..932b56394 100644 --- a/pay-api/tests/unit/factory/test_payment_system_factory.py +++ b/pay-api/tests/unit/factory/test_payment_system_factory.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/__init__.py b/pay-api/tests/unit/models/__init__.py index 4a0c382d6..c0d5410ee 100755 --- a/pay-api/tests/unit/models/__init__.py +++ b/pay-api/tests/unit/models/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_comment.py b/pay-api/tests/unit/models/test_comment.py index 8cd96b28d..26694f80b 100644 --- a/pay-api/tests/unit/models/test_comment.py +++ b/pay-api/tests/unit/models/test_comment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_corp_type.py b/pay-api/tests/unit/models/test_corp_type.py index 1a90df909..5541b42ce 100644 --- a/pay-api/tests/unit/models/test_corp_type.py +++ b/pay-api/tests/unit/models/test_corp_type.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_fee_code.py b/pay-api/tests/unit/models/test_fee_code.py index 82a75f1e3..d3bed9c4b 100644 --- a/pay-api/tests/unit/models/test_fee_code.py +++ b/pay-api/tests/unit/models/test_fee_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_fee_schedule.py b/pay-api/tests/unit/models/test_fee_schedule.py index 89d7b43fb..b23f934fe 100644 --- a/pay-api/tests/unit/models/test_fee_schedule.py +++ b/pay-api/tests/unit/models/test_fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_filing_type.py b/pay-api/tests/unit/models/test_filing_type.py index f5cc35831..608a77240 100644 --- a/pay-api/tests/unit/models/test_filing_type.py +++ b/pay-api/tests/unit/models/test_filing_type.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_invoice.py b/pay-api/tests/unit/models/test_invoice.py index 3b2e999c4..4878a3799 100644 --- a/pay-api/tests/unit/models/test_invoice.py +++ b/pay-api/tests/unit/models/test_invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_non_sufficient_funds.py b/pay-api/tests/unit/models/test_non_sufficient_funds.py index 8f88ce59f..6782ec2b1 100644 --- a/pay-api/tests/unit/models/test_non_sufficient_funds.py +++ b/pay-api/tests/unit/models/test_non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment.py b/pay-api/tests/unit/models/test_payment.py index fde0ebfe4..e3b474096 100644 --- a/pay-api/tests/unit/models/test_payment.py +++ b/pay-api/tests/unit/models/test_payment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_account.py b/pay-api/tests/unit/models/test_payment_account.py index 0e6b35724..9dd064c5e 100644 --- a/pay-api/tests/unit/models/test_payment_account.py +++ b/pay-api/tests/unit/models/test_payment_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_method.py b/pay-api/tests/unit/models/test_payment_method.py index e3042161e..5c6009315 100644 --- a/pay-api/tests/unit/models/test_payment_method.py +++ b/pay-api/tests/unit/models/test_payment_method.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_system.py b/pay-api/tests/unit/models/test_payment_system.py index e5095f2d9..7e12a68ff 100644 --- a/pay-api/tests/unit/models/test_payment_system.py +++ b/pay-api/tests/unit/models/test_payment_system.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_transaction.py b/pay-api/tests/unit/models/test_payment_transaction.py index 4ad738bef..b14788a11 100644 --- a/pay-api/tests/unit/models/test_payment_transaction.py +++ b/pay-api/tests/unit/models/test_payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_receipt.py b/pay-api/tests/unit/models/test_receipt.py index b04303dcb..9acf4b405 100644 --- a/pay-api/tests/unit/models/test_receipt.py +++ b/pay-api/tests/unit/models/test_receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_routing_slip.py b/pay-api/tests/unit/models/test_routing_slip.py index 68943e440..e4aff84b2 100644 --- a/pay-api/tests/unit/models/test_routing_slip.py +++ b/pay-api/tests/unit/models/test_routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_status_code.py b/pay-api/tests/unit/models/test_status_code.py index d67bf3b80..a85ec1ff2 100644 --- a/pay-api/tests/unit/models/test_status_code.py +++ b/pay-api/tests/unit/models/test_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/__init__.py b/pay-api/tests/unit/services/__init__.py index c467e0241..051177240 100755 --- a/pay-api/tests/unit/services/__init__.py +++ b/pay-api/tests/unit/services/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_auth.py b/pay-api/tests/unit/services/test_auth.py index d1ec39d9f..adba238fa 100644 --- a/pay-api/tests/unit/services/test_auth.py +++ b/pay-api/tests/unit/services/test_auth.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_bcol_service.py b/pay-api/tests/unit/services/test_bcol_service.py index 52cb3a044..c1f856950 100644 --- a/pay-api/tests/unit/services/test_bcol_service.py +++ b/pay-api/tests/unit/services/test_bcol_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_cfs_service.py b/pay-api/tests/unit/services/test_cfs_service.py index 94ab0d5b2..14c74377b 100644 --- a/pay-api/tests/unit/services/test_cfs_service.py +++ b/pay-api/tests/unit/services/test_cfs_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_code.py b/pay-api/tests/unit/services/test_code.py index 165838cb5..5232be98c 100644 --- a/pay-api/tests/unit/services/test_code.py +++ b/pay-api/tests/unit/services/test_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_comment.py b/pay-api/tests/unit/services/test_comment.py index 17beae748..e8bd43232 100644 --- a/pay-api/tests/unit/services/test_comment.py +++ b/pay-api/tests/unit/services/test_comment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_distribution_code.py b/pay-api/tests/unit/services/test_distribution_code.py index 26dd69c5c..d4bcad620 100644 --- a/pay-api/tests/unit/services/test_distribution_code.py +++ b/pay-api/tests/unit/services/test_distribution_code.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_eft_service.py b/pay-api/tests/unit/services/test_eft_service.py index b3382b78f..c236f925d 100644 --- a/pay-api/tests/unit/services/test_eft_service.py +++ b/pay-api/tests/unit/services/test_eft_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_fee_schedule.py b/pay-api/tests/unit/services/test_fee_schedule.py index a0a8307a9..aed929a34 100644 --- a/pay-api/tests/unit/services/test_fee_schedule.py +++ b/pay-api/tests/unit/services/test_fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_flags.py b/pay-api/tests/unit/services/test_flags.py index f1259206c..b59a11aba 100644 --- a/pay-api/tests/unit/services/test_flags.py +++ b/pay-api/tests/unit/services/test_flags.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_gcp_queue.py b/pay-api/tests/unit/services/test_gcp_queue.py index 6b293f1b5..8ded5facb 100644 --- a/pay-api/tests/unit/services/test_gcp_queue.py +++ b/pay-api/tests/unit/services/test_gcp_queue.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,29 +18,27 @@ """ from unittest.mock import ANY, MagicMock, patch +from dataclasses import asdict +from dotenv import load_dotenv +from gcp_queue.gcp_queue import GcpQueue +import humps import pytest -from flask import Flask -from pay_api.services.gcp_queue.gcp_queue import GcpQueue +from pay_api import create_app +from pay_api.services import gcp_queue_publisher +from pay_api.services.payment_transaction import PaymentToken from pay_api.services.gcp_queue_publisher import QueueMessage, publish_to_queue +from pay_api.utils.enums import TransactionStatus -@pytest.fixture(autouse=True) -def setup(): - """Initialize app with test env for testing.""" - global app - app = Flask(__name__) - app.env = 'testing' - - -@pytest.fixture(autouse=True) +@pytest.fixture() def mock_publisher_client(): """Mock the PublisherClient used in GcpQueue.""" with patch('google.cloud.pubsub_v1.PublisherClient') as publisher: yield publisher.return_value -@pytest.fixture(autouse=True) +@pytest.fixture() def mock_credentials(): """Mock Credentials.""" with patch('google.auth.jwt.Credentials') as mock: @@ -48,7 +46,7 @@ def mock_credentials(): yield mock -def test_publish_to_queue_success(): +def test_publish_to_queue_success(app, mock_credentials, mock_publisher_client): """Test publishing to GCP PubSub Queue successfully.""" with patch.object(GcpQueue, 'publish') as mock_publisher: with app.app_context(): @@ -63,17 +61,34 @@ def test_publish_to_queue_success(): mock_publisher.assert_called_once_with('projects/project-id/topics/topic', ANY) -def test_publish_to_queue_no_topic(): +def test_publish_to_queue_no_topic(app, mock_credentials, mock_publisher_client): """Test that publish_to_queue does not publish if no topic is set.""" with patch.object(GcpQueue, 'publish') as mock_publisher: - with patch.object(Flask, 'logger') as logger: - with app.app_context(): - queue_message = QueueMessage( - source='test-source', - message_type='test-message-type', - payload={'key': 'value'}, - topic=None - ) - publish_to_queue(queue_message) - mock_publisher.publish.assert_not_called() - logger.info.assert_called_once_with('Skipping queue message topic not set.') + with app.app_context(): + queue_message = QueueMessage( + source='test-source', + message_type='test-message-type', + payload={'key': 'value'}, + topic=None + ) + publish_to_queue(queue_message) + mock_publisher.publish.assert_not_called() + + +@pytest.mark.skip(reason='ADHOC only test.') +def test_gcp_pubsub_connectivity(monkeypatch): + """Test that a queue can publish to gcp pubsub.""" + # We don't want any of the monkeypatches by the fixtures. + monkeypatch.undo() + load_dotenv('.env') + app_prod = create_app('production') + payload = humps.camelize(asdict(PaymentToken(55, TransactionStatus.COMPLETED.value, 55, 'NRO'))) + with app_prod.app_context(): + gcp_queue_publisher.publish_to_queue( + QueueMessage(source='test', message_type='bc.registry.payment', payload=payload, + topic=app_prod.config.get('NAMEX_PAY_TOPIC')) + ) + gcp_queue_publisher.publish_to_queue( + QueueMessage(source='test', message_type='test', payload={'key': 'value'}, + topic=app_prod.config.get('ACCOUNT_MAILER_TOPIC')) + ) diff --git a/pay-api/tests/unit/services/test_hashing_service.py b/pay-api/tests/unit/services/test_hashing_service.py index 97a390115..b65b2defe 100644 --- a/pay-api/tests/unit/services/test_hashing_service.py +++ b/pay-api/tests/unit/services/test_hashing_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_invoice.py b/pay-api/tests/unit/services/test_invoice.py index c0030af6e..1c97a686e 100644 --- a/pay-api/tests/unit/services/test_invoice.py +++ b/pay-api/tests/unit/services/test_invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_invoice_reference.py b/pay-api/tests/unit/services/test_invoice_reference.py index 8d52fddda..3b34e3763 100644 --- a/pay-api/tests/unit/services/test_invoice_reference.py +++ b/pay-api/tests/unit/services/test_invoice_reference.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_non_sufficient_funds.py b/pay-api/tests/unit/services/test_non_sufficient_funds.py index 8f639fdd6..d10e33d31 100644 --- a/pay-api/tests/unit/services/test_non_sufficient_funds.py +++ b/pay-api/tests/unit/services/test_non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_oauth_service.py b/pay-api/tests/unit/services/test_oauth_service.py index e1a35f197..a49473ec8 100644 --- a/pay-api/tests/unit/services/test_oauth_service.py +++ b/pay-api/tests/unit/services/test_oauth_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_pad_service.py b/pay-api/tests/unit/services/test_pad_service.py index 276b49938..99cf83071 100644 --- a/pay-api/tests/unit/services/test_pad_service.py +++ b/pay-api/tests/unit/services/test_pad_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment.py b/pay-api/tests/unit/services/test_payment.py index dbde1d521..ecdce80d0 100644 --- a/pay-api/tests/unit/services/test_payment.py +++ b/pay-api/tests/unit/services/test_payment.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_account.py b/pay-api/tests/unit/services/test_payment_account.py index d6e465752..da1a626dd 100644 --- a/pay-api/tests/unit/services/test_payment_account.py +++ b/pay-api/tests/unit/services/test_payment_account.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_line_item.py b/pay-api/tests/unit/services/test_payment_line_item.py index c5157f259..db802f04c 100644 --- a/pay-api/tests/unit/services/test_payment_line_item.py +++ b/pay-api/tests/unit/services/test_payment_line_item.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_service.py b/pay-api/tests/unit/services/test_payment_service.py index 727842b62..be9483799 100644 --- a/pay-api/tests/unit/services/test_payment_service.py +++ b/pay-api/tests/unit/services/test_payment_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_system_service.py b/pay-api/tests/unit/services/test_payment_system_service.py index b36490955..619c34ca8 100644 --- a/pay-api/tests/unit/services/test_payment_system_service.py +++ b/pay-api/tests/unit/services/test_payment_system_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_transaction.py b/pay-api/tests/unit/services/test_payment_transaction.py index 52e9c3f22..2f6387be3 100644 --- a/pay-api/tests/unit/services/test_payment_transaction.py +++ b/pay-api/tests/unit/services/test_payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_receipt.py b/pay-api/tests/unit/services/test_receipt.py index 357aa0e35..688c9c8c0 100644 --- a/pay-api/tests/unit/services/test_receipt.py +++ b/pay-api/tests/unit/services/test_receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_refund.py b/pay-api/tests/unit/services/test_refund.py index d28e305e1..a4a06986f 100644 --- a/pay-api/tests/unit/services/test_refund.py +++ b/pay-api/tests/unit/services/test_refund.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_routing_slip_service.py b/pay-api/tests/unit/services/test_routing_slip_service.py index f62c8ae16..ff283b40c 100644 --- a/pay-api/tests/unit/services/test_routing_slip_service.py +++ b/pay-api/tests/unit/services/test_routing_slip_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_statement.py b/pay-api/tests/unit/services/test_statement.py index 99239f168..5700ed97f 100644 --- a/pay-api/tests/unit/services/test_statement.py +++ b/pay-api/tests/unit/services/test_statement.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_statement_settings.py b/pay-api/tests/unit/services/test_statement_settings.py index fdabd32cb..c3dd9378d 100644 --- a/pay-api/tests/unit/services/test_statement_settings.py +++ b/pay-api/tests/unit/services/test_statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_wire_service.py b/pay-api/tests/unit/services/test_wire_service.py index c7a5427a3..528234770 100644 --- a/pay-api/tests/unit/services/test_wire_service.py +++ b/pay-api/tests/unit/services/test_wire_service.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/__init__.py b/pay-api/tests/unit/utils/__init__.py index a41ec6419..16727833c 100755 --- a/pay-api/tests/unit/utils/__init__.py +++ b/pay-api/tests/unit/utils/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_error.py b/pay-api/tests/unit/utils/test_error.py index 1b93a07ac..f0dd7b189 100755 --- a/pay-api/tests/unit/utils/test_error.py +++ b/pay-api/tests/unit/utils/test_error.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_logging.py b/pay-api/tests/unit/utils/test_logging.py index 81f8de9cc..0f07a8843 100755 --- a/pay-api/tests/unit/utils/test_logging.py +++ b/pay-api/tests/unit/utils/test_logging.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_util.py b/pay-api/tests/unit/utils/test_util.py index f40af87a6..80aae8383 100644 --- a/pay-api/tests/unit/utils/test_util.py +++ b/pay-api/tests/unit/utils/test_util.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_util_cors.py b/pay-api/tests/unit/utils/test_util_cors.py index df4bbf050..fea00d6f8 100755 --- a/pay-api/tests/unit/utils/test_util_cors.py +++ b/pay-api/tests/unit/utils/test_util_cors.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/__init__.py b/pay-api/tests/utilities/__init__.py index eb431264d..af6a70494 100644 --- a/pay-api/tests/utilities/__init__.py +++ b/pay-api/tests/utilities/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/base_test.py b/pay-api/tests/utilities/base_test.py index 0c322bce6..adf30be69 100644 --- a/pay-api/tests/utilities/base_test.py +++ b/pay-api/tests/utilities/base_test.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/decorators.py b/pay-api/tests/utilities/decorators.py index 88deec93f..d6be57699 100644 --- a/pay-api/tests/utilities/decorators.py +++ b/pay-api/tests/utilities/decorators.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/schema_assertions.py b/pay-api/tests/utilities/schema_assertions.py index 24d3d1f8e..55cd2e6b3 100755 --- a/pay-api/tests/utilities/schema_assertions.py +++ b/pay-api/tests/utilities/schema_assertions.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/wsgi.py b/pay-api/wsgi.py index d584bdf8a..7c25417b6 100755 --- a/pay-api/wsgi.py +++ b/pay-api/wsgi.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/Makefile b/pay-queue/Makefile index 101a2de04..55b54a7d7 100644 --- a/pay-queue/Makefile +++ b/pay-queue/Makefile @@ -115,7 +115,7 @@ tag: push ## tag image ################################################################################# run: ## Run the project in local - . venv/bin/activate && python app.py + poetry run flask run -p 5001 ################################################################################# # Self Documenting Commands # diff --git a/pay-queue/README.md b/pay-queue/README.md index b98948f78..f2709a676 100755 --- a/pay-queue/README.md +++ b/pay-queue/README.md @@ -3,11 +3,11 @@ # Application Name -BC Registries Payment Reconciliation Queue +BC Registries Pay Queue ## Technology Stack Used * Python, Flask -* Postgres - SQLAlchemy, psycopg2-binary & alembic +* Postgres - SQLAlchemy, psycopg2-binary, alembic & Google PUB/SUB ## License diff --git a/pay-queue/app.py b/pay-queue/app.py index dd7bd16a7..a1f9fdbe5 100755 --- a/pay-queue/app.py +++ b/pay-queue/app.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -14,13 +14,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Provides the WSGI entry point for running the application.""" -import os +"""Initialize Flask app.""" +import os from pay_queue import create_app - app = create_app() if __name__ == '__main__': - server_port = os.environ.get('PORT', '8080') + server_port = os.environ.get('PORT', '5001') app.run(debug=False, port=server_port, host='0.0.0.0') diff --git a/pay-queue/devops/vaults.gcp.env b/pay-queue/devops/vaults.gcp.env index fa6f68780..ee10ddf5f 100644 --- a/pay-queue/devops/vaults.gcp.env +++ b/pay-queue/devops/vaults.gcp.env @@ -16,9 +16,8 @@ EFT_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/eft/EFT_INVOICE_PREF AUDIENCE="op://gcp-queue/$APP_ENV/base/AUDIENCE" GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/base/GCP_AUTH_KEY" PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" -PAY_SUB_AUDIENCE="op://gcp-queue/$APP_ENV/base/PAY_SUB_AUDIENCE" -VERIFY_PUBSUB_EMAIL="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_EMAIL" -VERIFY_PUBSUB_VIA_JWT="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_VIA_JWT" +PAY_AUDIENCE_SUB="op://gcp-queue/$APP_ENV/base/PAY_AUDIENCE_SUB" +VERIFY_PUBSUB_EMAILS="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_EMAILS" DEBUG_REQUEST="op://gcp-queue/$APP_ENV/base/DEBUG_REQUEST" ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" @@ -26,4 +25,3 @@ SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" LEGISLATIVE_TIMEZONE="op://relationship/$APP_ENV/pay-api/LEGISLATIVE_TIMEZONE" ACCOUNT_SECRET_KEY="op://relationship/$APP_ENV/pay-api/ACCOUNT_SECRET_KEY" DISABLE_EJV_ERROR_EMAIL="True" -DISABLE_PAD_SUCCESS_EMAIL="False" diff --git a/pay-queue/devops/vaults.json b/pay-queue/devops/vaults.json new file mode 100644 index 000000000..54912865c --- /dev/null +++ b/pay-queue/devops/vaults.json @@ -0,0 +1,58 @@ +[ + { + "vault": "shared", + "application": [ + "encryption-key" + ] + }, + { + "vault": "minio", + "application": [ + "base", + "pay-queue" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "base", + "account-events-listener", + "account-mailer", + "payment", + "pay-queue" + ] + }, + { + "vault": "payment-external-services", + "application": [ + "cfs" + ] + }, + { + "vault": "relationship", + "application": [ + "postgres-pay" + ] + }, + { + "vault": "sentry", + "application": [ + "relationship-api" + ] + }, + { + "vault": "launchdarkly", + "application": [ + "pay" + ] + }, + { + "vault": "gcp-queue", + "application": [ + "a083gt", + "authpay", + "gtksf3", + "topics" + ] + } +] diff --git a/pay-queue/logging.conf b/pay-queue/logging.conf deleted file mode 100644 index ded5cb81c..000000000 --- a/pay-queue/logging.conf +++ /dev/null @@ -1,34 +0,0 @@ -[loggers] -keys=root,api,asyncio - -[handlers] -keys=console - -[formatters] -keys=simple - -[logger_root] -level=DEBUG -handlers=console - -[logger_asyncio] -level=DEBUG -handlers=console -qualname=asyncio -propagate=0 - -[logger_api] -level=DEBUG -handlers=console -qualname=api -propagate=0 - -[handler_console] -class=StreamHandler -level=DEBUG -formatter=simple -args=(sys.stdout,) - -[formatter_simple] -format=%(asctime)s - %(name)s - %(levelname)s in %(module)s:%(filename)s:%(lineno)d - %(funcName)s: %(message)s -datefmt= diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index 28dfadd78..f9715fdb2 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -89,13 +89,13 @@ files = [ [[package]] name = "astroid" -version = "3.1.0" +version = "3.2.2" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, + {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, + {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, ] [[package]] @@ -119,13 +119,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.1.0" +version = "2.2.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false python-versions = ">=3.8" files = [ - {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, - {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, + {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, + {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, ] [package.dependencies] @@ -408,63 +408,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.0" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, - {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, - {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, - {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, - {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, - {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, - {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, - {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, - {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, - {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, - {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, - {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, - {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, - {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.extras] @@ -709,13 +709,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.1.0" +version = "2.3.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, - {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, + {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, + {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, ] [package.dependencies] @@ -737,25 +737,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask_jwt_oidc" -version = "0.5.0" -description = "Flask JWT OIDC" +name = "flask-jwt-oidc" +version = "0.7.0" +description = "Opinionated flask oidc client" optional = false -python-versions = "*" +python-versions = "^3.9" files = [] develop = false [package.dependencies] -cachelib = "*" -flask = "*" -python-jose = "*" -six = "*" +cachelib = "0.*" +Flask = ">=2" +python-jose = "^3.3.0" +six = "^1.16.0" [package.source] type = "git" -url = "https://github.com/thorwolpert/flask-jwt-oidc.git" +url = "https://github.com/seeker25/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "fc60d430a69e0d59e1d659d01aff78151ba498d0" +resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" [[package]] name = "flask-marshmallow" @@ -856,27 +856,49 @@ sqlalchemy = ">=2.0.16" [[package]] name = "freezegun" -version = "1.5.0" +version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.5.0-py3-none-any.whl", hash = "sha256:ec3f4ba030e34eb6cf7e1e257308aee2c60c3d038ff35996d7475760c9ff3719"}, - {file = "freezegun-1.5.0.tar.gz", hash = "sha256:200a64359b363aa3653d8aac289584078386c7c3da77339d257e46a01fb5c77c"}, + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, ] [package.dependencies] python-dateutil = ">=2.7" +[[package]] +name = "gcp-queue" +version = "0.3.0" +description = "" +optional = false +python-versions = "^3.9" +files = [] +develop = false + +[package.dependencies] +flask = ">=1" +google-auth = "^2.28.2" +google-cloud-pubsub = "^2.20.2" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} + +[package.source] +type = "git" +url = "https://github.com/seeker25/sbc-connect-common.git" +reference = "main" +resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" +subdirectory = "python/gcp-queue" + [[package]] name = "google-api-core" -version = "2.17.1" +version = "2.19.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, - {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, ] [package.dependencies] @@ -884,6 +906,7 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -894,13 +917,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.28.1" +version = "2.29.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, - {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, ] [package.dependencies] @@ -917,13 +940,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.20.0" +version = "2.21.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, - {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, ] [package.dependencies] @@ -1045,84 +1068,76 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.62.1" +version = "1.64.0" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, - {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, - {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, - {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, - {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, - {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, - {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, - {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, - {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, - {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, - {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, - {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, - {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, - {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, - {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, - {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, - {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, - {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, - {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, - {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, - {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, - {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, - {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, - {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.62.1)"] +protobuf = ["grpcio-tools (>=1.64.0)"] [[package]] name = "grpcio-status" -version = "1.62.1" +version = "1.62.2" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, - {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.1" +grpcio = ">=1.62.2" protobuf = ">=4.21.6" [[package]] @@ -1277,28 +1292,26 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "9.2.2" +version = "8.2.1" description = "LaunchDarkly SDK for Python" optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, - {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, + {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, + {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" -launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.26.0,<3" +urllib3 = ">=1.22.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] -test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1454,13 +1467,13 @@ files = [ [[package]] name = "minio" -version = "7.2.5" +version = "7.2.7" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = "*" files = [ - {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, - {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, + {file = "minio-7.2.7-py3-none-any.whl", hash = "sha256:59d1f255d852fe7104018db75b3bebbd987e538690e680f7c5de835e422de837"}, + {file = "minio-7.2.7.tar.gz", hash = "sha256:473d5d53d79f340f3cd632054d0c82d2f93177ce1af2eac34a235bea55708d98"}, ] [package.dependencies] @@ -1585,22 +1598,16 @@ dpath = "2.1.6" ecdsa = "0.18.0" expiringdict = "1.2.2" flask = "3.0.2" -flask-caching = "2.1.0" +flask-caching = "2.3.0" flask-cors = "4.0.0" -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} flask-marshmallow = "1.2.0" flask-migrate = "4.0.7" flask-moment = "1.0.5" flask-script = "2.0.6" flask-sqlalchemy = "3.1.1" -google-api-core = "2.17.1" -google-auth = "2.28.1" -google-cloud-pubsub = "2.20.0" -googleapis-common-protos = "1.63.0" +gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} greenlet = "3.0.3" -grpc-google-iam-v1 = "0.13.0" -grpcio = "1.62.1" -grpcio-status = "1.62.1" gunicorn = "21.2.0" holidays = "0.37" idna = "3.6" @@ -1609,7 +1616,7 @@ jaeger-client = "4.8.0" jinja2 = "3.1.3" jsonschema = "4.17.3" launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "9.2.2" +launchdarkly-server-sdk = "8.2.1" mako = "1.3.2" markupsafe = "2.1.5" marshmallow = "3.21.1" @@ -1635,7 +1642,6 @@ rsa = "4.9" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} semver = "3.0.2" sentry-sdk = "1.41.0" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} six = "1.16.0" sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} sqlalchemy = "2.0.28" @@ -1649,9 +1655,9 @@ werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/bcgov/sbc-pay.git" -reference = "feature-queue-python-upgrade" -resolved_reference = "031104546edc3c471f0d00bdbbf9c2025d468557" +url = "https://github.com/seeker25/sbc-pay.git" +reference = "sync-python-to-main" +resolved_reference = "1dae0d28e241a7bcdfc43dcaee1a36e35e77c31c" subdirectory = "pay-api" [[package]] @@ -1670,28 +1676,28 @@ flake8 = ">=5.0.0" [[package]] name = "pg8000" -version = "1.31.1" +version = "1.31.2" description = "PostgreSQL interface library" optional = false python-versions = ">=3.8" files = [ - {file = "pg8000-1.31.1-py3-none-any.whl", hash = "sha256:69aac9dba4114c9c8d0408232d54eaf7d06d271df7765caeed39960e057800e4"}, - {file = "pg8000-1.31.1.tar.gz", hash = "sha256:b11130d4c615dd3062ea8fed8143064a7978b7fe6d44f14b72261d43c8e27087"}, + {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, + {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, ] [package.dependencies] python-dateutil = ">=2.8.2" -scramp = ">=1.4.4" +scramp = ">=1.4.5" [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -1989,17 +1995,17 @@ files = [ [[package]] name = "pylint" -version = "3.1.0" +version = "3.2.2" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, + {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, + {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, ] [package.dependencies] -astroid = ">=3.1.0,<=3.2.0-dev0" +astroid = ">=3.2.2,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -2095,23 +2101,23 @@ files = [ [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" @@ -2281,7 +2287,7 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "8871ffcce8cc2232a5d7a3adb6103dfaf0d7689f" +resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" subdirectory = "python" [[package]] @@ -2359,19 +2365,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "69.5.1" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simple-cloudevent" @@ -2588,13 +2593,13 @@ twisted = ["twisted"] [[package]] name = "tomlkit" -version = "0.12.4" +version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] @@ -2665,4 +2670,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "f7e978de8d7c39e508ff106d26b6a9fedac7896203b705955ed5b3a5d481c775" +content-hash = "91f9fa21463afa27ba2b73d8efcd0b07bac434743792659ba832a588fc331d50" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml index 299044c9d..21af5378e 100644 --- a/pay-queue/pyproject.toml +++ b/pay-queue/pyproject.toml @@ -19,12 +19,9 @@ sqlalchemy = "^2.0.28" itsdangerous = "^2.1.2" jinja2 = "^3.1.3" protobuf = "4.25.3" -launchdarkly-server-sdk = "^9.2.2" +launchdarkly-server-sdk = "^8.2.1" cachecontrol = "^0.14.0" -sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/bcgov/sbc-pay.git", branch = "feature-queue-python-upgrade", subdirectory = "pay-api"} -flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", branch = "sync-python-to-main", subdirectory = "pay-api"} pg8000 = "^1.30.5" diff --git a/pay-queue/scripts/verify_license_headers.sh b/pay-queue/scripts/verify_license_headers.sh index 028b95c63..a160d3ac8 100755 --- a/pay-queue/scripts/verify_license_headers.sh +++ b/pay-queue/scripts/verify_license_headers.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ # limitations under the License. -COPYRIGHT="Copyright © 2019 Province of British Columbia" +COPYRIGHT="Copyright © 2024 Province of British Columbia" RET=0 for file in $(find $@ -not \( -path */venv -prune \) -not \( -path */migrations -prune \) -not \( -path */tests -prune \) -not \( -path */.egg* -prune \) -name \*.py) diff --git a/pay-queue/setup.cfg b/pay-queue/setup.cfg index a09beb534..a3d8c7414 100644 --- a/pay-queue/setup.cfg +++ b/pay-queue/setup.cfg @@ -1,6 +1,6 @@ [metadata] -name = pay-queue -url = https://github.com/bcgov/sbc-pay/pay-queue +name = pay_queue +url = https://github.com/bcgov/sbc-pay/ author = SBC Relationships team author_email = classifiers = @@ -22,7 +22,7 @@ include_package_data = True packages = find: [options.package_data] -reconciliations = +pay_queue = [wheel] universal = 1 @@ -34,6 +34,7 @@ universal = 1 test = pytest [flake8] +ignore = B902 exclude = .git,*migrations* max-line-length = 120 docstring-min-length=10 @@ -45,7 +46,7 @@ max_line_length = 120 ignore = E501 docstring-min-length=10 notes=FIXME,XXX # TODO is ignored -match_dir = src/reconciliations +match_dir = src/pay_queue ignored-modules=flask_sqlalchemy sqlalchemy per-file-ignores = diff --git a/pay-queue/setup.py b/pay-queue/setup.py index 31939e558..9bc1ac212 100644 --- a/pay-queue/setup.py +++ b/pay-queue/setup.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia. +# Copyright © 2024 Province of British Columbia. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ def read(filepath): REQUIREMENTS = read_requirements('requirements.txt') setup( - name="reconciliations", + name="pay_queue", version=version, author_email='', packages=find_packages('src'), diff --git a/pay-queue/src/pay_queue/__init__.py b/pay-queue/src/pay_queue/__init__.py index 01c2cf845..1fe3ebdcc 100644 --- a/pay-queue/src/pay_queue/__init__.py +++ b/pay-queue/src/pay_queue/__init__.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""The Reconciliations queue service. +"""The pay-queue service. The service worker for applying payments, receipts and account balance to payment system. """ @@ -23,21 +23,26 @@ from flask import Flask from pay_api.models import db from pay_api.services.flags import flags +from pay_api.services.gcp_queue import queue +from pay_api.utils.cache import cache +from pay_api.utils.logging import setup_logging from pay_api.utils.run_version import get_run_version from sentry_sdk.integrations.flask import FlaskIntegration -from pay_queue.config import CONFIGURATION +from pay_queue import config from pay_queue.version import __version__ from .resources import register_endpoints -from .services import queue + + +setup_logging(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')) -> Flask: """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.env = run_mode - app.config.from_object(CONFIGURATION[run_mode]) + app.config.from_object(config.CONFIGURATION[run_mode]) # Configure Sentry if dsn := app.config.get('SENTRY_DSN', None): @@ -48,10 +53,25 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')) -> Flask: send_default_pii=False, ) + queue.init_app(app) flags.init_app(app) db.init_app(app) - queue.init_app(app) register_endpoints(app) + build_cache(app) return app + + +def build_cache(app): + """Build cache.""" + cache.init_app(app) + with app.app_context(): + cache.clear() + if not app.config.get('TESTING', False): + try: + from pay_api.services.code import Code as CodeService # pylint: disable=import-outside-toplevel + CodeService.build_all_codes_cache() + except Exception as e: # NOQA pylint:disable=broad-except + app.logger.error('Error on caching ') + app.logger.error(e) diff --git a/pay-queue/src/pay_queue/config.py b/pay-queue/src/pay_queue/config.py index cbceb71f4..1a284c1a5 100644 --- a/pay-queue/src/pay_queue/config.py +++ b/pay-queue/src/pay_queue/config.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ def get_named_config(config_name: str = 'production'): return app_config -class _Config(): # pylint: disable=too-few-public-methods +class _Config(): # pylint: disable=too-few-public-methods,protected-access """Base class configuration that should set reasonable defaults. Used as the base for all the other configurations. @@ -99,19 +99,14 @@ class _Config(): # pylint: disable=too-few-public-methods # Disable EJV Error Email DISABLE_EJV_ERROR_EMAIL = os.getenv('DISABLE_EJV_ERROR_EMAIL', 'true').lower() == 'true' - # Disable PAD Success Email - Incase we need to reprocess records weeks/months later - DISABLE_PAD_SUCCESS_EMAIL = os.getenv('DISABLE_PAD_SUCCESS_EMAIL', 'false').lower() == 'true' - # GCP PubSub - AUDIENCE = os.getenv('AUDIENCE', None) - GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) - PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) - ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) - PAY_SUB_AUDIENCE = os.getenv('PAY_SUB_AUDIENCE', None) - VERIFY_PUBSUB_EMAIL = os.getenv('VERIFY_PUBSUB_EMAIL', None) - - VERIFY_PUBSUB_VIA_JWT = os.getenv('VERIFY_PUBSUB_VIA_JWT', 'true').lower() == 'true' - VERIFY_PUBSUB_VIA_JWT = os.getenv('DEBUG_REQUEST', 'true').lower() == 'true' + # PUB/SUB - PUB: account-mailer-dev, auth-event-dev, SUB to ftp-poller-payment-reconciliation-dev, business-events + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') + AUTH_EVENT_TOPIC = os.getenv('AUTH_EVENT_TOPIC', 'auth-event-dev') + GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) + # If blank in PUBSUB, this should match the https endpoint the subscription is pushing to. + PAY_AUDIENCE_SUB = os.getenv('PAY_AUDIENCE_SUB', None) + VERIFY_PUBSUB_EMAILS = f'{os.getenv("AUTHPAY_SERVICE_ACCOUNT")},{os.getenv("BUSINESS_SERVICE_ACCOUNT")}'.split(',') class DevConfig(_Config): # pylint: disable=too-few-public-methods @@ -157,11 +152,13 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods ACCOUNT_SECRET_KEY = os.getenv('ACCOUNT_SECRET_KEY', 'test') # Secrets for integration tests - TEST_GCP_PROJECT_NAME = 'abdefg-dev' + TEST_GCP_PROJECT_NAME = 'pay-queue-dev' # Needs to have ftp-poller-dev in it. TEST_GCP_TOPICS = ['account-mailer-dev', 'ftp-poller-dev', 'business-identifier-update-pay-dev'] TEST_PUSH_ENDPOINT_PORT = 5020 TEST_PUSH_ENDPOINT = os.getenv('TEST_PUSH_ENDPOINT', f'http://host.docker.internal:{str(TEST_PUSH_ENDPOINT_PORT)}/') + GCP_AUTH_KEY = None + DISABLE_EJV_ERROR_EMAIL = False class ProdConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/pay-queue/src/pay_queue/enums.py b/pay-queue/src/pay_queue/enums.py index cf16aabf2..f23c76288 100644 --- a/pay-queue/src/pay_queue/enums.py +++ b/pay-queue/src/pay_queue/enums.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/src/pay_queue/external/__init__.py b/pay-queue/src/pay_queue/external/__init__.py new file mode 100644 index 000000000..de7538dc3 --- /dev/null +++ b/pay-queue/src/pay_queue/external/__init__.py @@ -0,0 +1,14 @@ +# Copyright © 2024 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""External to gcp auth.""" diff --git a/pay-queue/src/pay_queue/external/gcp_auth.py b/pay-queue/src/pay_queue/external/gcp_auth.py index 31f575ac6..398b9ca5f 100644 --- a/pay-queue/src/pay_queue/external/gcp_auth.py +++ b/pay-queue/src/pay_queue/external/gcp_auth.py @@ -19,15 +19,16 @@ def verify_jwt(session): claims = id_token.verify_oauth2_token( jwt_token, Request(session=session), - audience=current_app.config.get('PAY_SUB_AUDIENCE') + audience=current_app.config.get('PAY_AUDIENCE_SUB') ) - # Check if the email is verified and matches the configured email - required_email = current_app.config.get('VERIFY_PUBSUB_EMAIL') - if not claims.get('email_verified') or claims.get('email') != required_email: + required_emails = current_app.config.get('VERIFY_PUBSUB_EMAILS') + if claims.get('email_verified') and claims.get('email') in required_emails: + return None + else: return 'Email not verified or does not match', 401 except Exception as e: + current_app.logger.info(f'Invalid token {e}') return f'Invalid token: {e}', 400 - return None def ensure_authorized_queue_user(f): @@ -35,12 +36,7 @@ def ensure_authorized_queue_user(f): @functools.wraps(f) def decorated_function(*args, **kwargs): # Use CacheControl to avoid re-fetching certificates for every request. - if current_app.config.get('DEBUG_REQUEST') is True: - current_app.logger.info(f'Headers: {request.headers}') - verifyJWT = current_app.config.get('VERIFY_PUBSUB_VIA_JWT', True) - current_app.logger.info(f'verifyJWT: {verifyJWT}') - if verifyJWT is True: - if message := verify_jwt(CacheControl(Session())): - abort(HTTPStatus.UNAUTHORIZED) + if verify_jwt(CacheControl(Session())): + abort(HTTPStatus.UNAUTHORIZED) return f(*args, **kwargs) return decorated_function diff --git a/pay-queue/src/pay_queue/minio.py b/pay-queue/src/pay_queue/minio.py index ad58ae8cd..d8e8f4eec 100644 --- a/pay-queue/src/pay_queue/minio.py +++ b/pay-queue/src/pay_queue/minio.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/src/pay_queue/resources/worker.py b/pay-queue/src/pay_queue/resources/worker.py index d429ec693..aee57b242 100644 --- a/pay-queue/src/pay_queue/resources/worker.py +++ b/pay-queue/src/pay_queue/resources/worker.py @@ -12,13 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. """Worker resource to handle incoming queue pushes from gcp.""" + +import dataclasses +import json from http import HTTPStatus -from flask import Blueprint, request -from pay_api.utils.enums import MessageType +from flask import Blueprint, current_app, request +from pay_api.services.gcp_queue_publisher import queue +from sbc_common_components.utils.enums import QueueMessageTypes from pay_queue.external.gcp_auth import ensure_authorized_queue_user -from pay_queue.services import queue, update_temporary_identifier +from pay_queue.services import update_temporary_identifier from pay_queue.services.cgi_reconciliations import reconcile_distributions from pay_queue.services.eft.eft_reconciliation import reconcile_eft_payments from pay_queue.services.payment_reconciliations import reconcile_payments @@ -31,22 +35,27 @@ @ensure_authorized_queue_user def worker(): """Worker to handle incoming queue pushes.""" - if not (ce := queue.get_simple_cloud_event(request)): - # Return a 200, so event is removed from the Queue + ce = queue.get_simple_cloud_event(request, wrapped=True) + if not ce: return {}, HTTPStatus.OK - match ce.type: - case MessageType.CAS_UPLOADED.value: + try: + current_app.logger.info('Event Message Received: %s ', json.dumps(dataclasses.asdict(ce))) + if ce.type == QueueMessageTypes.CAS_MESSAGE_TYPE.value: reconcile_payments(ce.data) - case MessageType.CGI_ACK_RECEIVED.value: + elif ce.type == QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value: reconcile_distributions(ce.data) - case MessageType.CGI_FEEDBACK_RECEIVED.value: + elif ce.type == QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value: reconcile_distributions(ce.data, is_feedback=True) - case MessageType.EFT_FILE_UPLOADED.value: + elif ce.type == QueueMessageTypes.EFT_FILE_UPLOADED.value: reconcile_eft_payments(ce.data) - case MessageType.INCORPORATION.value | MessageType.REGISTRATION.value: + elif ce.type in [QueueMessageTypes.INCORPORATION.value, QueueMessageTypes.REGISTRATION.value]: update_temporary_identifier(ce.data) - case _: + else: raise Exception('Invalid queue message type') # pylint: disable=broad-exception-raised - return {}, HTTPStatus.OK + return {}, HTTPStatus.OK + except Exception: # pylint: disable=broad-exception-caught + current_app.logger.error('Failed to process queue message: %s', HTTPStatus.INTERNAL_SERVER_ERROR) + # Optionally, return an error status code or message + return {}, HTTPStatus.OK diff --git a/pay-queue/src/pay_queue/services/__init__.py b/pay-queue/src/pay_queue/services/__init__.py index c86353543..45b466b91 100644 --- a/pay-queue/src/pay_queue/services/__init__.py +++ b/pay-queue/src/pay_queue/services/__init__.py @@ -33,9 +33,4 @@ # POSSIBILITY OF SUCH DAMAGE. """This module provides Queue type services.""" -from pay_api.services.gcp_queue import GcpQueue - from .identifier_updater import update_temporary_identifier - - -queue = GcpQueue() diff --git a/pay-queue/src/pay_queue/services/cgi_reconciliations.py b/pay-queue/src/pay_queue/services/cgi_reconciliations.py index 9926acb86..7ec308a8d 100644 --- a/pay-queue/src/pay_queue/services/cgi_reconciliations.py +++ b/pay-queue/src/pay_queue/services/cgi_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,8 +32,9 @@ from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.utils.enums import ( - DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, - PaymentStatus, PaymentSystem, QueueSources, RoutingSlipStatus) + DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, + PaymentSystem, QueueSources, RoutingSlipStatus) +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from pay_queue import config @@ -90,25 +91,46 @@ def _update_acknowledgement(msg: Dict[str, any]): def _update_feedback(msg: Dict[str, any]): # pylint:disable=too-many-locals, too-many-statements # Read the file and find records from the database, and update status. + file_name: str = msg.get('fileName') minio_location: str = msg.get('location') file = get_object(minio_location, file_name) content = file.data.decode('utf-8-sig') group_batches: List[str] = _group_batches(content) - has_errors, already_processed = _process_ejv_feedback(group_batches['EJV'], file_name) - if not already_processed: - has_errors = _process_ap_feedback(group_batches['AP']) or has_errors + if _is_processed_or_processing(group_batches['EJV'], file_name): + return + + has_errors = _process_ejv_feedback(group_batches['EJV']) + has_errors = _process_ap_feedback(group_batches['AP']) or has_errors - if has_errors and not APP_CONFIG.DISABLE_EJV_ERROR_EMAIL: - _publish_mailer_events(file_name, minio_location) - current_app.logger.info('> update_feedback') + if has_errors and not APP_CONFIG.DISABLE_EJV_ERROR_EMAIL: + _publish_mailer_events(file_name, minio_location) + current_app.logger.info('Feedback file processing completed.') -def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:disable=too-many-locals +def _is_processed_or_processing(group_batches, file_name) -> bool: + """Check to see if file has already been processed. Mark them as processing.""" + for group_batch in group_batches: + ejv_file: Optional[EjvFileModel] = None + for line in group_batch.splitlines(): + is_batch_group: bool = line[2:4] == 'BG' + if is_batch_group: + batch_number = int(line[15:24]) + ejv_file = EjvFileModel.find_by_id(batch_number) + if ejv_file.feedback_file_ref: + current_app.logger.info( + 'EJV file id %s with feedback file %s is already processing or has been processed. Skipping.', + batch_number, file_name) + return True + ejv_file.feedback_file_ref = file_name + ejv_file.save() + return False + + +def _process_ejv_feedback(group_batches) -> bool: # pylint:disable=too-many-locals """Process EJV Feedback contents.""" has_errors = False - already_processed = False for group_batch in group_batches: ejv_file: Optional[EjvFileModel] = None receipt_number: Optional[str] = None @@ -121,13 +143,6 @@ def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:disable=t if is_batch_group: batch_number = int(line[15:24]) ejv_file = EjvFileModel.find_by_id(batch_number) - if ejv_file.feedback_file_ref: - current_app.logger.info( - 'EJV file id %s with feedback file %s has already been processed, skipping.', - batch_number, file_name) - already_processed = True - return has_errors, already_processed - ejv_file.feedback_file_ref = file_name elif is_batch_header: return_code = line[7:11] return_message = line[11:161] @@ -155,7 +170,7 @@ def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:disable=t has_errors = _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number) db.session.commit() - return has_errors, already_processed + return has_errors def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number): # pylint:disable=too-many-locals @@ -251,7 +266,7 @@ def _fix_invoice_line(line): # Check for zeros within 300->315 range. Bump them over with spaces. if (zero_position := line[300:315].find('0')) > -1: spaces_to_insert = 15 - zero_position - return line[:300+zero_position] + (' ' * spaces_to_insert) + line[300+zero_position:] + return line[:300 + zero_position] + (' ' * spaces_to_insert) + line[300 + zero_position:] return line @@ -316,7 +331,7 @@ def _publish_mailer_events(file_name: str, minio_location: str): gcp_queue_publisher.publish_to_queue( QueueMessage( source=QueueSources.PAY_QUEUE.value, - message_type=MessageType.EJV_FAILED.value, + message_type=QueueMessageTypes.EJV_FAILED.value, payload=payload, topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') ) diff --git a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py index dd0774b1b..8ff86f2b9 100644 --- a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py +++ b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py @@ -64,8 +64,9 @@ def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-many-loc eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( EFTFileModel.file_ref == file_name).one_or_none() - if eft_file_model and eft_file_model.status_code == EFTProcessStatus.COMPLETED.value: - current_app.logger.info('File: %s already completed processing on %s.', file_name, eft_file_model.completed_on) + if eft_file_model and eft_file_model.status_code in \ + [EFTProcessStatus.IN_PROGRESS.value, EFTProcessStatus.COMPLETED.value]: + current_app.logger.info('File: %s already %s.', file_name, str(eft_file_model.status_code)) return # There is no existing EFT File record - instantiate one diff --git a/pay-queue/src/pay_queue/services/payment_reconciliations.py b/pay-queue/src/pay_queue/services/payment_reconciliations.py index de5cefeb7..82105187d 100644 --- a/pay-queue/src/pay_queue/services/payment_reconciliations.py +++ b/pay-queue/src/pay_queue/services/payment_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,9 +37,9 @@ from pay_api.services.non_sufficient_funds import NonSufficientFundsService from pay_api.services.payment_transaction import PaymentTransaction as PaymentTransactionService from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, MessageType, PaymentMethod, PaymentStatus, - QueueSources) + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, PaymentStatus, QueueSources) from pay_api.utils.util import get_topic_for_corp_type +from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from pay_queue import config @@ -191,12 +191,9 @@ def reconcile_payments(msg: Dict[str, any]): cas_settlement: CasSettlementModel = db.session.query(CasSettlementModel) \ .filter(CasSettlementModel.file_name == file_name).one_or_none() - if cas_settlement and not cas_settlement.processed_on: - current_app.logger.info('File: %s has attempted to be processed before.', file_name) - elif cas_settlement and cas_settlement.processed_on: - current_app.logger.info('File: %s already processed on: %s. Skipping file.', - file_name, cas_settlement.processed_on) - return + if cas_settlement: + current_app.logger.info('File: %s has been processed or processing in progress. Skipping file. ' + 'Removing this row will allow processing to be restarted.', file_name) else: current_app.logger.info('Creating cas_settlement record for file: %s', file_name) cas_settlement = _create_cas_settlement(file_name) @@ -277,15 +274,13 @@ def _process_consolidated_invoices(row): level='error') return _process_paid_invoices(inv_references, row) - if not APP_CONFIG.DISABLE_PAD_SUCCESS_EMAIL: - _publish_mailer_events(MessageType.PAD_PAYMENT_SUCCESS.value, payment_account, row) elif target_txn_status.lower() == Status.NOT_PAID.value.lower() \ or record_type in (RecordType.PADR.value, RecordType.PAYR.value): current_app.logger.info('NOT PAID. NSF identified.') # NSF Condition. Publish to account events for NSF. if _process_failed_payments(row): # Send mailer and account events to update status and send email notification - _publish_account_events(MessageType.NSF_LOCK_ACCOUNT.value, payment_account, row) + _publish_account_events(QueueMessageTypes.NSF_LOCK_ACCOUNT.value, payment_account, row) else: current_app.logger.error('Target Transaction Type is received as %s for PAD, and cannot process %s.', target_txn, row) @@ -593,7 +588,7 @@ def _publish_payment_event(inv: InvoiceModel): gcp_queue_publisher.publish_to_queue( QueueMessage( source=QueueSources.PAY_QUEUE.value, - message_type=MessageType.PAYMENT.value, + message_type=QueueMessageTypes.PAYMENT.value, payload=payload, topic=get_topic_for_corp_type(inv.corp_type_code) ) @@ -638,13 +633,13 @@ def _publish_online_banking_mailer_events(rows: List[Dict[str, str]], paid_amoun credit_amount: float = 0 if credit_rows: - message_type = 'bc.registry.payment.OverPaid' + message_type = QueueMessageTypes.ONLINE_BANKING_OVER_PAYMENT.value for row in credit_rows: credit_amount += float(_get_row_value(row, Column.APP_AMOUNT)) elif under_pay_rows: - message_type = 'bc.registry.payment.UnderPaid' + message_type = QueueMessageTypes.ONLINE_BANKING_UNDER_PAYMENT.value else: - message_type = 'bc.registry.payment.Payment' + message_type = QueueMessageTypes.ONLINE_BANKING_PAYMENT.value payload = { 'accountId': pay_account.auth_account_id, @@ -681,7 +676,7 @@ def _publish_account_events(message_type: str, pay_account: PaymentAccountModel, source=QueueSources.PAY_QUEUE.value, message_type=message_type, payload=payload, - topic=current_app.config.get('AUTH_QUEUE_TOPIC') + topic=current_app.config.get('AUTH_EVENT_TOPIC') ) ) except Exception as e: # NOQA pylint: disable=broad-except diff --git a/pay-queue/src/pay_queue/version.py b/pay-queue/src/pay_queue/version.py index 3b723bf04..c29b19b02 100644 --- a/pay-queue/src/pay_queue/version.py +++ b/pay-queue/src/pay_queue/version.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/__init__.py b/pay-queue/tests/__init__.py index 3e44a42f5..b2425e245 100644 --- a/pay-queue/tests/__init__.py +++ b/pay-queue/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/conftest.py b/pay-queue/tests/conftest.py index c691641c5..8c5fdebba 100644 --- a/pay-queue/tests/conftest.py +++ b/pay-queue/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ # limitations under the License. """Common setup and fixtures for the pytest suite used by this service.""" import os +from concurrent.futures import CancelledError import pytest from flask_migrate import Migrate, upgrade @@ -30,9 +31,6 @@ def app(): """Return a session-wide application configured in TEST mode.""" _app = create_app('testing') - _app.config['GCP_AUTH_KEY'] = 'xxxxx' - _app.config['AUDIENCE'] = 'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber' - _app.config['PUBLISHER_AUDIENCE'] = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher' return _app @@ -145,7 +143,7 @@ def initialize_pubsub(app): except NotFound: pass publisher.create_topic(name=topic_path) - subscription_path = subscriber.subscription_path(project, f'{topic}_subscription') + subscription_path = subscriber.subscription_path(project, f'{topic}_subscription') try: subscriber.delete_subscription(subscription=subscription_path) except NotFound: @@ -157,3 +155,19 @@ def initialize_pubsub(app): 'push_config': push_config, } ) + + +@pytest.fixture(autouse=True) +def mock_pub_sub_call(mocker): + """Mock pub sub call.""" + class PublisherMock: + """Publisher Mock.""" + + def __init__(self, *args, **kwargs): + pass + + def publish(self, *args, **kwargs): + """Publish mock.""" + raise CancelledError('This is a mock') + + mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) diff --git a/pay-queue/tests/integration/__init__.py b/pay-queue/tests/integration/__init__.py index 44812fe3c..d95cabaff 100644 --- a/pay-queue/tests/integration/__init__.py +++ b/pay-queue/tests/integration/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/integration/factory.py b/pay-queue/tests/integration/factory.py index e7ccc4bdd..744cb377c 100644 --- a/pay-queue/tests/integration/factory.py +++ b/pay-queue/tests/integration/factory.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/integration/test_cgi_reconciliations.py b/pay-queue/tests/integration/test_cgi_reconciliations.py index f308d9f46..19ac6a16e 100644 --- a/pay-queue/tests/integration/test_cgi_reconciliations.py +++ b/pay-queue/tests/integration/test_cgi_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -33,8 +33,9 @@ from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db from pay_api.utils.enums import ( - CfsAccountStatus, DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, MessageType, + CfsAccountStatus, DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, RoutingSlipStatus) +from sbc_common_components.utils.enums import QueueMessageTypes from tests.integration.utils import add_file_event_to_queue_and_process @@ -44,7 +45,7 @@ from .utils import upload_to_minio -def test_successful_partner_ejv_reconciliations(client): +def test_successful_partner_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -107,7 +108,7 @@ def test_successful_partner_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -149,7 +150,7 @@ def test_successful_partner_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -158,7 +159,7 @@ def test_successful_partner_ejv_reconciliations(client): assert invoice.disbursement_status_code == DisbursementStatus.COMPLETED.value -def test_failed_partner_ejv_reconciliations(client): +def test_failed_partner_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -223,7 +224,7 @@ def test_failed_partner_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -265,7 +266,7 @@ def test_failed_partner_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -276,7 +277,7 @@ def test_failed_partner_ejv_reconciliations(client): assert disbursement_distribution_code.stop_ejv -def test_successful_partner_reversal_ejv_reconciliations(client): +def test_successful_partner_reversal_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -342,7 +343,7 @@ def test_successful_partner_reversal_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -384,7 +385,7 @@ def test_successful_partner_reversal_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -394,7 +395,7 @@ def test_successful_partner_reversal_ejv_reconciliations(client): assert invoice.disbursement_date == datetime(2023, 5, 29) -def test_succesful_payment_ejv_reconciliations(client): +def test_succesful_payment_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create EJV payment accounts # 2. Create invoice and related records @@ -461,10 +462,9 @@ def test_succesful_payment_ejv_reconciliations(client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() - EjvLinkModel( - link_id=inv.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value - ).save() + EjvLinkModel(link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) service_fee_amount = f'{line.service_fees:.2f}'.zfill(15) @@ -511,7 +511,7 @@ def test_succesful_payment_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -527,7 +527,7 @@ def test_succesful_payment_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -555,7 +555,7 @@ def test_succesful_payment_ejv_reconciliations(client): assert payment[0][0].paid_amount == inv_total_amount -def test_succesful_payment_reversal_ejv_reconciliations(client): +def test_succesful_payment_reversal_ejv_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create EJV payment accounts # 2. Create invoice and related records @@ -621,8 +621,8 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() EjvLinkModel( - link_id=inv.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) @@ -669,7 +669,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -685,7 +685,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -711,7 +711,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(client): assert payment[0][0].paid_amount == inv_total_amount -def test_successful_refund_reconciliations(client): +def test_successful_refund_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create a routing slip. # 2. Mark the routing slip for refund. @@ -759,7 +759,7 @@ def test_successful_refund_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -834,7 +834,7 @@ def test_successful_refund_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -844,7 +844,7 @@ def test_successful_refund_reconciliations(client): assert routing_slip.status == RoutingSlipStatus.REFUND_COMPLETED.value -def test_failed_refund_reconciliations(client): +def test_failed_refund_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create a routing slip. # 2. Mark the routing slip for refund. @@ -892,7 +892,7 @@ def test_failed_refund_reconciliations(client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -968,7 +968,7 @@ def test_failed_refund_reconciliations(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -980,7 +980,7 @@ def test_failed_refund_reconciliations(client): assert routing_slip_2.status == RoutingSlipStatus.REFUND_REJECTED.value -def test_prevent_duplicate_ack(client): +def test_prevent_duplicate_ack(session, app, client): """Assert processing completes when existing ack.""" file_ref = f'INBOX.{datetime.now()}' # Upload an acknowledgement file @@ -994,18 +994,18 @@ def test_prevent_duplicate_ack(client): jv_file.write('') jv_file.close() - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value # Nothing should change, because it's already processed this ACK. ejv.disbursement_status_code = DisbursementStatus.UPLOADED.value - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.UPLOADED.value -def test_successful_ap_disbursement(client): +def test_successful_ap_disbursement(session, app, client): """Test Reconciliations worker for ap disbursement.""" # 1. Create invoice. # 2. Create a AP reconciliation file. @@ -1048,12 +1048,12 @@ def test_successful_ap_disbursement(client): ejv_file_id=ejv_file.id, payment_account_id=account.id).save() EjvLinkModel( - link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() EjvLinkModel( - link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, + link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() @@ -1065,7 +1065,7 @@ def test_successful_ap_disbursement(client): upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1138,7 +1138,7 @@ def test_successful_ap_disbursement(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value @@ -1154,7 +1154,7 @@ def test_successful_ap_disbursement(client): assert refund.gl_posted is not None -def test_failure_ap_disbursement(client): +def test_failure_ap_disbursement(session, app, client): """Test Reconciliations worker for ap disbursement.""" # 1. Create invoice. # 2. Create a AP reconciliation file. @@ -1212,7 +1212,7 @@ def test_failure_ap_disbursement(client): upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1288,7 +1288,7 @@ def test_failure_ap_disbursement(client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) + add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value diff --git a/pay-queue/tests/integration/test_eft_reconciliation.py b/pay-queue/tests/integration/test_eft_reconciliation.py index 114ae89e2..97fb9520c 100644 --- a/pay-queue/tests/integration/test_eft_reconciliation.py +++ b/pay-queue/tests/integration/test_eft_reconciliation.py @@ -28,7 +28,8 @@ from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, MessageType, PaymentMethod +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, PaymentMethod +from sbc_common_components.utils.enums import QueueMessageTypes from pay_queue.services.eft.eft_enums import EFTConstants from tests.integration.factory import factory_create_eft_account, factory_invoice @@ -36,7 +37,7 @@ from tests.utilities.factory_utils import factory_eft_header, factory_eft_record, factory_eft_trailer -def test_eft_tdi17_fail_header(client): +def test_eft_tdi17_fail_header(session, app, client): """Test EFT Reconciliations properly fails for a bad EFT header.""" # Generate file with invalid header file_name: str = 'test_eft_tdi17.txt' @@ -45,7 +46,7 @@ def test_eft_tdi17_fail_header(client): create_and_upload_eft_file(file_name, [header]) - add_file_event_to_queue_and_process(client, file_name, MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name, QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -88,7 +89,7 @@ def test_eft_tdi17_fail_header(client): assert not bool(eft_transactions) -def test_eft_tdi17_fail_trailer(client): +def test_eft_tdi17_fail_trailer(session, app, client): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' @@ -99,7 +100,9 @@ def test_eft_tdi17_fail_trailer(client): create_and_upload_eft_file(file_name, [header, trailer]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -142,7 +145,7 @@ def test_eft_tdi17_fail_trailer(client): assert not bool(eft_transactions) -def test_eft_tdi17_fail_transactions(client): +def test_eft_tdi17_fail_transactions(session, app, client): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' @@ -161,7 +164,9 @@ def test_eft_tdi17_fail_transactions(client): create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -199,13 +204,15 @@ def test_eft_tdi17_fail_transactions(client): assert eft_transactions[0].error_messages[0] == 'Invalid transaction deposit amount CAD.' -def test_eft_tdi17_basic_process(client): +def test_eft_tdi17_basic_process(session, app, client): """Test EFT Reconciliations worker is able to create basic EFT processing records.""" # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_basic_tdi17_file(file_name) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -281,14 +288,20 @@ def test_eft_tdi17_basic_process(client): assert not eft_credit_invoice_links -def test_eft_tdi17_process(client): +def test_eft_tdi17_process(session, app, client): """Test EFT Reconciliations worker.""" payment_account, eft_shortname, invoice = create_test_data() + + assert payment_account is not None + assert eft_shortname is not None + assert invoice is not None # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_tdi17_file(file_name) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -343,7 +356,6 @@ def test_eft_tdi17_process(client): # NOTE THIS NEEDS TO BE RE-WRITTEN INSIDE OF THE JOB. # today = datetime.now().date() - # # Assert Invoice is paid # invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) # expected_amount = 100 @@ -410,7 +422,7 @@ def test_eft_tdi17_process(client): # assert eft_credit_invoice_links[0].invoice_id == invoice.id -def test_eft_tdi17_rerun(client): +def test_eft_tdi17_rerun(session, app, client): """Test EFT Reconciliations can be re-executed with a corrected file.""" payment_account, eft_shortname, invoice = create_test_data() @@ -431,7 +443,9 @@ def test_eft_tdi17_rerun(client): create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -470,7 +484,9 @@ def test_eft_tdi17_rerun(client): jv_number='002425669', transaction_date='') create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) # Check file is completed after correction eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( diff --git a/pay-queue/tests/integration/test_payment_reconciliations.py b/pay-queue/tests/integration/test_payment_reconciliations.py index 8e6850413..d63a3d4aa 100644 --- a/pay-queue/tests/integration/test_payment_reconciliations.py +++ b/pay-queue/tests/integration/test_payment_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,8 +26,8 @@ from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus) +from pay_api.utils.enums import CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus +from sbc_common_components.utils.enums import QueueMessageTypes from pay_queue.enums import RecordType, SourceTransaction, Status, TargetTransaction @@ -37,7 +37,7 @@ from .utils import add_file_event_to_queue_and_process, create_and_upload_settlement_file -def test_online_banking_reconciliations(client): +def test_online_banking_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -67,7 +67,9 @@ def test_online_banking_reconciliations(client): TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -81,7 +83,7 @@ def test_online_banking_reconciliations(client): assert payment.invoice_number == invoice_number -def test_online_banking_reconciliations_over_payment(client): +def test_online_banking_reconciliations_over_payment(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -113,7 +115,9 @@ def test_online_banking_reconciliations_over_payment(client): over_payment_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, over_payment_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -127,7 +131,7 @@ def test_online_banking_reconciliations_over_payment(client): assert payment.invoice_number is None # No invoice_number if payment is not for 1 invoice -def test_online_banking_reconciliations_with_credit(client): +def test_online_banking_reconciliations_with_credit(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -159,7 +163,9 @@ def test_online_banking_reconciliations_with_credit(client): credit_row = [RecordType.ONAC.value, SourceTransaction.EFT_WIRE.value, '555566677', 100001, date, credit_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -173,7 +179,7 @@ def test_online_banking_reconciliations_with_credit(client): assert payment.invoice_number == invoice_number -def test_online_banking_reconciliations_overflows_credit(client): +def test_online_banking_reconciliations_overflows_credit(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -210,7 +216,9 @@ def test_online_banking_reconciliations_overflows_credit(client): Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row, onac_row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -224,7 +232,7 @@ def test_online_banking_reconciliations_overflows_credit(client): assert payment.invoice_number is None -def test_online_banking_under_payment(client): +def test_online_banking_under_payment(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -256,7 +264,9 @@ def test_online_banking_under_payment(client): TargetTransaction.INV.value, invoice_number, total, total - paid_amount, Status.PARTIAL.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id) @@ -271,7 +281,7 @@ def test_online_banking_under_payment(client): assert payment.invoice_number == invoice_number -def test_pad_reconciliations(client): +def test_pad_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoices and related records @@ -313,7 +323,9 @@ def test_pad_reconciliations(client): 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -335,7 +347,7 @@ def test_pad_reconciliations(client): assert rcpt1.receipt_date == rcpt2.receipt_date -def test_pad_reconciliations_with_credit_memo(client): +def test_pad_reconciliations_with_credit_memo(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoices and related records @@ -381,7 +393,9 @@ def test_pad_reconciliations_with_credit_memo(client): pad_row = [RecordType.PAD.value, SourceTransaction.PAD.value, receipt_number, 100001, date, total - credit_amount, cfs_account_number, 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [credit_row, pad_row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -403,7 +417,7 @@ def test_pad_reconciliations_with_credit_memo(client): assert rcpt1.receipt_date == rcpt2.receipt_date -def test_pad_nsf_reconciliations(client): +def test_pad_nsf_reconciliations(session, app, client): """Test Reconciliations worker for NSF.""" # 1. Create payment account # 2. Create invoices and related records @@ -446,7 +460,9 @@ def test_pad_nsf_reconciliations(client): 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -465,7 +481,7 @@ def test_pad_nsf_reconciliations(client): assert cfs_account.status == CfsAccountStatus.FREEZE.value -def test_pad_reversal_reconciliations(client): +def test_pad_reversal_reconciliations(session, app, client): """Test Reconciliations worker for NSF.""" # 1. Create payment account # 2. Create invoices and related records for a completed payment @@ -517,7 +533,9 @@ def test_pad_reversal_reconciliations(client): 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -541,7 +559,7 @@ def test_pad_reversal_reconciliations(client): @pytest.mark.asyncio -async def test_eft_wire_reconciliations(client): +async def test_eft_wire_reconciliations(session, app, client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -582,7 +600,9 @@ async def test_eft_wire_reconciliations(client): row = [RecordType.EFTP.value, SourceTransaction.EFT_WIRE.value, eft_wire_receipt, 100001, date, total, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -595,7 +615,7 @@ async def test_eft_wire_reconciliations(client): @pytest.mark.asyncio -async def test_credits(client, monkeypatch): +async def test_credits(session, app, client, monkeypatch): """Test Reconciliations worker.""" # 1. Create payment account. # 2. Create EFT/WIRE payment db record. @@ -657,7 +677,9 @@ def mock_cms(cfs_account: CfsAccountModel, cfs_account_number, TargetTransaction.RECEIPT.value, eft_wire_receipt, onac_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) + add_file_event_to_queue_and_process(client, + file_name=file_name, + message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) # Look up credit file and make sure the credits are recorded. pay_account = PaymentAccountModel.find_by_id(pay_account_id) diff --git a/pay-queue/tests/integration/test_worker_queue.py b/pay-queue/tests/integration/test_worker_queue.py index 932aa48bd..79d1002e2 100644 --- a/pay-queue/tests/integration/test_worker_queue.py +++ b/pay-queue/tests/integration/test_worker_queue.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ from .utils import helper_add_identifier_event_to_queue -def test_update_payment(client): +def test_update_payment(session, app, client): """Assert that the update internal payment records works.""" # vars old_identifier = 'T000000000' diff --git a/pay-queue/tests/integration/utils.py b/pay-queue/tests/integration/utils.py index 58d190d4b..07b9e7c7e 100644 --- a/pay-queue/tests/integration/utils.py +++ b/pay-queue/tests/integration/utils.py @@ -1,4 +1,4 @@ -# Copyright © 2019 Province of British Columbia +# Copyright © 2024 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,7 +27,8 @@ from minio import Minio from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage -from pay_api.utils.enums import MessageType, QueueSources +from pay_api.utils.enums import QueueSources +from sbc_common_components.utils.enums import QueueMessageTypes from simple_cloudevent import SimpleCloudEvent, to_queue_message @@ -98,7 +99,7 @@ def upload_to_minio(value_as_bytes, file_name: str): os.stat(file_name).st_size) -def forward_incoming_message_to_test_instance(client): +def forward_incoming_message_to_test_instance(session, app, client): """Forward incoming http message to test instance.""" # Note this is a bit different than how the queue could behave, it could send multiples. # This just receives one HTTP request and forwards it to the test instance. @@ -122,7 +123,7 @@ def forward_incoming_message_to_test_instance(client): assert tries > 0 -def add_file_event_to_queue_and_process(client, file_name: str, message_type: str, use_pubsub_emulator=True): +def add_file_event_to_queue_and_process(client, file_name: str, message_type: str, use_pubsub_emulator=False): """Add event to the Queue.""" queue_payload = { 'fileName': file_name, @@ -146,7 +147,7 @@ def add_file_event_to_queue_and_process(client, file_name: str, message_type: st def helper_add_identifier_event_to_queue(client, old_identifier: str = 'T1234567890', new_identifier: str = 'BC1234567890'): """Add event to the Queue.""" - message_type = MessageType.INCORPORATION.value + message_type = QueueMessageTypes.INCORPORATION.value queue_payload = { 'filing': { 'header': {'filingId': '12345678'}, diff --git a/report-api/requirements.txt b/report-api/requirements.txt index 3956cf9ad..4e81b8787 100644 --- a/report-api/requirements.txt +++ b/report-api/requirements.txt @@ -21,7 +21,7 @@ flask-restx==1.1.0 fonttools==4.43.0 gunicorn==20.1.0 html5lib==1.1 -idna==3.4 +idna==3.7 importlib-resources==5.12.0 itsdangerous==2.0.1 jaeger-client==4.8.0 From d235277c4b1481acc84e2f8d080f40addf1cb41a Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 09:07:50 -0700 Subject: [PATCH 71/87] Revert "21464 - Resync EFT branch with main (#1547)" (#1550) This reverts commit 23fed0f2445acf1c3fddbb6e412ad5ea92f4858a. --- .github/workflows/pay-queue-cd.yml | 118 +--- .github/workflows/pay-queue-ci.yml | 1 + .github/workflows/pay-queue-gcp-cd.yml | 33 - .gitignore | 3 - bcol-api/requirements.txt | 2 +- jobs/ftp-poller/Makefile | 2 +- jobs/ftp-poller/config.py | 8 +- jobs/ftp-poller/devops/vaults.json | 7 - jobs/ftp-poller/invoke_jobs.py | 4 +- .../openshift/ftp-poller-build.json | 139 ++++ .../openshift/ftp-poller-deploy.json | 417 ++++++++++++ jobs/ftp-poller/poetry.lock | 622 ++++++++---------- jobs/ftp-poller/pyproject.toml | 6 +- .../tasks/cgi_feeder_poller_task.py | 6 +- jobs/ftp-poller/tasks/eft_poller_ftp.py | 4 +- jobs/ftp-poller/tests/jobs/test_sftp.py | 15 +- jobs/ftp-poller/utils/utils.py | 14 +- jobs/notebook-report/requirements.txt | 2 +- jobs/notebook-report/requirements/prod.txt | 4 +- jobs/payment-jobs/config.py | 6 - jobs/payment-jobs/devops/vaults.json | 70 -- jobs/payment-jobs/invoke_jobs.py | 2 - jobs/payment-jobs/poetry.lock | 101 ++- jobs/payment-jobs/pyproject.toml | 7 +- .../tasks/activate_pad_account_task.py | 5 +- jobs/payment-jobs/tasks/ap_task.py | 4 +- .../tasks/cfs_create_account_task.py | 5 +- .../tasks/cfs_create_invoice_task.py | 8 +- .../tasks/unpaid_invoice_notify_task.py | 6 +- jobs/payment-jobs/tests/jobs/conftest.py | 24 - .../tests/jobs/test_statement_due_task.py | 1 - jobs/payment-jobs/utils/mailer.py | 28 +- pay-admin/requirements.txt | 34 + pay-api/devops/vaults.json | 59 -- pay-api/gunicorn_config.py | 2 +- pay-api/manage.py | 2 +- pay-api/poetry.lock | 405 ++++++------ pay-api/pyproject.toml | 15 +- pay-api/scripts/verify_license_headers.sh | 4 +- pay-api/setup.cfg | 2 +- pay-api/setup.py | 2 +- pay-api/src/pay_api/__init__.py | 3 +- pay-api/src/pay_api/config.py | 16 +- pay-api/src/pay_api/exceptions/__init__.py | 2 +- pay-api/src/pay_api/factory/__init__.py | 2 +- .../pay_api/factory/payment_system_factory.py | 2 +- pay-api/src/pay_api/models/__init__.py | 2 +- pay-api/src/pay_api/models/account_fee.py | 2 +- pay-api/src/pay_api/models/audit.py | 2 +- pay-api/src/pay_api/models/base_model.py | 2 +- pay-api/src/pay_api/models/base_schema.py | 2 +- pay-api/src/pay_api/models/cfs_account.py | 2 +- .../pay_api/models/cfs_account_status_code.py | 2 +- pay-api/src/pay_api/models/code_table.py | 2 +- pay-api/src/pay_api/models/comment.py | 2 +- pay-api/src/pay_api/models/corp_type.py | 2 +- pay-api/src/pay_api/models/credit.py | 2 +- pay-api/src/pay_api/models/db.py | 2 +- .../models/disbursement_status_code.py | 2 +- pay-api/src/pay_api/models/ejv_file.py | 2 +- pay-api/src/pay_api/models/ejv_header.py | 2 +- pay-api/src/pay_api/models/ejv_link.py | 2 +- pay-api/src/pay_api/models/error_code.py | 2 +- pay-api/src/pay_api/models/fee_code.py | 2 +- pay-api/src/pay_api/models/fee_schedule.py | 2 +- pay-api/src/pay_api/models/filing_type.py | 2 +- pay-api/src/pay_api/models/invoice.py | 2 +- pay-api/src/pay_api/models/invoice_batch.py | 2 +- .../src/pay_api/models/invoice_batch_link.py | 2 +- .../src/pay_api/models/invoice_reference.py | 2 +- .../models/invoice_reference_status_code.py | 2 +- .../src/pay_api/models/invoice_status_code.py | 2 +- .../pay_api/models/line_item_status_code.py | 2 +- .../pay_api/models/non_sufficient_funds.py | 2 +- .../models/notification_status_code.py | 2 +- pay-api/src/pay_api/models/payment.py | 2 +- pay-api/src/pay_api/models/payment_account.py | 2 +- .../src/pay_api/models/payment_line_item.py | 2 +- pay-api/src/pay_api/models/payment_method.py | 2 +- .../src/pay_api/models/payment_status_code.py | 2 +- pay-api/src/pay_api/models/payment_system.py | 2 +- .../src/pay_api/models/payment_transaction.py | 2 +- pay-api/src/pay_api/models/receipt.py | 2 +- pay-api/src/pay_api/models/refund.py | 2 +- pay-api/src/pay_api/models/routing_slip.py | 2 +- .../models/routing_slip_status_code.py | 2 +- pay-api/src/pay_api/models/statement.py | 2 +- .../src/pay_api/models/statement_invoices.py | 2 +- .../pay_api/models/statement_recipients.py | 2 +- .../src/pay_api/models/statement_settings.py | 2 +- .../pay_api/models/transaction_status_code.py | 2 +- pay-api/src/pay_api/resources/__init__.py | 2 +- pay-api/src/pay_api/resources/ops.py | 2 +- pay-api/src/pay_api/resources/v1/account.py | 2 +- .../resources/v1/account_statements.py | 2 +- .../v1/account_statements_notifications.py | 2 +- .../v1/account_statements_settings.py | 2 +- .../src/pay_api/resources/v1/bank_accounts.py | 2 +- pay-api/src/pay_api/resources/v1/code.py | 2 +- .../src/pay_api/resources/v1/distributions.py | 2 +- .../src/pay_api/resources/v1/fas/__init__.py | 2 +- .../src/pay_api/resources/v1/fas/refund.py | 2 +- .../pay_api/resources/v1/fas/routing_slip.py | 3 +- pay-api/src/pay_api/resources/v1/fee.py | 2 +- .../src/pay_api/resources/v1/fee_schedule.py | 2 +- pay-api/src/pay_api/resources/v1/invoice.py | 2 +- .../pay_api/resources/v1/invoice_receipt.py | 2 +- pay-api/src/pay_api/resources/v1/invoices.py | 2 +- pay-api/src/pay_api/resources/v1/meta.py | 2 +- .../resources/v1/non_sufficient_funds.py | 2 +- pay-api/src/pay_api/resources/v1/payment.py | 3 +- pay-api/src/pay_api/resources/v1/refund.py | 2 +- .../src/pay_api/resources/v1/transaction.py | 3 +- pay-api/src/pay_api/schemas/__init__.py | 2 +- pay-api/src/pay_api/schemas/utils.py | 2 +- pay-api/src/pay_api/services/__init__.py | 4 +- pay-api/src/pay_api/services/auth.py | 2 +- .../pay_api/services/base_payment_system.py | 15 +- pay-api/src/pay_api/services/bcol_service.py | 2 +- pay-api/src/pay_api/services/cfs_service.py | 2 +- pay-api/src/pay_api/services/code.py | 2 +- .../src/pay_api/services/deposit_service.py | 6 +- .../pay_api/services/direct_pay_service.py | 2 +- .../src/pay_api/services/distribution_code.py | 2 +- pay-api/src/pay_api/services/eft_service.py | 2 +- .../src/pay_api/services/ejv_pay_service.py | 2 +- pay-api/src/pay_api/services/fas/__init__.py | 2 +- pay-api/src/pay_api/services/fas/comment.py | 2 +- .../src/pay_api/services/fas/routing_slip.py | 2 +- .../routing_slip_status_transition_service.py | 2 +- pay-api/src/pay_api/services/fee_schedule.py | 2 +- .../pay_api/services/gcp_queue/__init__.py | 2 +- .../pay_api/services/gcp_queue_publisher.py | 14 +- pay-api/src/pay_api/services/hashing.py | 2 +- .../pay_api/services/internal_pay_service.py | 2 +- pay-api/src/pay_api/services/invoice.py | 2 +- .../src/pay_api/services/invoice_reference.py | 2 +- .../pay_api/services/non_sufficient_funds.py | 2 +- pay-api/src/pay_api/services/oauth_service.py | 2 +- .../services/online_banking_service.py | 2 +- pay-api/src/pay_api/services/pad_service.py | 2 +- pay-api/src/pay_api/services/paybc_service.py | 2 +- pay-api/src/pay_api/services/payment.py | 2 +- .../src/pay_api/services/payment_account.py | 27 +- .../src/pay_api/services/payment_line_item.py | 2 +- .../src/pay_api/services/payment_service.py | 2 +- .../pay_api/services/payment_transaction.py | 22 +- pay-api/src/pay_api/services/receipt.py | 2 +- pay-api/src/pay_api/services/refund.py | 2 +- pay-api/src/pay_api/services/statement.py | 2 +- .../pay_api/services/statement_recipients.py | 2 +- .../pay_api/services/statement_settings.py | 2 +- pay-api/src/pay_api/services/wire_service.py | 2 +- pay-api/src/pay_api/utils/__init__.py | 2 +- pay-api/src/pay_api/utils/auth.py | 2 +- pay-api/src/pay_api/utils/cache.py | 2 +- pay-api/src/pay_api/utils/constants.py | 2 +- pay-api/src/pay_api/utils/enums.py | 32 +- pay-api/src/pay_api/utils/errors.py | 2 +- pay-api/src/pay_api/utils/logging.py | 2 +- .../utils/paybc_transaction_error_message.py | 2 +- pay-api/src/pay_api/utils/run_version.py | 2 +- pay-api/src/pay_api/utils/user_context.py | 2 +- pay-api/src/pay_api/utils/util.py | 20 +- pay-api/src/pay_api/version.py | 2 +- pay-api/tests/__init__.py | 2 +- pay-api/tests/conftest.py | 27 +- pay-api/tests/unit/__init__.py | 2 +- pay-api/tests/unit/api/__init__.py | 2 +- pay-api/tests/unit/api/fas/__init__.py | 2 +- pay-api/tests/unit/api/fas/test_refund.py | 2 +- .../tests/unit/api/fas/test_routing_slip.py | 2 +- pay-api/tests/unit/api/test_account.py | 2 +- pay-api/tests/unit/api/test_bank_accounts.py | 2 +- pay-api/tests/unit/api/test_code.py | 2 +- pay-api/tests/unit/api/test_distributions.py | 2 +- pay-api/tests/unit/api/test_fee.py | 2 +- pay-api/tests/unit/api/test_fee_schedule.py | 2 +- pay-api/tests/unit/api/test_invoice.py | 2 +- pay-api/tests/unit/api/test_meta.py | 2 +- .../unit/api/test_non_sufficient_funds.py | 2 +- pay-api/tests/unit/api/test_ops.py | 2 +- pay-api/tests/unit/api/test_payment.py | 2 +- .../tests/unit/api/test_payment_request.py | 2 +- pay-api/tests/unit/api/test_receipt.py | 2 +- pay-api/tests/unit/api/test_refund.py | 2 +- pay-api/tests/unit/api/test_statement.py | 2 +- .../tests/unit/api/test_statement_settings.py | 2 +- pay-api/tests/unit/api/test_transaction.py | 2 +- pay-api/tests/unit/conf/__init__.py | 2 +- pay-api/tests/unit/conf/test_configuration.py | 2 +- pay-api/tests/unit/conf/test_version.py | 2 +- pay-api/tests/unit/factory/__init__.py | 2 +- .../factory/test_payment_system_factory.py | 2 +- pay-api/tests/unit/models/__init__.py | 2 +- pay-api/tests/unit/models/test_comment.py | 2 +- pay-api/tests/unit/models/test_corp_type.py | 2 +- pay-api/tests/unit/models/test_fee_code.py | 2 +- .../tests/unit/models/test_fee_schedule.py | 2 +- pay-api/tests/unit/models/test_filing_type.py | 2 +- pay-api/tests/unit/models/test_invoice.py | 2 +- .../unit/models/test_non_sufficient_funds.py | 2 +- pay-api/tests/unit/models/test_payment.py | 2 +- .../tests/unit/models/test_payment_account.py | 2 +- .../tests/unit/models/test_payment_method.py | 2 +- .../tests/unit/models/test_payment_system.py | 2 +- .../unit/models/test_payment_transaction.py | 2 +- pay-api/tests/unit/models/test_receipt.py | 2 +- .../tests/unit/models/test_routing_slip.py | 2 +- pay-api/tests/unit/models/test_status_code.py | 2 +- pay-api/tests/unit/services/__init__.py | 2 +- pay-api/tests/unit/services/test_auth.py | 2 +- .../tests/unit/services/test_bcol_service.py | 2 +- .../tests/unit/services/test_cfs_service.py | 2 +- pay-api/tests/unit/services/test_code.py | 2 +- pay-api/tests/unit/services/test_comment.py | 2 +- .../unit/services/test_distribution_code.py | 2 +- .../tests/unit/services/test_eft_service.py | 2 +- .../tests/unit/services/test_fee_schedule.py | 2 +- pay-api/tests/unit/services/test_flags.py | 2 +- pay-api/tests/unit/services/test_gcp_queue.py | 67 +- .../unit/services/test_hashing_service.py | 2 +- pay-api/tests/unit/services/test_invoice.py | 2 +- .../unit/services/test_invoice_reference.py | 2 +- .../services/test_non_sufficient_funds.py | 2 +- .../tests/unit/services/test_oauth_service.py | 2 +- .../tests/unit/services/test_pad_service.py | 2 +- pay-api/tests/unit/services/test_payment.py | 2 +- .../unit/services/test_payment_account.py | 2 +- .../unit/services/test_payment_line_item.py | 2 +- .../unit/services/test_payment_service.py | 2 +- .../services/test_payment_system_service.py | 2 +- .../unit/services/test_payment_transaction.py | 2 +- pay-api/tests/unit/services/test_receipt.py | 2 +- pay-api/tests/unit/services/test_refund.py | 2 +- .../services/test_routing_slip_service.py | 2 +- pay-api/tests/unit/services/test_statement.py | 2 +- .../unit/services/test_statement_settings.py | 2 +- .../tests/unit/services/test_wire_service.py | 2 +- pay-api/tests/unit/utils/__init__.py | 2 +- pay-api/tests/unit/utils/test_error.py | 2 +- pay-api/tests/unit/utils/test_logging.py | 2 +- pay-api/tests/unit/utils/test_util.py | 2 +- pay-api/tests/unit/utils/test_util_cors.py | 2 +- pay-api/tests/utilities/__init__.py | 2 +- pay-api/tests/utilities/base_test.py | 2 +- pay-api/tests/utilities/decorators.py | 2 +- pay-api/tests/utilities/schema_assertions.py | 2 +- pay-api/wsgi.py | 2 +- pay-queue/Makefile | 2 +- pay-queue/README.md | 4 +- pay-queue/app.py | 9 +- pay-queue/devops/vaults.gcp.env | 6 +- pay-queue/devops/vaults.json | 58 -- pay-queue/logging.conf | 34 + pay-queue/poetry.lock | 399 ++++++----- pay-queue/pyproject.toml | 7 +- pay-queue/scripts/verify_license_headers.sh | 4 +- pay-queue/setup.cfg | 9 +- pay-queue/setup.py | 4 +- pay-queue/src/pay_queue/__init__.py | 30 +- pay-queue/src/pay_queue/config.py | 27 +- pay-queue/src/pay_queue/enums.py | 2 +- pay-queue/src/pay_queue/external/__init__.py | 14 - pay-queue/src/pay_queue/external/gcp_auth.py | 20 +- pay-queue/src/pay_queue/minio.py | 2 +- pay-queue/src/pay_queue/resources/worker.py | 35 +- pay-queue/src/pay_queue/services/__init__.py | 5 + .../pay_queue/services/cgi_reconciliations.py | 57 +- .../services/eft/eft_reconciliation.py | 5 +- .../services/payment_reconciliations.py | 29 +- pay-queue/src/pay_queue/version.py | 2 +- pay-queue/tests/__init__.py | 2 +- pay-queue/tests/conftest.py | 24 +- pay-queue/tests/integration/__init__.py | 2 +- pay-queue/tests/integration/factory.py | 2 +- .../integration/test_cgi_reconciliations.py | 82 +-- .../integration/test_eft_reconciliation.py | 46 +- .../test_payment_reconciliations.py | 72 +- .../tests/integration/test_worker_queue.py | 4 +- pay-queue/tests/integration/utils.py | 11 +- report-api/requirements.txt | 2 +- 282 files changed, 1950 insertions(+), 1908 deletions(-) delete mode 100644 .github/workflows/pay-queue-gcp-cd.yml create mode 100644 jobs/ftp-poller/openshift/ftp-poller-build.json create mode 100644 jobs/ftp-poller/openshift/ftp-poller-deploy.json delete mode 100644 jobs/payment-jobs/devops/vaults.json create mode 100644 pay-admin/requirements.txt delete mode 100644 pay-api/devops/vaults.json delete mode 100644 pay-queue/devops/vaults.json create mode 100644 pay-queue/logging.conf delete mode 100644 pay-queue/src/pay_queue/external/__init__.py diff --git a/.github/workflows/pay-queue-cd.yml b/.github/workflows/pay-queue-cd.yml index 96124a711..73e2b0518 100644 --- a/.github/workflows/pay-queue-cd.yml +++ b/.github/workflows/pay-queue-cd.yml @@ -4,112 +4,30 @@ on: push: branches: - main + - feature* paths: - "pay-queue/**" - "pay-api/src/pay_api/models/**" - "pay-api/src/pay_api/services/cfs_service.py" workflow_dispatch: inputs: - environment: - description: "Environment (dev/test/prod)" + target: + description: "Deploy To" required: true - default: "dev" - -defaults: - run: - shell: bash - working-directory: ./pay-queue - -env: - APP_NAME: "pay-queue" - TAG_NAME: "dev" + type: choice + options: + - dev + - test + - sandbox + - prod jobs: - pay-queue-cd-by-push: - runs-on: ubuntu-20.04 - - if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' - environment: - name: "dev" - - steps: - - uses: actions/checkout@v3 - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Payment Reconciliations Queue Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} - - pay-queue-cd-by-dispatch: - runs-on: ubuntu-20.04 - - if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' - environment: - name: "${{ github.event.inputs.environment }}" - - steps: - - uses: actions/checkout@v3 - - name: Set env by input - run: | - echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV - - - name: Login Openshift - shell: bash - run: | - oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} - - - name: CD Flow - shell: bash - env: - OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} - OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} - OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} - OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} - OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} - TAG_NAME: ${{ env.TAG_NAME }} - run: | - make cd - - - name: Watch new rollout (trigger by image change in Openshift) - shell: bash - run: | - oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w - - - name: Rocket.Chat Notification - uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master - if: failure() - with: - type: ${{ job.status }} - job_name: "*Payment Reconciliations Queue Built and Deployed to ${{env.TAG_NAME}}*" - channel: "#registries-bot" - url: ${{ secrets.ROCKETCHAT_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} + pay-queue-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-queue" + working_directory: "./pay-queue" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file diff --git a/.github/workflows/pay-queue-ci.yml b/.github/workflows/pay-queue-ci.yml index 238da6343..567c4d23b 100644 --- a/.github/workflows/pay-queue-ci.yml +++ b/.github/workflows/pay-queue-ci.yml @@ -14,6 +14,7 @@ defaults: run: shell: bash working-directory: ./pay-queue + jobs: setup-job: runs-on: ubuntu-20.04 diff --git a/.github/workflows/pay-queue-gcp-cd.yml b/.github/workflows/pay-queue-gcp-cd.yml deleted file mode 100644 index 73e2b0518..000000000 --- a/.github/workflows/pay-queue-gcp-cd.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Pay Queue CD - -on: - push: - branches: - - main - - feature* - paths: - - "pay-queue/**" - - "pay-api/src/pay_api/models/**" - - "pay-api/src/pay_api/services/cfs_service.py" - workflow_dispatch: - inputs: - target: - description: "Deploy To" - required: true - type: choice - options: - - dev - - test - - sandbox - - prod - -jobs: - pay-queue-cd: - uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main - with: - target: ${{ inputs.target }} - app_name: "pay-queue" - working_directory: "./pay-queue" - secrets: - WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index ba3b879e1..b3f42e947 100644 --- a/.gitignore +++ b/.gitignore @@ -129,6 +129,3 @@ ACK.INBOX.F12022020202 pay-queue/ACK.* pay-queue/FEEDBACK.* jobs/notebook-report/data/ -test_eft_tdi17.txt -ACK.INBOX* -FEEDBACK.INBOX* diff --git a/bcol-api/requirements.txt b/bcol-api/requirements.txt index f9b2fad15..44c6e1330 100644 --- a/bcol-api/requirements.txt +++ b/bcol-api/requirements.txt @@ -36,7 +36,7 @@ python-ldap==3.4.4 pytz==2024.1 requests-file==2.0.0 requests-toolbelt==1.0.0 -requests==2.32.2 +requests==2.31.0 rsa==4.9 sentry-sdk==1.41.0 six==1.16.0 diff --git a/jobs/ftp-poller/Makefile b/jobs/ftp-poller/Makefile index 715e83f48..46dd918d4 100644 --- a/jobs/ftp-poller/Makefile +++ b/jobs/ftp-poller/Makefile @@ -115,7 +115,7 @@ tag: push ## tag image ################################################################################# run: ## Run the project in local - echo "unimplememted use docker" + . venv/bin/activate && python app.py ################################################################################# # Self Documenting Commands # diff --git a/jobs/ftp-poller/config.py b/jobs/ftp-poller/config.py index 12082de1e..81a331765 100644 --- a/jobs/ftp-poller/config.py +++ b/jobs/ftp-poller/config.py @@ -136,9 +136,11 @@ class _Config(object): # pylint: disable=too-few-public-methods SENTRY_ENABLE = os.getenv('SENTRY_ENABLE', 'False') SENTRY_DSN = os.getenv('SENTRY_DSN', None) - # PUB/SUB - PUB: ftp-poller-payment-reconciliation-dev - FTP_POLLER_TOPIC = os.getenv('FTP_POLLER_TOPIC', 'ftp-poller-payment-reconciliation-dev') - GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) + # GCP PubSub + AUDIENCE = os.getenv('AUDIENCE', None) + GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) + PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) + FTP_POLLER_TOPIC = os.getenv('FTP_POLLER_TOPIC', None) TESTING = False DEBUG = True diff --git a/jobs/ftp-poller/devops/vaults.json b/jobs/ftp-poller/devops/vaults.json index 209734136..433ed9cbd 100644 --- a/jobs/ftp-poller/devops/vaults.json +++ b/jobs/ftp-poller/devops/vaults.json @@ -19,12 +19,5 @@ "application": [ "relationship-api" ] - }, - { - "vault": "gcp-queue", - "application": [ - "gtksf3", - "topics" - ] } ] diff --git a/jobs/ftp-poller/invoke_jobs.py b/jobs/ftp-poller/invoke_jobs.py index 875ff01e8..5e18dcccb 100755 --- a/jobs/ftp-poller/invoke_jobs.py +++ b/jobs/ftp-poller/invoke_jobs.py @@ -21,7 +21,6 @@ import sentry_sdk from flask import Flask from sentry_sdk.integrations.flask import FlaskIntegration -from pay_api.services.gcp_queue import queue import config from utils.logger import setup_logging @@ -44,8 +43,7 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): dsn=app.config.get('SENTRY_DSN'), integrations=[FlaskIntegration()] ) - app.logger.info('<<<< Starting Ftp Poller Job >>>>') - queue.init_app(app) + app.logger.info(f'<<<< Starting Ftp Poller Job >>>>') ma.init_app(app) register_shellcontext(app) diff --git a/jobs/ftp-poller/openshift/ftp-poller-build.json b/jobs/ftp-poller/openshift/ftp-poller-build.json new file mode 100644 index 000000000..8713416c5 --- /dev/null +++ b/jobs/ftp-poller/openshift/ftp-poller-build.json @@ -0,0 +1,139 @@ +{ + "kind": "Template", + "apiVersion": "v1", + "metadata": { + "annotations": { + "description": "Build template for a FTP Poller job.", + "tags": "flask", + "iconClass": "icon-python" + }, + "name": "${NAME}-build-template" + }, + "objects": [ + { + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}" + } + }, + { + "kind": "BuildConfig", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}", + "labels": { + "app": "${NAME}", + "app-group": "${APP_GROUP}", + "template": "${NAME}-build" + } + }, + "spec": { + "source": { + "type": "Git", + "git": { + "uri": "${GIT_REPO_URL}", + "ref": "${GIT_REF}" + }, + "contextDir": "${SOURCE_CONTEXT_DIR}" + }, + "strategy": { + "type": "Docker", + "dockerStrategy": { + "dockerfilePath": "${DOCKER_FILE_PATH}" + } + }, + "output": { + "to": { + "kind": "ImageStreamTag", + "name": "${NAME}:${OUTPUT_IMAGE_TAG}" + } + }, + "triggers": [ + { + "type": "ConfigChange" + } + ] + } + } + ], + "parameters": [ + { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the objects defined in this template. You should keep this as default unless your know what your doing.", + "required": true, + "value": "ftp-poller" + }, + { + "name": "APP_GROUP", + "displayName": "App Group", + "description": "The name assigned to all of the deployments in this project.", + "required": true, + "value": "sbc-pay" + }, + { + "name": "GIT_REPO_URL", + "displayName": "Git Repo URL", + "description": "The URL to your GIT repo, don't use the this default unless your just experimenting.", + "required": true, + "value": "https://github.com/bcgov/sbc-pay.git" + }, + { + "name": "GIT_REF", + "displayName": "Git Reference", + "description": "The git reference or branch.", + "required": true, + "value": "development" + }, + { + "name": "SOURCE_CONTEXT_DIR", + "displayName": "Source Context Directory", + "description": "The source context directory.", + "required": true, + "value": "jobs/ftp-poller" + }, + { + "name": "SOURCE_IMAGE_KIND", + "displayName": "Source Image Kind", + "required": true, + "description": "The 'kind' (type) of the source image; typically ImageStreamTag, or DockerImage.", + "value": "ImageStreamTag" + }, + { + "name": "SOURCE_IMAGE_NAME_SPACE", + "displayName": "Source Image Name Space", + "required": true, + "description": "The name space of the source image.", + "value": "d7eovc-tools" + }, + { + "name": "SOURCE_IMAGE_NAME", + "displayName": "Source Image Name", + "required": true, + "description": "The name of the source image.", + "value": "python" + }, + { + "name": "SOURCE_IMAGE_TAG", + "displayName": "Source Image Tag", + "required": true, + "description": "The tag of the source image.", + "value": "3.7" + }, + { + "name": "OUTPUT_IMAGE_TAG", + "displayName": "Output Image Tag", + "description": "The tag given to the built image.", + "required": true, + "value": "latest" + }, + { + "name": "DOCKER_FILE_PATH", + "displayName": "Docker File Path", + "description": "The path to the docker file defining the build.", + "required": false, + "value": "Dockerfile" + } + ] +} diff --git a/jobs/ftp-poller/openshift/ftp-poller-deploy.json b/jobs/ftp-poller/openshift/ftp-poller-deploy.json new file mode 100644 index 000000000..87dd37b9e --- /dev/null +++ b/jobs/ftp-poller/openshift/ftp-poller-deploy.json @@ -0,0 +1,417 @@ +{ + "kind": "Template", + "apiVersion": "v1", + "metadata": { + "annotations": { + "description": "Deployment template for a ftp poller job.", + "tags": "${NAME}-${TAG_NAME}" + }, + "name": "${NAME}-${TAG_NAME}-deploy" + }, + "objects": [ + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-${TAG_NAME}", + "labels": { + "app": "${NAME}-${TAG_NAME}", + "app-group": "${APP_GROUP}", + "template": "${NAME}-deploy" + } + }, + "spec": { + "strategy": { + "type": "Rolling", + "rollingParams": { + "updatePeriodSeconds": 1, + "intervalSeconds": 1, + "timeoutSeconds": 600, + "maxUnavailable": "25%", + "maxSurge": "25%" + } + }, + "triggers": [ + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "${NAME}-${TAG_NAME}" + ], + "from": { + "kind": "ImageStreamTag", + "namespace": "${IMAGE_NAMESPACE}", + "name": "${NAME}:${TAG_NAME}" + } + } + }, + { + "type": "ConfigChange" + } + ], + "replicas": 1, + "test": false, + "selector": { + "app": "${NAME}-${TAG_NAME}", + "deploymentconfig": "${NAME}-${TAG_NAME}" + }, + "template": { + "metadata": { + "labels": { + "app": "${NAME}-${TAG_NAME}", + "app-group": "${APP_GROUP}", + "deploymentconfig": "${NAME}-${TAG_NAME}", + "template": "${NAME}-deploy" + } + }, + "spec": { + "volumes": [ + { + "name": "cron-config", + "configMap": { + "name": "${NAME}-${TAG_NAME}-cron-configuration", + "defaultMode": 420 + } + }, + { + "name": "sftp-private-key", + "configMap": { + "name": "${NAME}-${TAG_NAME}-sftp-configuration", + "defaultMode": 420 + } + } + ], + "containers": [ + { + "name": "${NAME}-${TAG_NAME}", + "image": "docker-registry.default.svc:5000/${IMAGE_NAMESPACE}/${NAME}:${TAG_NAME}", + "ports": [ + { + "containerPort": 8080, + "protocol": "TCP" + } + ], + "volumeMounts": [ + { + "name": "cron-config", + "readOnly": true, + "mountPath": "/ftp-poller/cron/" + }, + { + "name": "sftp-private-key", + "readOnly": true, + "mountPath": "/ftp-poller/key/" + } + ], + "env": [ + { + "name": "DATABASE_USERNAME", + "valueFrom": { + "configMapKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-config", + "key": "DATABASE_USER" + } + } + }, + { + "name": "DATABASE_PASSWORD", + "valueFrom": { + "secretKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-secret", + "key": "DATABASE_PASSWORD" + } + } + }, + { + "name": "DATABASE_NAME", + "valueFrom": { + "configMapKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-config", + "key": "DATABASE_NAME" + } + } + }, + { + "name": "DATABASE_HOST", + "valueFrom": { + "configMapKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-config", + "key": "DATABASE_HOST" + } + } + }, + { + "name": "DATABASE_PORT", + "valueFrom": { + "configMapKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-config", + "key": "DATABASE_PORT" + } + } + }, + { + "name": "DATABASE_TEST_USERNAME", + "valueFrom": { + "configMapKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-config", + "key": "DATABASE_TEST_USER" + } + } + }, + { + "name": "DATABASE_TEST_PASSWORD", + "valueFrom": { + "secretKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-secret", + "key": "DATABASE_TEST_PASSWORD" + } + } + }, + { + "name": "DATABASE_TEST_NAME", + "valueFrom": { + "configMapKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-config", + "key": "DATABASE_TEST_NAME" + } + } + }, + { + "name": "DATABASE_TEST_HOST", + "valueFrom": { + "configMapKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-config", + "key": "DATABASE_TEST_HOST" + } + } + }, + { + "name": "DATABASE_TEST_PORT", + "valueFrom": { + "configMapKeyRef": { + "name": "${DATABASE_NAME}-${TAG_NAME}-config", + "key": "DATABASE_TEST_PORT" + } + } + }, + { + "name": "PAYBC_DIRECT_PAY_REF_NUMBER", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "PAYBC_DIRECT_PAY_REF_NUMBER" + } + } + }, + { + "name": "PAYBC_DIRECT_PAY_API_KEY", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "PAYBC_DIRECT_PAY_API_KEY" + } + } + }, + { + "name": "PAYBC_DIRECT_PAY_BASE_URL", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "PAYBC_DIRECT_PAY_BASE_URL" + } + } + }, + { + "name": "PAYBC_DIRECT_PAY_CLIENT_ID", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "PAYBC_DIRECT_PAY_CLIENT_ID" + } + } + }, + { + "name": "PAYBC_DIRECT_PAY_CLIENT_SECRET", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "PAYBC_DIRECT_PAY_CLIENT_SECRET" + } + } + }, + { + "name": "NOTIFY_API_URL", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "NOTIFY_API_URL" + } + } + }, + { + "name": "KEYCLOAK_SERVICE_ACCOUNT_ID", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "KEYCLOAK_SERVICE_ACCOUNT_ID" + } + } + }, + { + "name": "KEYCLOAK_SERVICE_ACCOUNT_SECRET", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "KEYCLOAK_SERVICE_ACCOUNT_SECRET" + } + } + }, + { + "name": "JWT_OIDC_ISSUER", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "JWT_OIDC_ISSUER" + } + } + }, + { + "name": "AUTH_WEB_PAY_TRANSACTION_URL", + "valueFrom": { + "secretKeyRef": { + "name": "pay-api-${TAG_NAME}-secret", + "key": "AUTH_WEB_PAY_TRANSACTION_URL" + } + } + } + ], + "resources": { + "requests": { + "cpu": "${CPU_REQUEST}", + "memory": "${MEMORY_REQUEST}" + }, + "limits": { + "cpu": "${CPU_LIMIT}", + "memory": "${MEMORY_LIMIT}" + } + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "${NAME}-${TAG_NAME}", + "creationTimestamp": null, + "labels": { + "app": "${NAME}-${TAG_NAME}", + "app-group": "${APP_GROUP}", + "template": "${NAME}-deploy" + } + }, + "spec": { + "ports": [ + { + "name": "8080-tcp", + "protocol": "TCP", + "port": 8080, + "targetPort": 8080 + } + ], + "selector": { + "deploymentconfig": "${NAME}-${TAG_NAME}" + }, + "type": "ClusterIP", + "sessionAffinity": "None" + }, + "status": { + "loadBalancer": {} + } + } + ], + "parameters": [ + { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the OpenShift resources associated to the server instance.", + "required": true, + "value": "ftp-poller" + }, + { + "name": "APP_GROUP", + "displayName": "App Group", + "description": "The name assigned to all of the deployments in this project.", + "required": true, + "value": "sbc-pay" + }, + { + "name": "IMAGE_NAMESPACE", + "displayName": "Image Namespace", + "required": true, + "description": "The namespace of the OpenShift project containing the imagestream for the application.", + "value": "l4ygcl-tools" + }, + { + "name": "TAG_NAME", + "displayName": "Environment TAG name", + "description": "The TAG name for this environment, e.g., dev, test, prod", + "required": true, + "value": "dev" + }, + { + "name": "DATABASE_NAME", + "displayName": "Database App Name", + "description": "A valid database app name used by the service.", + "required": true, + "value": "postgresql" + }, + { + "name": "CPU_REQUEST", + "displayName": "Resources CPU Request", + "description": "The resources CPU request (in cores) for this build.", + "required": true, + "value": "100m" + }, + { + "name": "CPU_LIMIT", + "displayName": "Resources CPU Limit", + "description": "The resources CPU limit (in cores) for this build.", + "required": true, + "value": "750m" + }, + { + "name": "MEMORY_REQUEST", + "displayName": "Resources Memory Request", + "description": "The resources Memory request (in Mi, Gi, etc) for this build.", + "required": true, + "value": "100Mi" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Resources Memory Limit", + "description": "The resources Memory limit (in Mi, Gi, etc) for this build.", + "required": true, + "value": "2Gi" + }, + { + "name": "REPLICAS", + "displayName": "The number of replicas to run", + "description": "The number of replicas to run in this environment.", + "required": true, + "value": "1" + } + ] +} \ No newline at end of file diff --git a/jobs/ftp-poller/poetry.lock b/jobs/ftp-poller/poetry.lock index befc94f7c..c49f113da 100644 --- a/jobs/ftp-poller/poetry.lock +++ b/jobs/ftp-poller/poetry.lock @@ -90,26 +90,15 @@ cffi = ">=1.0.1" dev = ["cogapp", "pre-commit", "pytest", "wheel"] tests = ["pytest"] -[[package]] -name = "asn1crypto" -version = "1.5.1" -description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" -optional = false -python-versions = "*" -files = [ - {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, - {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, -] - [[package]] name = "astroid" -version = "3.2.2" +version = "3.1.0" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, - {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, ] [[package]] @@ -133,52 +122,52 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.2.0" +version = "2.0.4" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false -python-versions = ">=3.8" +python-versions = ">=3.6" files = [ - {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, - {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, ] [package.dependencies] -pycodestyle = ">=2.11.0" +pycodestyle = ">=2.10.0" [[package]] name = "bcrypt" -version = "4.1.3" +version = "4.1.2" description = "Modern password hashing for your software and your servers" optional = false python-versions = ">=3.7" files = [ - {file = "bcrypt-4.1.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:48429c83292b57bf4af6ab75809f8f4daf52aa5d480632e53707805cc1ce9b74"}, - {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8bea4c152b91fd8319fef4c6a790da5c07840421c2b785084989bf8bbb7455"}, - {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d3b317050a9a711a5c7214bf04e28333cf528e0ed0ec9a4e55ba628d0f07c1a"}, - {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:094fd31e08c2b102a14880ee5b3d09913ecf334cd604af27e1013c76831f7b05"}, - {file = "bcrypt-4.1.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4fb253d65da30d9269e0a6f4b0de32bd657a0208a6f4e43d3e645774fb5457f3"}, - {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:193bb49eeeb9c1e2db9ba65d09dc6384edd5608d9d672b4125e9320af9153a15"}, - {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:8cbb119267068c2581ae38790e0d1fbae65d0725247a930fc9900c285d95725d"}, - {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6cac78a8d42f9d120b3987f82252bdbeb7e6e900a5e1ba37f6be6fe4e3848286"}, - {file = "bcrypt-4.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01746eb2c4299dd0ae1670234bf77704f581dd72cc180f444bfe74eb80495b64"}, - {file = "bcrypt-4.1.3-cp37-abi3-win32.whl", hash = "sha256:037c5bf7c196a63dcce75545c8874610c600809d5d82c305dd327cd4969995bf"}, - {file = "bcrypt-4.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:8a893d192dfb7c8e883c4576813bf18bb9d59e2cfd88b68b725990f033f1b978"}, - {file = "bcrypt-4.1.3-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d4cf6ef1525f79255ef048b3489602868c47aea61f375377f0d00514fe4a78c"}, - {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5698ce5292a4e4b9e5861f7e53b1d89242ad39d54c3da451a93cac17b61921a"}, - {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec3c2e1ca3e5c4b9edb94290b356d082b721f3f50758bce7cce11d8a7c89ce84"}, - {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3a5be252fef513363fe281bafc596c31b552cf81d04c5085bc5dac29670faa08"}, - {file = "bcrypt-4.1.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5f7cd3399fbc4ec290378b541b0cf3d4398e4737a65d0f938c7c0f9d5e686611"}, - {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:c4c8d9b3e97209dd7111bf726e79f638ad9224b4691d1c7cfefa571a09b1b2d6"}, - {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:31adb9cbb8737a581a843e13df22ffb7c84638342de3708a98d5c986770f2834"}, - {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:551b320396e1d05e49cc18dd77d970accd52b322441628aca04801bbd1d52a73"}, - {file = "bcrypt-4.1.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6717543d2c110a155e6821ce5670c1f512f602eabb77dba95717ca76af79867d"}, - {file = "bcrypt-4.1.3-cp39-abi3-win32.whl", hash = "sha256:6004f5229b50f8493c49232b8e75726b568535fd300e5039e255d919fc3a07f2"}, - {file = "bcrypt-4.1.3-cp39-abi3-win_amd64.whl", hash = "sha256:2505b54afb074627111b5a8dc9b6ae69d0f01fea65c2fcaea403448c503d3991"}, - {file = "bcrypt-4.1.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:cb9c707c10bddaf9e5ba7cdb769f3e889e60b7d4fea22834b261f51ca2b89fed"}, - {file = "bcrypt-4.1.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9f8ea645eb94fb6e7bea0cf4ba121c07a3a182ac52876493870033141aa687bc"}, - {file = "bcrypt-4.1.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f44a97780677e7ac0ca393bd7982b19dbbd8d7228c1afe10b128fd9550eef5f1"}, - {file = "bcrypt-4.1.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d84702adb8f2798d813b17d8187d27076cca3cd52fe3686bb07a9083930ce650"}, - {file = "bcrypt-4.1.3.tar.gz", hash = "sha256:2ee15dd749f5952fe3f0430d0ff6b74082e159c50332a1413d51b5689cf06623"}, + {file = "bcrypt-4.1.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0"}, + {file = "bcrypt-4.1.2-cp37-abi3-win32.whl", hash = "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"}, + {file = "bcrypt-4.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551"}, + {file = "bcrypt-4.1.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a"}, + {file = "bcrypt-4.1.2-cp39-abi3-win32.whl", hash = "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f"}, + {file = "bcrypt-4.1.2-cp39-abi3-win_amd64.whl", hash = "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb"}, + {file = "bcrypt-4.1.2.tar.gz", hash = "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258"}, ] [package.extras] @@ -442,63 +431,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.3" +version = "7.4.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, ] [package.extras] @@ -743,13 +732,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.3.0" +version = "2.1.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, - {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, ] [package.dependencies] @@ -771,25 +760,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask-jwt-oidc" -version = "0.7.0" -description = "Opinionated flask oidc client" +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" optional = false -python-versions = "^3.9" +python-versions = "*" files = [] develop = false [package.dependencies] -cachelib = "0.*" -Flask = ">=2" -python-jose = "^3.3.0" -six = "^1.16.0" +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" [package.source] type = "git" -url = "https://github.com/seeker25/flask-jwt-oidc.git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" [[package]] name = "flask-marshmallow" @@ -911,37 +900,15 @@ files = [ flask = ">=2.2.5" sqlalchemy = ">=2.0.16" -[[package]] -name = "gcp-queue" -version = "0.3.0" -description = "" -optional = false -python-versions = "^3.9" -files = [] -develop = false - -[package.dependencies] -flask = ">=1" -google-auth = "^2.28.2" -google-cloud-pubsub = "^2.20.2" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} - -[package.source] -type = "git" -url = "https://github.com/seeker25/sbc-connect-common.git" -reference = "main" -resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" -subdirectory = "python/gcp-queue" - [[package]] name = "google-api-core" -version = "2.19.0" +version = "2.17.1" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, - {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, ] [package.dependencies] @@ -949,7 +916,6 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} -proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -960,13 +926,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.29.0" +version = "2.28.1" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, - {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, ] [package.dependencies] @@ -983,13 +949,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.21.2" +version = "2.20.0" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, - {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, ] [package.dependencies] @@ -1111,76 +1077,84 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.64.0" +version = "1.62.1" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, - {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, - {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, - {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, - {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, - {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, - {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, - {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, - {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, - {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, - {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, - {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, - {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, - {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, - {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, - {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, - {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, - {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, - {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, - {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, - {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.64.0)"] +protobuf = ["grpcio-tools (>=1.62.1)"] [[package]] name = "grpcio-status" -version = "1.62.2" +version = "1.62.1" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, - {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.2" +grpcio = ">=1.62.1" protobuf = ">=4.21.6" [[package]] @@ -1335,26 +1309,28 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "8.2.1" +version = "9.2.2" description = "LaunchDarkly SDK for Python" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, - {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.22.0,<3" +urllib3 = ">=1.26.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1510,13 +1486,13 @@ files = [ [[package]] name = "minio" -version = "7.2.7" +version = "7.2.5" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = "*" files = [ - {file = "minio-7.2.7-py3-none-any.whl", hash = "sha256:59d1f255d852fe7104018db75b3bebbd987e538690e680f7c5de835e422de837"}, - {file = "minio-7.2.7.tar.gz", hash = "sha256:473d5d53d79f340f3cd632054d0c82d2f93177ce1af2eac34a235bea55708d98"}, + {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, + {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, ] [package.dependencies] @@ -1572,91 +1548,52 @@ gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] invoke = ["invoke (>=2.0)"] [[package]] -name = "pay-api" -version = "0.1.0" -description = "" +name = "pay_api" +version = "0.0.0" +description = "A short description of the project" optional = false -python-versions = "^3.12" +python-versions = ">=3.12" files = [] develop = false [package.dependencies] -alembic = "1.13.1" -attrs = "23.2.0" -blinker = "1.7.0" -cachelib = "0.9.0" -cachetools = "5.3.3" -cattrs = "23.2.3" -certifi = "2024.2.2" -cffi = "1.16.0" -charset-normalizer = "3.3.2" -click = "8.1.7" -croniter = "2.0.2" -cryptography = "42.0.5" -dpath = "2.1.6" -ecdsa = "0.18.0" -expiringdict = "1.2.2" -flask = "3.0.2" -flask-caching = "2.3.0" -flask-cors = "4.0.0" -flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} -flask-marshmallow = "1.2.0" -flask-migrate = "4.0.7" -flask-moment = "1.0.5" -flask-script = "2.0.6" -flask-sqlalchemy = "3.1.1" -gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} -greenlet = "3.0.3" -gunicorn = "21.2.0" +cattrs = "*" +croniter = "*" +cryptography = "*" +dpath = "*" +Flask = "*" +Flask-Caching = "*" +Flask-Cors = "*" +flask-jwt-oidc = "*" +flask-marshmallow = "*" +Flask-Migrate = "*" +Flask-Moment = "*" +Flask-Script = "*" +Flask-SQLAlchemy = "*" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +gunicorn = "*" holidays = "0.37" -idna = "3.6" -itsdangerous = "2.1.2" -jaeger-client = "4.8.0" -jinja2 = "3.1.3" +itsdangerous = "*" +jaeger-client = "*" +Jinja2 = "*" jsonschema = "4.17.3" -launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "8.2.1" -mako = "1.3.2" -markupsafe = "2.1.5" -marshmallow = "3.21.1" -marshmallow-sqlalchemy = "1.0.0" -opentracing = "2.4.0" -packaging = "24.0" -pg8000 = "^1.30.5" -proto-plus = "1.23.0" -protobuf = "4.25.3" -psycopg2-binary = "2.9.9" -pyasn1 = "0.5.1" -pyasn1-modules = "0.3.0" -pycparser = "2.21" -pyhumps = "3.8.0" -pyrfc3339 = "1.1" -pyrsistent = "0.20.0" -python-dateutil = "2.9.0.post0" -python-dotenv = "1.0.1" -python-jose = "3.3.0" -pytz = "2024.1" -requests = "2.31.0" -rsa = "4.9" -sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -semver = "3.0.2" -sentry-sdk = "1.41.0" -six = "1.16.0" -sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} -sqlalchemy = "2.0.28" -sqlalchemy-utils = "0.41.1" -threadloop = "1.0.2" -thrift = "0.16.0" -tornado = "6.4" -typing-extensions = "4.10.0" -urllib3 = "2.2.1" -werkzeug = "3.0.1" +launchdarkly-server-sdk = "*" +marshmallow-sqlalchemy = "*" +psycopg2-binary = "*" +pyhumps = "*" +python-dotenv = "*" +requests = "*" +sentry-sdk = {version = "*", extras = ["flask"]} +sqlalchemy = "*" +sqlalchemy_utils = "*" +Werkzeug = "*" [package.source] type = "git" url = "https://github.com/seeker25/sbc-pay.git" -reference = "sync-python-to-main" -resolved_reference = "522ed4997b03f8bb5d88864e739815548aa606be" +reference = "18263" +resolved_reference = "fe42bf81c86c77946a1df238f69f12ad6b83d804" subdirectory = "pay-api" [[package]] @@ -1673,46 +1610,30 @@ files = [ [package.dependencies] flake8 = ">=5.0.0" -[[package]] -name = "pg8000" -version = "1.31.2" -description = "PostgreSQL interface library" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, - {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, -] - -[package.dependencies] -python-dateutil = ">=2.8.2" -scramp = ">=1.4.5" - [[package]] name = "platformdirs" -version = "4.2.2" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.5.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1983,17 +1904,17 @@ files = [ [[package]] name = "pylint" -version = "3.2.2" +version = "3.1.0" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, - {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, ] [package.dependencies] -astroid = ">=3.2.2,<=3.3.0-dev0" +astroid = ">=3.1.0,<=3.2.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -2128,33 +2049,33 @@ paramiko = ">=1.17" [[package]] name = "pytest" -version = "8.2.1" +version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, - {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.4,<2.0" [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" -version = "0.23.7" +version = "0.23.5.post1" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"}, - {file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"}, + {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, + {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, ] [package.dependencies] @@ -2184,17 +2105,17 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.14.0" +version = "3.12.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, ] [package.dependencies] -pytest = ">=6.2.5" +pytest = ">=5.0" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] @@ -2314,23 +2235,9 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" subdirectory = "python" -[[package]] -name = "scramp" -version = "1.4.5" -description = "An implementation of the SCRAM protocol." -optional = false -python-versions = ">=3.8" -files = [ - {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, - {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, -] - -[package.dependencies] -asn1crypto = ">=1.5.1" - [[package]] name = "semver" version = "3.0.2" @@ -2344,17 +2251,20 @@ files = [ [[package]] name = "sentry-sdk" -version = "1.41.0" +version = "1.42.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"}, - {file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"}, + {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, + {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, ] [package.dependencies] +blinker = {version = ">=1.1", optional = true, markers = "extra == \"flask\""} certifi = "*" +flask = {version = ">=0.11", optional = true, markers = "extra == \"flask\""} +markupsafe = {version = "*", optional = true, markers = "extra == \"flask\""} urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} [package.extras] @@ -2374,6 +2284,7 @@ grpcio = ["grpcio (>=1.21.1)"] httpx = ["httpx (>=0.16.0)"] huey = ["huey (>=2)"] loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] opentelemetry = ["opentelemetry-distro (>=0.35b0)"] opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] pure-eval = ["asttokens", "executing", "pure-eval"] @@ -2389,18 +2300,19 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "70.0.0" +version = "69.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, - {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simple-cloudevent" @@ -2442,22 +2354,6 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] -[[package]] -name = "sql-versioning" -version = "0.1.0" -description = "" -optional = false -python-versions = "^3.10" -files = [] -develop = false - -[package.source] -type = "git" -url = "https://github.com/bcgov/lear.git" -reference = "feature-legal-name" -resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" -subdirectory = "python/common/sql-versioning" - [[package]] name = "sqlalchemy" version = "2.0.28" @@ -2617,13 +2513,13 @@ twisted = ["twisted"] [[package]] name = "tomlkit" -version = "0.12.5" +version = "0.12.4" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, - {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, ] [[package]] @@ -2694,4 +2590,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "3aeb528030f868f3ab60ec2d31aab0dd38f09872fe7ba62082c45715aaeed206" +content-hash = "f048f3fd232a6fb3c61c8d1f8651e55f81e6c283799dfde0ed451cea62a6e3a6" diff --git a/jobs/ftp-poller/pyproject.toml b/jobs/ftp-poller/pyproject.toml index 34ecaccca..cf312a280 100644 --- a/jobs/ftp-poller/pyproject.toml +++ b/jobs/ftp-poller/pyproject.toml @@ -21,9 +21,11 @@ jaeger-client = "^4.8.0" itsdangerous = "^2.1.2" jinja2 = "^3.1.3" protobuf = "4.25.3" -launchdarkly-server-sdk = "^8.2.1" +launchdarkly-server-sdk = "^9.2.2" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} -pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "sync-python-to-main", subdirectory = "pay-api"} +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +pay-api = {git = "https://github.com/seeker25/sbc-pay.git", rev = "18263", subdirectory = "pay-api"} [tool.poetry.group.dev.dependencies] diff --git a/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py b/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py index ba79e2347..7542a4d84 100644 --- a/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py +++ b/jobs/ftp-poller/tasks/cgi_feeder_poller_task.py @@ -16,8 +16,8 @@ from flask import current_app from paramiko.sftp_attr import SFTPAttributes -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.utils.enums import MessageType from services.sftp import SFTPService from utils import utils @@ -48,12 +48,12 @@ def poll_ftp(cls): f'Skipping directory {file_name}.') continue if cls._is_ack_file(file_name): - utils.publish_to_queue([file_name], QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + utils.publish_to_queue([file_name], MessageType.CGI_ACK_RECEIVED.value) cls._move_file_to_backup(sftp_client, [file_name]) elif cls._is_feedback_file(file_name): bucket_name = current_app.config.get('MINIO_CGI_BUCKET_NAME') utils.upload_to_minio(file, file_full_name, sftp_client, bucket_name) - utils.publish_to_queue([file_name], QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value, + utils.publish_to_queue([file_name], MessageType.CGI_FEEDBACK_RECEIVED.value, location=bucket_name) cls._move_file_to_backup(sftp_client, [file_name]) elif cls._is_a_trigger_file(file_name): diff --git a/jobs/ftp-poller/tasks/eft_poller_ftp.py b/jobs/ftp-poller/tasks/eft_poller_ftp.py index ff6b3dc52..10f59a4f4 100644 --- a/jobs/ftp-poller/tasks/eft_poller_ftp.py +++ b/jobs/ftp-poller/tasks/eft_poller_ftp.py @@ -17,7 +17,7 @@ from flask import current_app from paramiko.sftp_attr import SFTPAttributes -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.utils.enums import MessageType from services.sftp import SFTPService from utils.utils import publish_to_queue, upload_to_minio @@ -66,7 +66,7 @@ def _post_process(cls, sftp_client, payment_file_list: List[str]): 2.Send a message to queue """ cls._move_file_to_backup(sftp_client, payment_file_list) - publish_to_queue(payment_file_list, QueueMessageTypes.EFT_FILE_UPLOADED.value, + publish_to_queue(payment_file_list, MessageType.EFT_FILE_UPLOADED.value, location=current_app.config.get('MINIO_EFT_BUCKET_NAME')) @classmethod diff --git a/jobs/ftp-poller/tests/jobs/test_sftp.py b/jobs/ftp-poller/tests/jobs/test_sftp.py index 07dc79d5b..d88470a8d 100644 --- a/jobs/ftp-poller/tests/jobs/test_sftp.py +++ b/jobs/ftp-poller/tests/jobs/test_sftp.py @@ -19,8 +19,6 @@ import pytest from flask import current_app -from sbc_common_components.utils.enums import QueueMessageTypes - from services.sftp import SFTPService from utils.utils import publish_to_queue @@ -40,14 +38,7 @@ def test_poll_ftp_task(): assert len(files) == 1, 'Files exist in FTP folder' -@pytest.mark.skip(reason='leave this to manually verify pubsub connection;' - 'needs env vars, disable def mock_queue_publish(monkeypatch):') -def test_queue_message(session): # pylint:disable=unused-argument +@pytest.mark.skip(reason='leave this to manually verify pubsub connection; needs env vars') +def test_queue_message(): """Test publishing to topic.""" - file_name = 'file1.csv' - publish_to_queue([file_name]) - publish_to_queue([file_name], QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) - publish_to_queue([file_name], QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value, - location=current_app.config.get('MINIO_CGI_BUCKET_NAME')) - publish_to_queue([file_name], QueueMessageTypes.EFT_FILE_UPLOADED.value, - location=current_app.config.get('MINIO_EFT_BUCKET_NAME')) + publish_to_queue(['file1.csv']) diff --git a/jobs/ftp-poller/utils/utils.py b/jobs/ftp-poller/utils/utils.py index afc22828a..aa452f5c4 100644 --- a/jobs/ftp-poller/utils/utils.py +++ b/jobs/ftp-poller/utils/utils.py @@ -12,21 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. """Service to manage PAYBC services.""" -from time import time from typing import List from flask import current_app from paramiko import SFTPFile from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage -from pay_api.utils.enums import QueueSources -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.utils.enums import MessageType, QueueSources from utils.minio import put_object -def publish_to_queue(payment_file_list: List[str], message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value, - location: str = ''): +def publish_to_queue(payment_file_list: List[str], message_type=MessageType.CAS_UPLOADED.value, location: str = ''): """Publish message to the Queue, saying file has been uploaded. Using the event spec.""" queue_data = { 'fileSource': 'MINIO', @@ -41,11 +38,11 @@ def publish_to_queue(payment_file_list: List[str], message_type=QueueMessageType source=QueueSources.FTP_POLLER.value, message_type=message_type, payload=queue_data, - topic=current_app.config.get('FTP_POLLER_TOPIC'), - ordering_key=str(time()) + topic=current_app.config.get('FTP_POLLER_TOPIC') ) ) except Exception as e: # NOQA # pylint: disable=broad-except + current_app.logger.error(e) current_app.logger.warning( f'Notification to Queue failed for the file {file_name}', e) @@ -60,6 +57,7 @@ def upload_to_minio(file, file_full_name, sftp_client, bucket_name): value_as_bytes = f.read() try: put_object(value_as_bytes, file.filename, bucket_name, file.st_size, ) - except Exception: # NOQA # pylint: disable=broad-except + except Exception as e: # NOQA # pylint: disable=broad-except + current_app.logger.error(e) current_app.logger.error(f'upload to minio failed for the file: {file_full_name}') raise diff --git a/jobs/notebook-report/requirements.txt b/jobs/notebook-report/requirements.txt index 4a095802e..bb27f8d33 100644 --- a/jobs/notebook-report/requirements.txt +++ b/jobs/notebook-report/requirements.txt @@ -30,5 +30,5 @@ marshmallow==2.20.5 Werkzeug==0.16.1 certifi==2023.7.22 urllib3==1.26.17 -idna==3.7 +idna==2.9 pylint diff --git a/jobs/notebook-report/requirements/prod.txt b/jobs/notebook-report/requirements/prod.txt index 6d869f683..1f8cbae23 100644 --- a/jobs/notebook-report/requirements/prod.txt +++ b/jobs/notebook-report/requirements/prod.txt @@ -25,9 +25,9 @@ pyrsistent==0.16.0 Flask==1.1.2 Click==7.1.2 python-dotenv==0.13.0 -requests==2.32.2 +requests==2.31.0 marshmallow==2.20.5 Werkzeug==0.16.1 certifi==2023.7.22 urllib3==1.26.17 -idna==3.7 +idna==2.9 diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index a18c89791..cede16606 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -116,11 +116,6 @@ class _Config(object): # pylint: disable=too-few-public-methods AUTH_WEB_STATEMENT_URL = os.getenv('AUTH_WEB_STATEMENT_URL', 'account/orgId/settings/statements') REGISTRIES_LOGO_IMAGE_NAME = os.getenv('REGISTRIES_LOGO_IMAGE_NAME', 'bc_logo_for_email.png') - # PUB/SUB- PUB: account-mailer-dev - ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') - GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) - - CFS_ACCOUNT_DESCRIPTION = os.getenv('CFS_ACCOUNT_DESCRIPTION', 'BCR') CFS_INVOICE_PREFIX = os.getenv('CFS_INVOICE_PREFIX', 'REG') CFS_STOP_PAD_ACCOUNT_CREATION = os.getenv('CFS_STOP_PAD_ACCOUNT_CREATION', 'false').lower() == 'true' @@ -248,7 +243,6 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods CGI_SFTP_PORT = 2222 CGI_SFTP_DIRECTORY = '/data/' CGI_SFTP_HOST = 'localhost' - GCP_AUTH_KEY = None class ProdConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/jobs/payment-jobs/devops/vaults.json b/jobs/payment-jobs/devops/vaults.json deleted file mode 100644 index 420b0bbe9..000000000 --- a/jobs/payment-jobs/devops/vaults.json +++ /dev/null @@ -1,70 +0,0 @@ -[ - { - "vault": "shared", - "application": [ - "api-endpoints", - "encryption-key" - ] - }, - { - "vault": "keycloak", - "application": [ - "jwt-base", - "sbc-auth-admin" - ] - }, - { - "vault": "payment-external-services", - "application": [ - "paybc", - "cfs" - ] - }, - { - "vault": "relationship", - "application": [ - "postgres-pay", - "pay-api", - "jwt", - "payment-jobs", - "ftp-poller" - ] - }, - { - "vault": "sentry", - "application": [ - "relationship-api" - ] - }, - { - "vault": "minio", - "application": [ - "payment-jobs" - ] - }, - { - "vault": "minio", - "application": [ - "base" - ] - }, - { - "vault": "launchdarkly", - "application": [ - "pay" - ] - }, - { - "vault": "entity", - "application": [ - "colin-api" - ] - }, - { - "vault": "gcp-queue", - "application": [ - "gtksf3", - "topics" - ] - } -] diff --git a/jobs/payment-jobs/invoke_jobs.py b/jobs/payment-jobs/invoke_jobs.py index 7636770a6..ebbb7d567 100755 --- a/jobs/payment-jobs/invoke_jobs.py +++ b/jobs/payment-jobs/invoke_jobs.py @@ -30,7 +30,6 @@ from utils.logger import setup_logging from pay_api.services import Flags -from pay_api.services.gcp_queue import queue setup_logging(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first @@ -52,7 +51,6 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production'), job_name='unk release=f'payment-jobs-{job_name}@-', ) app.logger.info('<<<< Starting Payment Jobs >>>>') - queue.init_app(app) db.init_app(app) if init_oracle: oracle_db.init_app(app) diff --git a/jobs/payment-jobs/poetry.lock b/jobs/payment-jobs/poetry.lock index 0a123832e..ff1195ca7 100644 --- a/jobs/payment-jobs/poetry.lock +++ b/jobs/payment-jobs/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "alembic" @@ -784,13 +784,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.3.0" +version = "2.1.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, - {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, ] [package.dependencies] @@ -812,25 +812,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask-jwt-oidc" -version = "0.7.0" -description = "Opinionated flask oidc client" +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" optional = false -python-versions = "^3.9" +python-versions = "*" files = [] develop = false [package.dependencies] -cachelib = "0.*" -Flask = ">=2" -python-jose = "^3.3.0" -six = "^1.16.0" +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" [package.source] type = "git" -url = "https://github.com/seeker25/flask-jwt-oidc.git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" [[package]] name = "flask-marshmallow" @@ -943,28 +943,6 @@ files = [ [package.dependencies] python-dateutil = ">=2.7" -[[package]] -name = "gcp-queue" -version = "0.3.0" -description = "" -optional = false -python-versions = "^3.9" -files = [] -develop = false - -[package.dependencies] -flask = ">=1" -google-auth = "^2.28.2" -google-cloud-pubsub = "^2.20.2" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} - -[package.source] -type = "git" -url = "https://github.com/seeker25/sbc-connect-common.git" -reference = "main" -resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" -subdirectory = "python/gcp-queue" - [[package]] name = "google-api-core" version = "2.17.1" @@ -991,13 +969,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.29.0" +version = "2.28.1" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, - {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, ] [package.dependencies] @@ -1014,13 +992,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.21.2" +version = "2.20.0" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, - {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, ] [package.dependencies] @@ -1374,26 +1352,28 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "8.2.1" +version = "9.2.2" description = "LaunchDarkly SDK for Python" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, - {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.22.0,<3" +urllib3 = ">=1.26.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1647,16 +1627,22 @@ dpath = "2.1.6" ecdsa = "0.18.0" expiringdict = "1.2.2" flask = "3.0.2" -flask-caching = "2.3.0" +flask-caching = "2.1.0" flask-cors = "4.0.0" -flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} flask-marshmallow = "1.2.0" flask-migrate = "4.0.7" flask-moment = "1.0.5" flask-script = "2.0.6" flask-sqlalchemy = "3.1.1" -gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} +google-api-core = "2.17.1" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +googleapis-common-protos = "1.63.0" greenlet = "3.0.3" +grpc-google-iam-v1 = "0.13.0" +grpcio = "1.62.1" +grpcio-status = "1.62.1" gunicorn = "21.2.0" holidays = "0.37" idna = "3.6" @@ -1665,7 +1651,7 @@ jaeger-client = "4.8.0" jinja2 = "3.1.3" jsonschema = "4.17.3" launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "8.2.1" +launchdarkly-server-sdk = "9.2.2" mako = "1.3.2" markupsafe = "2.1.5" marshmallow = "3.21.1" @@ -1691,6 +1677,7 @@ rsa = "4.9" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} semver = "3.0.2" sentry-sdk = "1.41.0" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} six = "1.16.0" sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} sqlalchemy = "2.0.28" @@ -1704,9 +1691,9 @@ werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/seeker25/sbc-pay.git" -reference = "sync-python-to-main" -resolved_reference = "3cc0024430f298a460f4780bf8a55f6c84e1ec8e" +url = "https://github.com/bcgov/sbc-pay.git" +reference = "feature-queue-python-upgrade" +resolved_reference = "ba20cecf7e65065fa22dbfedb0f0bb5c1ee7ec94" subdirectory = "pay-api" [[package]] @@ -2363,7 +2350,7 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "22978d810dc4e85c51c3129936686b0a17124e64" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" subdirectory = "python" [[package]] @@ -2744,4 +2731,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "b16efd0364fcd29610e0eae102b66eda5771f66116953175e2d4e54550537cfa" +content-hash = "eaa76036fa004456010050a8138aa7b147b9f3f8f614708710e146224d71b8a4" diff --git a/jobs/payment-jobs/pyproject.toml b/jobs/payment-jobs/pyproject.toml index 402cfb107..91f347013 100644 --- a/jobs/payment-jobs/pyproject.toml +++ b/jobs/payment-jobs/pyproject.toml @@ -7,7 +7,10 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" -pay-api = {git = "https://github.com/seeker25/sbc-pay.git", branch = "sync-python-to-main", subdirectory = "pay-api"} +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +pay-api = {git = "https://github.com/bcgov/sbc-pay.git", branch = "feature-queue-python-upgrade", subdirectory = "pay-api"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} gunicorn = "^21.2.0" flask = "^3.0.2" flask-sqlalchemy = "^3.1.1" @@ -25,7 +28,7 @@ pysftp = "^0.2.9" flask-migrate = "^4.0.7" itsdangerous = "^2.1.2" dataclass-wizard = "^0.22.3" -launchdarkly-server-sdk = "^8.2.1" +launchdarkly-server-sdk = "^9.2.2" cx-oracle = "^8.3.0" more-itertools = "^10.2.0" pg8000 = "^1.30.5" diff --git a/jobs/payment-jobs/tasks/activate_pad_account_task.py b/jobs/payment-jobs/tasks/activate_pad_account_task.py index 2659006aa..d24127537 100644 --- a/jobs/payment-jobs/tasks/activate_pad_account_task.py +++ b/jobs/payment-jobs/tasks/activate_pad_account_task.py @@ -19,8 +19,7 @@ from flask import current_app from pay_api.models import CfsAccount as CfsAccountModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.utils.enums import CfsAccountStatus, PaymentMethod -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.utils.enums import CfsAccountStatus, MessageType, PaymentMethod from utils import mailer @@ -58,4 +57,4 @@ def activate_pad_accounts(cls): if pay_account.payment_method != PaymentMethod.PAD.value: pay_account.payment_method = PaymentMethod.PAD.value pay_account.save() - mailer.publish_mailer_events(QueueMessageTypes.CONFIRMATION_PERIOD_OVER.value, pay_account) + mailer.publish_mailer_events(MessageType.PAD_CONFIRMATION_PERIOD_OVER, pay_account) diff --git a/jobs/payment-jobs/tasks/ap_task.py b/jobs/payment-jobs/tasks/ap_task.py index c317cb447..0633a8c3b 100644 --- a/jobs/payment-jobs/tasks/ap_task.py +++ b/jobs/payment-jobs/tasks/ap_task.py @@ -76,7 +76,7 @@ def _create_routing_slip_refund_file(cls): # pylint:disable=too-many-locals, to if not routing_slips_dao: return - for routing_slips in list(batched(routing_slips_dao, 250)): + for routing_slips in batched(routing_slips_dao, 250): ejv_file_model: EjvFileModel = EjvFileModel( file_type=cls.ap_type.value, file_ref=cls.get_file_name(), @@ -118,7 +118,7 @@ def _create_non_gov_disbursement_file(cls): # pylint:disable=too-many-locals return # 250 MAX is all the transactions the feeder can take per batch. - for invoices in list(batched(total_invoices, 250)): + for invoices in batched(total_invoices, 250): bca_distribution = cls._get_bca_distribution_string() ejv_file_model: EjvFileModel = EjvFileModel( file_type=cls.ap_type.value, diff --git a/jobs/payment-jobs/tasks/cfs_create_account_task.py b/jobs/payment-jobs/tasks/cfs_create_account_task.py index 55dbf497e..4c075604c 100644 --- a/jobs/payment-jobs/tasks/cfs_create_account_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_account_task.py @@ -22,8 +22,7 @@ from pay_api.services.cfs_service import CFSService from pay_api.services.oauth_service import OAuthService from pay_api.utils.constants import RECEIPT_METHOD_EFT_MONTHLY, RECEIPT_METHOD_PAD_DAILY -from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, PaymentMethod -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.utils.enums import AuthHeaderType, CfsAccountStatus, ContentType, MessageType, PaymentMethod from sentry_sdk import capture_message from services import routing_slip from utils import mailer @@ -141,7 +140,7 @@ def _create_cfs_account(cls, pending_account: CfsAccountModel, pay_account: Paym capture_message(f'User Input needed for creating CFS Account: account id={pay_account.id}, ' f'auth account : {pay_account.auth_account_id}, ERROR : Invalid Bank Details', level='error') - mailer.publish_mailer_events(QueueMessageTypes.PAD_SETUP_FAILED.value, pay_account) + mailer.publish_mailer_events(MessageType.PAD_SETUP_FAILED, pay_account) pending_account.status = CfsAccountStatus.INACTIVE.value pending_account.save() return diff --git a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py index 862d2fce7..bdc24ec4a 100644 --- a/jobs/payment-jobs/tasks/cfs_create_invoice_task.py +++ b/jobs/payment-jobs/tasks/cfs_create_invoice_task.py @@ -32,9 +32,8 @@ from pay_api.services.payment import Payment from pay_api.services.payment_account import PaymentAccount as PaymentAccountService from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, PaymentSystem) + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, PaymentSystem) from pay_api.utils.util import generate_transaction_number -from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from utils import mailer @@ -321,8 +320,7 @@ def _create_pad_invoices(cls): # pylint: disable=too-many-locals 'invoice_total': float(invoice_total), 'invoice_process_date': f'{datetime.now()}' } - mailer.publish_mailer_events(QueueMessageTypes.PAD_INVOICE_CREATED.value, payment_account, - additional_params) + mailer.publish_mailer_events(MessageType.PAD_INVOICE_CREATED, payment_account, additional_params) # Iterate invoice and create invoice reference records for invoice in account_invoices: invoice_reference = InvoiceReferenceModel( @@ -454,7 +452,7 @@ def _create_eft_invoices(cls): current_app.logger.error(e) continue - mailer.publish_mailer_events(QueueMessageTypes.EFT_INVOICE_CREATED.value, payment_account, { + mailer.publish_mailer_events(MessageType.EFT_INVOICE_CREATED, payment_account, { 'invoice_total': float(invoice_total), 'invoice_process_date': f'{datetime.now(tz=timezone.utc)}' }) diff --git a/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py b/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py index 200f792c6..2a04f3485 100644 --- a/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py +++ b/jobs/payment-jobs/tasks/unpaid_invoice_notify_task.py @@ -19,8 +19,7 @@ from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import db -from pay_api.utils.enums import InvoiceStatus, PaymentMethod -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.utils.enums import InvoiceStatus, MessageType, PaymentMethod from sentry_sdk import capture_message from sqlalchemy import Date, and_, cast, func @@ -77,8 +76,7 @@ def _notify_for_ob(cls): # pylint: disable=too-many-locals 'cfsAccountId': cfs_account.cfs_account, 'authAccountId': pay_account.auth_account_id, } - mailer.publish_mailer_events(QueueMessageTypes.PAYMENT_PENDING.value, - pay_account, + mailer.publish_mailer_events(MessageType.ONLINE_BANKING_OUTSTANDING_INVOICE, pay_account, addition_params_to_mailer) except Exception as e: # NOQA # pylint: disable=broad-except capture_message(f'Error on notifying mailer OB Pending invoice: account id={pay_account.id}, ' diff --git a/jobs/payment-jobs/tests/jobs/conftest.py b/jobs/payment-jobs/tests/jobs/conftest.py index 832ed837f..0f6bcfeb3 100644 --- a/jobs/payment-jobs/tests/jobs/conftest.py +++ b/jobs/payment-jobs/tests/jobs/conftest.py @@ -27,30 +27,6 @@ from utils.logger import setup_logging -@pytest.fixture(autouse=True) -def mock_pub_sub_call(mocker): - """Mock pub sub call.""" - class Expando(object): - """Expando class.""" - - class PublisherMock: - """Publisher Mock.""" - - def __init__(self, *args, **kwargs): - def result(): - """Return true for mock.""" - return True - self.result = result - - def publish(self, *args, **kwargs): - """Publish mock.""" - ex = Expando() - ex.result = self.result - return ex - - mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) - - @pytest.fixture(scope='session') def app(): """Return a session-wide application configured in TEST mode.""" diff --git a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py index 7b4fd3960..9b3f596d8 100644 --- a/jobs/payment-jobs/tests/jobs/test_statement_due_task.py +++ b/jobs/payment-jobs/tests/jobs/test_statement_due_task.py @@ -45,7 +45,6 @@ app = None -# Travis Semple - in the future please remove this, this should be inside of conftest.py like the other fixtures. @pytest.fixture def setup(): """Initialize app with test env for testing.""" diff --git a/jobs/payment-jobs/utils/mailer.py b/jobs/payment-jobs/utils/mailer.py index 69cf1a6b9..1379db03c 100644 --- a/jobs/payment-jobs/utils/mailer.py +++ b/jobs/payment-jobs/utils/mailer.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -14,14 +14,15 @@ """Task to activate accounts with pending activation.Mostly for PAD with 3 day activation period.""" from dataclasses import dataclass from datetime import datetime +from typing import Dict from flask import current_app from pay_api.models import FeeSchedule as FeeScheduleModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Statement as StatementModel from pay_api.services import gcp_queue_publisher -from pay_api.utils.enums import QueueSources -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.services.gcp_queue_publisher import QueueMessage +from pay_api.utils.enums import QueueSources, MessageType from sentry_sdk import capture_message @@ -37,7 +38,8 @@ class StatementNotificationInfo: total_amount_owing: float -def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, additional_params=None): +def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, + additional_params: Dict = {}): """Publish payment message to the mailer queue.""" # Publish message to the Queue, saying account has been activated. Using the event spec. @@ -46,11 +48,11 @@ def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, a payload = { 'accountId': pay_account.auth_account_id, 'nsfFee': float(fee_schedule.fee.amount), - **(additional_params or {}) + **additional_params } try: gcp_queue_publisher.publish_to_queue( - gcp_queue_publisher.QueueMessage( + QueueMessage( source=QueueSources.PAY_JOBS.value, message_type=message_type, payload=payload, @@ -69,7 +71,6 @@ def publish_mailer_events(message_type: str, pay_account: PaymentAccountModel, a def publish_statement_notification(pay_account: PaymentAccountModel, statement: StatementModel, total_amount_owing: float, emails: str) -> bool: """Publish payment statement notification message to the mailer queue.""" - message_type = QueueMessageTypes.STATEMENT_NOTIFICATION.value payload = { 'emailAddresses': emails, 'accountId': pay_account.auth_account_id, @@ -80,9 +81,9 @@ def publish_statement_notification(pay_account: PaymentAccountModel, statement: } try: gcp_queue_publisher.publish_to_queue( - gcp_queue_publisher.QueueMessage( + QueueMessage( source=QueueSources.PAY_JOBS.value, - message_type=message_type, + message_type=MessageType.STATEMENT_NOTIFICATION.value, payload=payload, topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') ) @@ -102,8 +103,8 @@ def publish_statement_notification(pay_account: PaymentAccountModel, statement: def publish_payment_notification(info: StatementNotificationInfo) -> bool: """Publish payment notification message to the mailer queue.""" - message_type = QueueMessageTypes.PAYMENT_DUE_NOTIFICATION.value if info.is_due \ - else QueueMessageTypes.PAYMENT_REMINDER_NOTIFICATION.value + notification_type = MessageType.STATEMENT_DUE_NOTIFICATION.value if info.is_due \ + else MessageType.STATEMENT_REMINDER_NOTIFICATION.value payload = { 'emailAddresses': info.emails, @@ -114,9 +115,9 @@ def publish_payment_notification(info: StatementNotificationInfo) -> bool: } try: gcp_queue_publisher.publish_to_queue( - gcp_queue_publisher.QueueMessage( + QueueMessage( source=QueueSources.PAY_JOBS.value, - message_type=message_type, + message_type=notification_type, payload=payload, topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') ) @@ -132,3 +133,4 @@ def publish_payment_notification(info: StatementNotificationInfo) -> bool: return False return True + diff --git a/pay-admin/requirements.txt b/pay-admin/requirements.txt new file mode 100644 index 000000000..a0d4f1ac5 --- /dev/null +++ b/pay-admin/requirements.txt @@ -0,0 +1,34 @@ +Authlib==1.3.0 +Flask-Admin==1.6.1 +Flask-SQLAlchemy==3.1.1 +Flask==3.0.2 +Jinja2==3.1.3 +MarkupSafe==2.1.5 +SQLAlchemy==2.0.28 +WTForms==3.1.2 +Werkzeug==3.0.1 +blinker==1.7.0 +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +cryptography==42.0.5 +flask-oidc==2.1.1 +greenlet==3.0.3 +gunicorn==21.2.0 +httplib2==0.22.0 +idna==3.6 +itsdangerous==2.1.2 +packaging==23.2 +psycopg2-binary==2.9.9 +pycparser==2.21 +pyparsing==3.1.2 +python-dotenv==1.0.1 +requests-futures==1.0.1 +requests==2.31.0 +typing_extensions==4.10.0 +urllib3==2.2.1 +-e git+https://github.com/bcgov/sbc-pay.git@feature-queue-python-upgrade#egg=pay-api&subdirectory=pay-api +-e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python +git+https://github.com/daxiom/simple-cloudevent.py.git +git+https://github.com/thorwolpert/flask-jwt-oidc.git diff --git a/pay-api/devops/vaults.json b/pay-api/devops/vaults.json deleted file mode 100644 index 5a1bc6bf6..000000000 --- a/pay-api/devops/vaults.json +++ /dev/null @@ -1,59 +0,0 @@ -[ - { - "vault": "shared", - "application": [ - "api-endpoints", - "encryption-key" - ] - }, - { - "vault": "keycloak", - "application": [ - "jwt-base", - "sbc-auth-admin" - ] - }, - { - "vault": "gcp-queue", - "application": [ - "base", - "account-events-listener", - "payment", - "account-mailer" - ] - }, - { - "vault": "payment-external-services", - "application": [ - "paybc", - "cfs" - ] - }, - { - "vault": "relationship", - "application": [ - "postgres-pay", - "pay-api", - "jwt" - ] - }, - { - "vault": "sentry", - "application": [ - "relationship-api" - ] - }, - { - "vault": "launchdarkly", - "application": [ - "pay" - ] - }, - { - "vault": "gcp-queue", - "application": [ - "gtksf3", - "topics" - ] - } -] diff --git a/pay-api/gunicorn_config.py b/pay-api/gunicorn_config.py index 5a05fdb3b..a3ab9633f 100755 --- a/pay-api/gunicorn_config.py +++ b/pay-api/gunicorn_config.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/manage.py b/pay-api/manage.py index 7f786c35b..8e1952a5c 100755 --- a/pay-api/manage.py +++ b/pay-api/manage.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/poetry.lock b/pay-api/poetry.lock index 3253cd826..49cd2bdc5 100644 --- a/pay-api/poetry.lock +++ b/pay-api/poetry.lock @@ -32,13 +32,13 @@ files = [ [[package]] name = "astroid" -version = "3.2.2" +version = "3.1.0" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, - {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, ] [[package]] @@ -62,13 +62,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.2.0" +version = "2.1.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false python-versions = ">=3.8" files = [ - {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, - {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, + {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, + {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, ] [package.dependencies] @@ -331,63 +331,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.3" +version = "7.4.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, ] [package.extras] @@ -522,13 +522,13 @@ tests = ["coverage", "coveralls", "dill", "mock", "nose"] [[package]] name = "faker" -version = "24.14.1" +version = "24.3.0" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-24.14.1-py3-none-any.whl", hash = "sha256:a5edba3aa17a1d689c8907e5b0cd1653079c2466a4807f083aa7b5f80a00225d"}, - {file = "Faker-24.14.1.tar.gz", hash = "sha256:380a3697e696ae4fcf50a93a3d9e0286fab7dfbf05a9caa4421fa4727c6b1e89"}, + {file = "Faker-24.3.0-py3-none-any.whl", hash = "sha256:9978025e765ba79f8bf6154c9630a9c2b7f9c9b0f175d4ad5e04b19a82a8d8d6"}, + {file = "Faker-24.3.0.tar.gz", hash = "sha256:5fb5aa9749d09971e04a41281ae3ceda9414f683d4810a694f8a8eebb8f9edec"}, ] [package.dependencies] @@ -646,13 +646,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.3.0" +version = "2.1.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, - {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, ] [package.dependencies] @@ -674,25 +674,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask-jwt-oidc" -version = "0.7.0" -description = "Opinionated flask oidc client" +name = "flask_jwt_oidc" +version = "0.3.0" +description = "Flask JWT OIDC" optional = false -python-versions = "^3.9" +python-versions = "*" files = [] develop = false [package.dependencies] -cachelib = "0.*" -Flask = ">=2" -python-jose = "^3.3.0" -six = "^1.16.0" +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" [package.source] type = "git" -url = "https://github.com/seeker25/flask-jwt-oidc.git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" +resolved_reference = "40cc811ccf70e838c5f7522fe8d83b7e58853539" [[package]] name = "flask-marshmallow" @@ -793,49 +793,27 @@ sqlalchemy = ">=2.0.16" [[package]] name = "freezegun" -version = "1.5.1" +version = "1.4.0" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, - {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, + {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, + {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, ] [package.dependencies] python-dateutil = ">=2.7" -[[package]] -name = "gcp-queue" -version = "0.3.0" -description = "" -optional = false -python-versions = "^3.9" -files = [] -develop = false - -[package.dependencies] -flask = ">=1" -google-auth = "^2.28.2" -google-cloud-pubsub = "^2.20.2" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} - -[package.source] -type = "git" -url = "https://github.com/seeker25/sbc-connect-common.git" -reference = "main" -resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" -subdirectory = "python/gcp-queue" - [[package]] name = "google-api-core" -version = "2.19.0" +version = "2.17.1" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, - {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, ] [package.dependencies] @@ -843,7 +821,6 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} -proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -854,13 +831,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.29.0" +version = "2.28.1" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, - {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, ] [package.dependencies] @@ -877,13 +854,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.21.2" +version = "2.20.0" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, - {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, ] [package.dependencies] @@ -1005,76 +982,84 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.64.0" +version = "1.62.1" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, - {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, - {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, - {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, - {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, - {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, - {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, - {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, - {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, - {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, - {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, - {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, - {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, - {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, - {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, - {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, - {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, - {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, - {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, - {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, - {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.64.0)"] +protobuf = ["grpcio-tools (>=1.62.1)"] [[package]] name = "grpcio-status" -version = "1.62.2" +version = "1.62.1" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, - {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.2" +grpcio = ">=1.62.1" protobuf = ">=4.21.6" [[package]] @@ -1229,26 +1214,28 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "8.2.1" +version = "9.2.2" description = "LaunchDarkly SDK for Python" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, - {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.22.0,<3" +urllib3 = ">=1.26.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1442,44 +1429,43 @@ flake8 = ">=5.0.0" [[package]] name = "pg8000" -version = "1.31.2" +version = "1.30.5" description = "PostgreSQL interface library" optional = false python-versions = ">=3.8" files = [ - {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, - {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, + {file = "pg8000-1.30.5-py3-none-any.whl", hash = "sha256:1abf18da652b0ad8e9cbfe57ed841c350b5330c33d8151303555db1fe5ce57f8"}, + {file = "pg8000-1.30.5.tar.gz", hash = "sha256:072f7ad00cd723695cb2e9fc02c1dfb84c781455e97b8de6f4c4281eea08078c"}, ] [package.dependencies] python-dateutil = ">=2.8.2" -scramp = ">=1.4.5" +scramp = ">=1.4.4" [[package]] name = "platformdirs" -version = "4.2.2" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.5.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1709,17 +1695,17 @@ files = [ [[package]] name = "pylint" -version = "3.2.2" +version = "3.1.0" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, - {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, ] [package.dependencies] -astroid = ">=3.2.2,<=3.3.0-dev0" +astroid = ">=3.1.0,<=3.2.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -1815,23 +1801,23 @@ files = [ [[package]] name = "pytest" -version = "8.2.1" +version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, - {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.4,<2.0" [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" @@ -1853,17 +1839,17 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.14.0" +version = "3.12.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, ] [package.dependencies] -pytest = ">=6.2.5" +pytest = ">=5.0" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] @@ -1983,18 +1969,18 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" +resolved_reference = "5f99e135214ae949c9af951d4aa0b88b1067d853" subdirectory = "python" [[package]] name = "scramp" -version = "1.4.5" +version = "1.4.4" description = "An implementation of the SCRAM protocol." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, - {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, + {file = "scramp-1.4.4-py3-none-any.whl", hash = "sha256:b142312df7c2977241d951318b7ee923d6b7a4f75ba0f05b621ece1ed616faa3"}, + {file = "scramp-1.4.4.tar.gz", hash = "sha256:b7022a140040f33cf863ab2657917ed05287a807b917950489b89b9f685d59bc"}, ] [package.dependencies] @@ -2058,18 +2044,19 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "70.0.0" +version = "69.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, - {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simple-cloudevent" @@ -2124,7 +2111,7 @@ develop = false type = "git" url = "https://github.com/bcgov/lear.git" reference = "feature-legal-name" -resolved_reference = "e5a432d1460dc84208465ef35c0c81ab02e66f51" +resolved_reference = "bb3209f8e8894c9b9f7be95a9fd871c644e2ec69" subdirectory = "python/common/sql-versioning" [[package]] @@ -2286,13 +2273,13 @@ twisted = ["twisted"] [[package]] name = "tomlkit" -version = "0.12.5" +version = "0.12.4" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, - {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, ] [[package]] @@ -2363,4 +2350,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "fbf5a52a364793793ad0d5944f9f222cc7fcc3af331f493284727ff4aa2cd5de" +content-hash = "6ed39989416242b0917aea66317b7c94e2d6bdc34e58c391ce23c15defbd55e6" diff --git a/pay-api/pyproject.toml b/pay-api/pyproject.toml index b8335b7a8..c9639f753 100644 --- a/pay-api/pyproject.toml +++ b/pay-api/pyproject.toml @@ -7,7 +7,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" -flask-caching = "2.3.0" +flask-caching = "2.1.0" flask-cors = "4.0.0" flask-migrate = "4.0.7" flask-moment = "1.0.5" @@ -35,10 +35,16 @@ cryptography = "42.0.5" dpath = "2.1.6" ecdsa = "0.18.0" expiringdict = "1.2.2" -flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} flask-marshmallow = "1.2.0" -gcp-queue = { git = "https://github.com/seeker25/sbc-connect-common.git", subdirectory = "python/gcp-queue", branch = "main" } +google-api-core = "2.17.1" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +googleapis-common-protos = "1.63.0" greenlet = "3.0.3" +grpc-google-iam-v1 = "0.13.0" +grpcio-status = "1.62.1" +grpcio = "1.62.1" gunicorn = "21.2.0" holidays = "0.37" idna = "3.6" @@ -46,7 +52,7 @@ itsdangerous = "2.1.2" jaeger-client = "4.8.0" jsonschema = "4.17.3" launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "8.2.1" +launchdarkly-server-sdk = "9.2.2" marshmallow-sqlalchemy = "1.0.0" marshmallow = "3.21.1" opentracing = "2.4.0" @@ -74,6 +80,7 @@ thrift = "0.16.0" tornado = "6.4" typing-extensions = "4.10.0" urllib3 = "2.2.1" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} pg8000 = "^1.30.5" sql-versioning = { git = "https://github.com/bcgov/lear.git", subdirectory = "python/common/sql-versioning", branch = "feature-legal-name" } diff --git a/pay-api/scripts/verify_license_headers.sh b/pay-api/scripts/verify_license_headers.sh index a160d3ac8..028b95c63 100755 --- a/pay-api/scripts/verify_license_headers.sh +++ b/pay-api/scripts/verify_license_headers.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ # limitations under the License. -COPYRIGHT="Copyright © 2024 Province of British Columbia" +COPYRIGHT="Copyright © 2019 Province of British Columbia" RET=0 for file in $(find $@ -not \( -path */venv -prune \) -not \( -path */migrations -prune \) -not \( -path */tests -prune \) -not \( -path */.egg* -prune \) -name \*.py) diff --git a/pay-api/setup.cfg b/pay-api/setup.cfg index ccbd8d7ce..4a29f38b9 100755 --- a/pay-api/setup.cfg +++ b/pay-api/setup.cfg @@ -9,7 +9,7 @@ classifiers = Topic :: Payment License :: OSI Approved :: Apache Software License Natural Language :: English - Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.7 license = Apache Software License Version 2.0 description = A short description of the project long_description = file: README.md diff --git a/pay-api/setup.py b/pay-api/setup.py index 59919617f..9f0ddb399 100755 --- a/pay-api/setup.py +++ b/pay-api/setup.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia. +# Copyright © 2019 Province of British Columbia. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index c5216cd3e..611fdcba2 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -48,7 +48,6 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')): app.config.from_object(config.CONFIGURATION[run_mode]) flags.init_app(app) - queue.init_app(app) db.init_app(app) queue.init_app(app) Migrate(app, db) diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index 5d2593124..408484a69 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -132,12 +132,14 @@ class _Config: # pylint: disable=too-few-public-methods 'PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL' ) - # PUB/SUB - PUB: auth-event-dev, account-mailer-dev, business-pay-dev, namex-pay-dev - ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') - AUTH_EVENT_TOPIC = os.getenv('AUTH_EVENT_TOPIC', 'auth-event-dev') - BUSINESS_PAY_TOPIC = os.getenv('BUSINESS_PAY_TOPIC', 'business-pay-dev') - GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) - NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', 'namex-pay-dev') + # GCP PubSub + AUDIENCE = os.getenv('AUDIENCE', None) + GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) + PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher') + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) + EVENT_LISTENER_TOPIC = os.getenv('EVENT_LISTENER_TOPIC', None) + NAMEX_PAY_TOPIC = os.getenv('NAMEX_PAY_TOPIC', None) + BUSINESS_PAY_TOPIC = os.getenv('BUSINESS_PAY_TOPIC', None) # API Endpoints AUTH_API_URL = os.getenv('AUTH_API_URL', '') diff --git a/pay-api/src/pay_api/exceptions/__init__.py b/pay-api/src/pay_api/exceptions/__init__.py index 07dd45970..03c47bcea 100755 --- a/pay-api/src/pay_api/exceptions/__init__.py +++ b/pay-api/src/pay_api/exceptions/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/factory/__init__.py b/pay-api/src/pay_api/factory/__init__.py index 2f5095570..c24369f03 100644 --- a/pay-api/src/pay_api/factory/__init__.py +++ b/pay-api/src/pay_api/factory/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/factory/payment_system_factory.py b/pay-api/src/pay_api/factory/payment_system_factory.py index b9000b55f..f5ffbb998 100644 --- a/pay-api/src/pay_api/factory/payment_system_factory.py +++ b/pay-api/src/pay_api/factory/payment_system_factory.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/__init__.py b/pay-api/src/pay_api/models/__init__.py index c8f85920e..31ba7d754 100755 --- a/pay-api/src/pay_api/models/__init__.py +++ b/pay-api/src/pay_api/models/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/account_fee.py b/pay-api/src/pay_api/models/account_fee.py index a23a340ef..7df2f5663 100644 --- a/pay-api/src/pay_api/models/account_fee.py +++ b/pay-api/src/pay_api/models/account_fee.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/audit.py b/pay-api/src/pay_api/models/audit.py index 5845ac386..663ee514b 100644 --- a/pay-api/src/pay_api/models/audit.py +++ b/pay-api/src/pay_api/models/audit.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/base_model.py b/pay-api/src/pay_api/models/base_model.py index 7ad36bbac..22a10e467 100644 --- a/pay-api/src/pay_api/models/base_model.py +++ b/pay-api/src/pay_api/models/base_model.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/base_schema.py b/pay-api/src/pay_api/models/base_schema.py index d628f1cb6..e4255a85b 100644 --- a/pay-api/src/pay_api/models/base_schema.py +++ b/pay-api/src/pay_api/models/base_schema.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/cfs_account.py b/pay-api/src/pay_api/models/cfs_account.py index 2d13c78ac..b16eb778b 100644 --- a/pay-api/src/pay_api/models/cfs_account.py +++ b/pay-api/src/pay_api/models/cfs_account.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/cfs_account_status_code.py b/pay-api/src/pay_api/models/cfs_account_status_code.py index b13ba1a3c..1507d6495 100644 --- a/pay-api/src/pay_api/models/cfs_account_status_code.py +++ b/pay-api/src/pay_api/models/cfs_account_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/code_table.py b/pay-api/src/pay_api/models/code_table.py index 9cb7afd11..aac84d3c1 100644 --- a/pay-api/src/pay_api/models/code_table.py +++ b/pay-api/src/pay_api/models/code_table.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/comment.py b/pay-api/src/pay_api/models/comment.py index 1f57b9f43..eece02a87 100644 --- a/pay-api/src/pay_api/models/comment.py +++ b/pay-api/src/pay_api/models/comment.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/corp_type.py b/pay-api/src/pay_api/models/corp_type.py index 587e59cc5..0174857b7 100644 --- a/pay-api/src/pay_api/models/corp_type.py +++ b/pay-api/src/pay_api/models/corp_type.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/credit.py b/pay-api/src/pay_api/models/credit.py index d604f1e3c..1b010b921 100644 --- a/pay-api/src/pay_api/models/credit.py +++ b/pay-api/src/pay_api/models/credit.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/db.py b/pay-api/src/pay_api/models/db.py index 9ee95c8fd..dee2fa81c 100755 --- a/pay-api/src/pay_api/models/db.py +++ b/pay-api/src/pay_api/models/db.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/disbursement_status_code.py b/pay-api/src/pay_api/models/disbursement_status_code.py index bed1297e1..7ffb5720c 100644 --- a/pay-api/src/pay_api/models/disbursement_status_code.py +++ b/pay-api/src/pay_api/models/disbursement_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/ejv_file.py b/pay-api/src/pay_api/models/ejv_file.py index 7c44c6d8e..d0f5b4343 100644 --- a/pay-api/src/pay_api/models/ejv_file.py +++ b/pay-api/src/pay_api/models/ejv_file.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/ejv_header.py b/pay-api/src/pay_api/models/ejv_header.py index c6fe07fa1..aa514e4f8 100644 --- a/pay-api/src/pay_api/models/ejv_header.py +++ b/pay-api/src/pay_api/models/ejv_header.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/ejv_link.py b/pay-api/src/pay_api/models/ejv_link.py index 210803a5a..4055df51d 100644 --- a/pay-api/src/pay_api/models/ejv_link.py +++ b/pay-api/src/pay_api/models/ejv_link.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/error_code.py b/pay-api/src/pay_api/models/error_code.py index 630f0d32d..c0190cbb7 100644 --- a/pay-api/src/pay_api/models/error_code.py +++ b/pay-api/src/pay_api/models/error_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/fee_code.py b/pay-api/src/pay_api/models/fee_code.py index 5c5854173..b0992c696 100644 --- a/pay-api/src/pay_api/models/fee_code.py +++ b/pay-api/src/pay_api/models/fee_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/fee_schedule.py b/pay-api/src/pay_api/models/fee_schedule.py index 044cc567a..70686b08f 100644 --- a/pay-api/src/pay_api/models/fee_schedule.py +++ b/pay-api/src/pay_api/models/fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/filing_type.py b/pay-api/src/pay_api/models/filing_type.py index 08d8c010a..08f45fb84 100644 --- a/pay-api/src/pay_api/models/filing_type.py +++ b/pay-api/src/pay_api/models/filing_type.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice.py b/pay-api/src/pay_api/models/invoice.py index 1627d5fd4..cdafa08bb 100644 --- a/pay-api/src/pay_api/models/invoice.py +++ b/pay-api/src/pay_api/models/invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_batch.py b/pay-api/src/pay_api/models/invoice_batch.py index 27ee84d97..7d769dc11 100644 --- a/pay-api/src/pay_api/models/invoice_batch.py +++ b/pay-api/src/pay_api/models/invoice_batch.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_batch_link.py b/pay-api/src/pay_api/models/invoice_batch_link.py index 111a62bcc..2c1451d6b 100644 --- a/pay-api/src/pay_api/models/invoice_batch_link.py +++ b/pay-api/src/pay_api/models/invoice_batch_link.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_reference.py b/pay-api/src/pay_api/models/invoice_reference.py index dd2f8747c..fcfb903ca 100644 --- a/pay-api/src/pay_api/models/invoice_reference.py +++ b/pay-api/src/pay_api/models/invoice_reference.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_reference_status_code.py b/pay-api/src/pay_api/models/invoice_reference_status_code.py index 998ae5892..ea707dd5e 100644 --- a/pay-api/src/pay_api/models/invoice_reference_status_code.py +++ b/pay-api/src/pay_api/models/invoice_reference_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/invoice_status_code.py b/pay-api/src/pay_api/models/invoice_status_code.py index 696fa15d3..ce92ef38c 100644 --- a/pay-api/src/pay_api/models/invoice_status_code.py +++ b/pay-api/src/pay_api/models/invoice_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/line_item_status_code.py b/pay-api/src/pay_api/models/line_item_status_code.py index 5fe9ea968..4f7d86690 100644 --- a/pay-api/src/pay_api/models/line_item_status_code.py +++ b/pay-api/src/pay_api/models/line_item_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/non_sufficient_funds.py b/pay-api/src/pay_api/models/non_sufficient_funds.py index 21dc9bd13..deb5169ef 100644 --- a/pay-api/src/pay_api/models/non_sufficient_funds.py +++ b/pay-api/src/pay_api/models/non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/notification_status_code.py b/pay-api/src/pay_api/models/notification_status_code.py index b86942693..856ae0aa6 100644 --- a/pay-api/src/pay_api/models/notification_status_code.py +++ b/pay-api/src/pay_api/models/notification_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment.py b/pay-api/src/pay_api/models/payment.py index fc996bc5b..be3c35823 100644 --- a/pay-api/src/pay_api/models/payment.py +++ b/pay-api/src/pay_api/models/payment.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_account.py b/pay-api/src/pay_api/models/payment_account.py index 46bbc179f..3e2b7b72a 100644 --- a/pay-api/src/pay_api/models/payment_account.py +++ b/pay-api/src/pay_api/models/payment_account.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_line_item.py b/pay-api/src/pay_api/models/payment_line_item.py index 62635f4a5..85a5acfd7 100644 --- a/pay-api/src/pay_api/models/payment_line_item.py +++ b/pay-api/src/pay_api/models/payment_line_item.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_method.py b/pay-api/src/pay_api/models/payment_method.py index ed9b4edb7..8f77ef949 100644 --- a/pay-api/src/pay_api/models/payment_method.py +++ b/pay-api/src/pay_api/models/payment_method.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_status_code.py b/pay-api/src/pay_api/models/payment_status_code.py index bb4f92378..6fcf9da34 100644 --- a/pay-api/src/pay_api/models/payment_status_code.py +++ b/pay-api/src/pay_api/models/payment_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_system.py b/pay-api/src/pay_api/models/payment_system.py index 91a0ab6b7..dd6f29074 100644 --- a/pay-api/src/pay_api/models/payment_system.py +++ b/pay-api/src/pay_api/models/payment_system.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/payment_transaction.py b/pay-api/src/pay_api/models/payment_transaction.py index 0e38d3966..ea1877c14 100644 --- a/pay-api/src/pay_api/models/payment_transaction.py +++ b/pay-api/src/pay_api/models/payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/receipt.py b/pay-api/src/pay_api/models/receipt.py index a9f617f94..91b3faa27 100644 --- a/pay-api/src/pay_api/models/receipt.py +++ b/pay-api/src/pay_api/models/receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/refund.py b/pay-api/src/pay_api/models/refund.py index bba73faed..c8db6e5ce 100644 --- a/pay-api/src/pay_api/models/refund.py +++ b/pay-api/src/pay_api/models/refund.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/routing_slip.py b/pay-api/src/pay_api/models/routing_slip.py index a6c3163fe..8a5cd062c 100644 --- a/pay-api/src/pay_api/models/routing_slip.py +++ b/pay-api/src/pay_api/models/routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/routing_slip_status_code.py b/pay-api/src/pay_api/models/routing_slip_status_code.py index 9f7273aed..f1ae33e70 100644 --- a/pay-api/src/pay_api/models/routing_slip_status_code.py +++ b/pay-api/src/pay_api/models/routing_slip_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement.py b/pay-api/src/pay_api/models/statement.py index 6899525bd..422bf1ffd 100644 --- a/pay-api/src/pay_api/models/statement.py +++ b/pay-api/src/pay_api/models/statement.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement_invoices.py b/pay-api/src/pay_api/models/statement_invoices.py index 92060cb53..5dea45eec 100644 --- a/pay-api/src/pay_api/models/statement_invoices.py +++ b/pay-api/src/pay_api/models/statement_invoices.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement_recipients.py b/pay-api/src/pay_api/models/statement_recipients.py index 531fffd62..5a89f4a4b 100644 --- a/pay-api/src/pay_api/models/statement_recipients.py +++ b/pay-api/src/pay_api/models/statement_recipients.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/statement_settings.py b/pay-api/src/pay_api/models/statement_settings.py index 325b31b80..3b4eacbbd 100644 --- a/pay-api/src/pay_api/models/statement_settings.py +++ b/pay-api/src/pay_api/models/statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/models/transaction_status_code.py b/pay-api/src/pay_api/models/transaction_status_code.py index 615a1c9ae..c42128c72 100644 --- a/pay-api/src/pay_api/models/transaction_status_code.py +++ b/pay-api/src/pay_api/models/transaction_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/__init__.py b/pay-api/src/pay_api/resources/__init__.py index 13c649a27..b9e0dcbb5 100644 --- a/pay-api/src/pay_api/resources/__init__.py +++ b/pay-api/src/pay_api/resources/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/ops.py b/pay-api/src/pay_api/resources/ops.py index c399771a1..2926fa831 100755 --- a/pay-api/src/pay_api/resources/ops.py +++ b/pay-api/src/pay_api/resources/ops.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account.py b/pay-api/src/pay_api/resources/v1/account.py index ad54e40ea..097a638ae 100644 --- a/pay-api/src/pay_api/resources/v1/account.py +++ b/pay-api/src/pay_api/resources/v1/account.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account_statements.py b/pay-api/src/pay_api/resources/v1/account_statements.py index 8559819d7..32a348c61 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements.py +++ b/pay-api/src/pay_api/resources/v1/account_statements.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py index 0c8dba6ef..59beb51af 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/account_statements_settings.py b/pay-api/src/pay_api/resources/v1/account_statements_settings.py index 60cdc4335..e1f4b42e7 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_settings.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/bank_accounts.py b/pay-api/src/pay_api/resources/v1/bank_accounts.py index 2783c5855..9398164e8 100644 --- a/pay-api/src/pay_api/resources/v1/bank_accounts.py +++ b/pay-api/src/pay_api/resources/v1/bank_accounts.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/code.py b/pay-api/src/pay_api/resources/v1/code.py index a578bd1e8..d1f3d64d6 100644 --- a/pay-api/src/pay_api/resources/v1/code.py +++ b/pay-api/src/pay_api/resources/v1/code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/distributions.py b/pay-api/src/pay_api/resources/v1/distributions.py index 3f9c1a94b..42e5b7b75 100644 --- a/pay-api/src/pay_api/resources/v1/distributions.py +++ b/pay-api/src/pay_api/resources/v1/distributions.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/fas/__init__.py b/pay-api/src/pay_api/resources/v1/fas/__init__.py index d248d9f54..7ec3a8f2d 100644 --- a/pay-api/src/pay_api/resources/v1/fas/__init__.py +++ b/pay-api/src/pay_api/resources/v1/fas/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/fas/refund.py b/pay-api/src/pay_api/resources/v1/fas/refund.py index b6ca9cd39..78e098f32 100644 --- a/pay-api/src/pay_api/resources/v1/fas/refund.py +++ b/pay-api/src/pay_api/resources/v1/fas/refund.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py index f8311e8f5..aab02d8be 100644 --- a/pay-api/src/pay_api/resources/v1/fas/routing_slip.py +++ b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -190,7 +190,6 @@ def get_routing_slip_comments(routing_slip_number: str): def post_routing_slip_comment(routing_slip_number: str): """Create comment for a slip.""" current_app.logger.info(' InvoiceReference: """Return a static invoice number for direct pay.""" current_app.logger.debug(' PaymentAccount: pay_account.save() pa_service = cls.find_by_id(pay_account.id) if not already_has_eft_enabled: - payload = pa_service.create_account_event_payload(QueueMessageTypes.EFT_AVAILABLE_NOTIFICATION.value) - pa_service._publish_queue_message(payload, QueueMessageTypes.EFT_AVAILABLE_NOTIFICATION.value) + payload = pa_service.create_account_event_payload(MessageType.EFT_AVAILABLE_NOTIFICATION.value) + pa_service._publish_queue_message(payload, MessageType.EFT_AVAILABLE_NOTIFICATION.value) return pa_service diff --git a/pay-api/src/pay_api/services/payment_line_item.py b/pay-api/src/pay_api/services/payment_line_item.py index 9098efd7f..6c3b36d4f 100644 --- a/pay-api/src/pay_api/services/payment_line_item.py +++ b/pay-api/src/pay_api/services/payment_line_item.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/payment_service.py b/pay-api/src/pay_api/services/payment_service.py index feecefa92..6da906950 100644 --- a/pay-api/src/pay_api/services/payment_service.py +++ b/pay-api/src/pay_api/services/payment_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/payment_transaction.py b/pay-api/src/pay_api/services/payment_transaction.py index 3fa1cea81..d93b02079 100644 --- a/pay-api/src/pay_api/services/payment_transaction.py +++ b/pay-api/src/pay_api/services/payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -16,15 +16,13 @@ from __future__ import annotations import uuid -from dataclasses import asdict +from dataclasses import asdict, dataclass from datetime import datetime -from typing import Dict, List +from typing import Dict, List, Optional import humps from flask import current_app from sentry_sdk import capture_message -from sbc_common_components.utils.dataclasses import PaymentToken -from sbc_common_components.utils.enums import QueueMessageTypes from pay_api.exceptions import BusinessException, ServiceUnavailableException from pay_api.factory.payment_system_factory import PaymentSystemFactory @@ -38,13 +36,23 @@ from pay_api.services.payment_account import PaymentAccount from pay_api.services.receipt import Receipt from pay_api.utils.enums import ( - InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, QueueSources, TransactionStatus) + InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, QueueSources, TransactionStatus) from pay_api.utils.errors import Error from pay_api.utils.util import get_topic_for_corp_type, is_valid_redirect_url from .payment import Payment +@dataclass +class PaymentToken: + """Payment Token payload common interface for LEAR and Names.""" + + id: Optional[str] = None + status_code: Optional[str] = None + filing_identifier: Optional[str] = None + corp_type_code: Optional[str] = None + + class PaymentTransaction: # pylint: disable=too-many-instance-attributes, too-many-public-methods """Service to manage Payment transaction operations.""" @@ -503,7 +511,7 @@ def publish_status(transaction_dao: PaymentTransactionModel, invoice: Invoice): gcp_queue_publisher.publish_to_queue( QueueMessage( source=QueueSources.PAY_API.value, - message_type=QueueMessageTypes.PAYMENT.value, + message_type=MessageType.PAYMENT.value, payload=PaymentTransaction.create_event_payload(invoice, status_code), topic=get_topic_for_corp_type(invoice.corp_type_code) ) diff --git a/pay-api/src/pay_api/services/receipt.py b/pay-api/src/pay_api/services/receipt.py index 7d1312de0..3cc5a108e 100644 --- a/pay-api/src/pay_api/services/receipt.py +++ b/pay-api/src/pay_api/services/receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/refund.py b/pay-api/src/pay_api/services/refund.py index 3fc23d9c3..d0d21b8e7 100644 --- a/pay-api/src/pay_api/services/refund.py +++ b/pay-api/src/pay_api/services/refund.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/statement.py b/pay-api/src/pay_api/services/statement.py index ab0f9e77f..5a23b00c5 100644 --- a/pay-api/src/pay_api/services/statement.py +++ b/pay-api/src/pay_api/services/statement.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/statement_recipients.py b/pay-api/src/pay_api/services/statement_recipients.py index 8180cf7c7..750577f68 100644 --- a/pay-api/src/pay_api/services/statement_recipients.py +++ b/pay-api/src/pay_api/services/statement_recipients.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/statement_settings.py b/pay-api/src/pay_api/services/statement_settings.py index c233b96f3..602c943ce 100644 --- a/pay-api/src/pay_api/services/statement_settings.py +++ b/pay-api/src/pay_api/services/statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/services/wire_service.py b/pay-api/src/pay_api/services/wire_service.py index 20bfa2333..1d4d20d91 100644 --- a/pay-api/src/pay_api/services/wire_service.py +++ b/pay-api/src/pay_api/services/wire_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/__init__.py b/pay-api/src/pay_api/utils/__init__.py index 8150902f6..9b5291dad 100755 --- a/pay-api/src/pay_api/utils/__init__.py +++ b/pay-api/src/pay_api/utils/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/auth.py b/pay-api/src/pay_api/utils/auth.py index 19435fc86..c900a8ab7 100644 --- a/pay-api/src/pay_api/utils/auth.py +++ b/pay-api/src/pay_api/utils/auth.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/cache.py b/pay-api/src/pay_api/utils/cache.py index 1f57a55cf..c1011ca67 100644 --- a/pay-api/src/pay_api/utils/cache.py +++ b/pay-api/src/pay_api/utils/cache.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/constants.py b/pay-api/src/pay_api/utils/constants.py index 657e5793a..a01f3d7c2 100644 --- a/pay-api/src/pay_api/utils/constants.py +++ b/pay-api/src/pay_api/utils/constants.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/enums.py b/pay-api/src/pay_api/utils/enums.py index 7a91fd76a..1437c2e02 100644 --- a/pay-api/src/pay_api/utils/enums.py +++ b/pay-api/src/pay_api/utils/enums.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -195,9 +195,6 @@ class CfsAccountStatus(Enum): class CorpType(Enum): """Corp Type.""" - BTR = 'BTR' - ESRA = 'ESRA' - MHR = 'MHR' NRO = 'NRO' PPR = 'PPR' VS = 'VS' @@ -219,7 +216,6 @@ class Product(Enum): """Product.""" BUSINESS = 'BUSINESS' - NRO = 'NRO' class RoutingSlipStatus(Enum): @@ -353,6 +349,32 @@ class EFTShortnameStatus(Enum): PENDING = 'PENDING' +class MessageType(Enum): + """Queue Event Types.""" + + EFT_AVAILABLE_NOTIFICATION = 'eftAvailableNotification' + PAD_PAYMENT_SUCCESS = 'PAD.PaymentSuccess' + PAD_ACCOUNT_CREATE = 'padAccountCreate' + NSF_LOCK_ACCOUNT = 'lockAccount' + NSF_UNLOCK_ACCOUNT = 'unlockAccount' + STATEMENT_NOTIFICATION = 'statementNotification' + STATEMENT_DUE_NOTIFICATION = 'statementDueNotification' + STATEMENT_REMINDER_NOTIFICATION = 'statementReminderNotification' + PAYMENT = 'payment' + EJV_FAILED = 'ejvFailed' + CAS_UPLOADED = 'casSettlementUploaded' + INCORPORATION = 'incorporationApplication' + REGISTRATION = 'registration' + CGI_ACK_RECEIVED = 'ACKReceived' + CGI_FEEDBACK_RECEIVED = 'FEEDBACKReceived' + EFT_FILE_UPLOADED = 'eftFileUploaded' + EFT_INVOICE_CREATED = 'eft.invoiceCreated' + PAD_INVOICE_CREATED = 'pad.invoiceCreated' + PAD_SETUP_FAILED = 'PadSetupFailed' + PAD_CONFIRMATION_PERIOD_OVER = 'confirmationPeriodOver' + ONLINE_BANKING_OUTSTANDING_INVOICE = 'ob.outstandingInvoice' + + class PaymentDetailsGlStatus(Enum): """Payment details GL status.""" diff --git a/pay-api/src/pay_api/utils/errors.py b/pay-api/src/pay_api/utils/errors.py index 2f7a747d4..231c3c87c 100644 --- a/pay-api/src/pay_api/utils/errors.py +++ b/pay-api/src/pay_api/utils/errors.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/logging.py b/pay-api/src/pay_api/utils/logging.py index 905300f5a..8568f87dd 100755 --- a/pay-api/src/pay_api/utils/logging.py +++ b/pay-api/src/pay_api/utils/logging.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/paybc_transaction_error_message.py b/pay-api/src/pay_api/utils/paybc_transaction_error_message.py index 73eb1b628..c93067cbb 100644 --- a/pay-api/src/pay_api/utils/paybc_transaction_error_message.py +++ b/pay-api/src/pay_api/utils/paybc_transaction_error_message.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/run_version.py b/pay-api/src/pay_api/utils/run_version.py index f447beb71..41c42458b 100755 --- a/pay-api/src/pay_api/utils/run_version.py +++ b/pay-api/src/pay_api/utils/run_version.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/user_context.py b/pay-api/src/pay_api/utils/user_context.py index b31b5a813..2719e323c 100644 --- a/pay-api/src/pay_api/utils/user_context.py +++ b/pay-api/src/pay_api/utils/user_context.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/src/pay_api/utils/util.py b/pay-api/src/pay_api/utils/util.py index 631d31f8f..a2640f985 100755 --- a/pay-api/src/pay_api/utils/util.py +++ b/pay-api/src/pay_api/utils/util.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,11 +28,9 @@ from dpath import get as dpath_get from flask import current_app -from pay_api.services.code import Code as CodeService - from .constants import DT_SHORT_FORMAT from .converter import Converter -from .enums import Code, CorpType, Product, StatementFrequency +from .enums import CorpType, StatementFrequency def cors_preflight(methods: str = 'GET'): @@ -276,12 +274,14 @@ def cents_to_decimal(amount: int): def get_topic_for_corp_type(corp_type: str): """Return a topic to direct the queue message to.""" - if corp_type == CorpType.NRO.value: - return current_app.config.get('NAMEX_PAY_TOPIC') - product_code = CodeService.find_code_value_by_type_and_code(Code.CORP_TYPE.value, corp_type).get('product') - if product_code == Product.BUSINESS.value: - return current_app.config.get('BUSINESS_PAY_TOPIC') - return None + match corp_type: + case CorpType.NRO.value: + return current_app.config.get('NAMEX_PAY_TOPIC') + # Unused for now, intentionally don't send a queue message for these. + case CorpType.PPR.value | CorpType.VS.value | CorpType.CSO.value: + return None + case _: + return current_app.config.get('BUSINESS_PAY_TOPIC') def unstructure_schema_items(schema, items): diff --git a/pay-api/src/pay_api/version.py b/pay-api/src/pay_api/version.py index f80e8c8d1..7003370ab 100644 --- a/pay-api/src/pay_api/version.py +++ b/pay-api/src/pay_api/version.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/__init__.py b/pay-api/tests/__init__.py index 212195959..5b2e2f73f 100755 --- a/pay-api/tests/__init__.py +++ b/pay-api/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/conftest.py b/pay-api/tests/conftest.py index e6b606c2e..0e56f8990 100755 --- a/pay-api/tests/conftest.py +++ b/pay-api/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,27 +35,10 @@ def app(): @pytest.fixture(autouse=True) -def mock_pub_sub_call(mocker): - """Mock pub sub call.""" - class Expando(object): - """Expando class.""" - - class PublisherMock: - """Publisher Mock.""" - - def __init__(self, *args, **kwargs): - def result(): - """Return true for mock.""" - return True - self.result = result - - def publish(self, *args, **kwargs): - """Publish mock.""" - ex = Expando() - ex.result = self.result - return ex - - mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) +def mock_queue_publish(monkeypatch): + """Mock queue publish.""" + # TODO: so it can be used like this from gcp_queue_publisher import publish_to_queue + monkeypatch.setattr('pay_api.services.gcp_queue_publisher.publish_to_queue', lambda *args, **kwargs: None) @pytest.fixture(scope='function') diff --git a/pay-api/tests/unit/__init__.py b/pay-api/tests/unit/__init__.py index 65e08c540..d755f6a1d 100755 --- a/pay-api/tests/unit/__init__.py +++ b/pay-api/tests/unit/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/__init__.py b/pay-api/tests/unit/api/__init__.py index b8fd2aced..02d5354ad 100755 --- a/pay-api/tests/unit/api/__init__.py +++ b/pay-api/tests/unit/api/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/fas/__init__.py b/pay-api/tests/unit/api/fas/__init__.py index c538a08a1..5bef134ec 100644 --- a/pay-api/tests/unit/api/fas/__init__.py +++ b/pay-api/tests/unit/api/fas/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/fas/test_refund.py b/pay-api/tests/unit/api/fas/test_refund.py index de9f3530f..18cfc28f2 100644 --- a/pay-api/tests/unit/api/fas/test_refund.py +++ b/pay-api/tests/unit/api/fas/test_refund.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/fas/test_routing_slip.py b/pay-api/tests/unit/api/fas/test_routing_slip.py index 64eab26fe..de829e550 100755 --- a/pay-api/tests/unit/api/fas/test_routing_slip.py +++ b/pay-api/tests/unit/api/fas/test_routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_account.py b/pay-api/tests/unit/api/test_account.py index 0aeda1e47..d417df26b 100755 --- a/pay-api/tests/unit/api/test_account.py +++ b/pay-api/tests/unit/api/test_account.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_bank_accounts.py b/pay-api/tests/unit/api/test_bank_accounts.py index ccab4f65b..ee49c5145 100755 --- a/pay-api/tests/unit/api/test_bank_accounts.py +++ b/pay-api/tests/unit/api/test_bank_accounts.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_code.py b/pay-api/tests/unit/api/test_code.py index 5006fce95..e8d1020a2 100755 --- a/pay-api/tests/unit/api/test_code.py +++ b/pay-api/tests/unit/api/test_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_distributions.py b/pay-api/tests/unit/api/test_distributions.py index 7e2b08673..2b3f0d799 100755 --- a/pay-api/tests/unit/api/test_distributions.py +++ b/pay-api/tests/unit/api/test_distributions.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_fee.py b/pay-api/tests/unit/api/test_fee.py index f8cd3928e..594aaa503 100755 --- a/pay-api/tests/unit/api/test_fee.py +++ b/pay-api/tests/unit/api/test_fee.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_fee_schedule.py b/pay-api/tests/unit/api/test_fee_schedule.py index c6bc5638b..704517346 100755 --- a/pay-api/tests/unit/api/test_fee_schedule.py +++ b/pay-api/tests/unit/api/test_fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_invoice.py b/pay-api/tests/unit/api/test_invoice.py index 253cb3fe7..a8fbe8d6a 100755 --- a/pay-api/tests/unit/api/test_invoice.py +++ b/pay-api/tests/unit/api/test_invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_meta.py b/pay-api/tests/unit/api/test_meta.py index 02757760d..52df42ed2 100755 --- a/pay-api/tests/unit/api/test_meta.py +++ b/pay-api/tests/unit/api/test_meta.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_non_sufficient_funds.py b/pay-api/tests/unit/api/test_non_sufficient_funds.py index ba7638669..74dad35f0 100644 --- a/pay-api/tests/unit/api/test_non_sufficient_funds.py +++ b/pay-api/tests/unit/api/test_non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_ops.py b/pay-api/tests/unit/api/test_ops.py index c081b9aa5..ca4473f74 100755 --- a/pay-api/tests/unit/api/test_ops.py +++ b/pay-api/tests/unit/api/test_ops.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_payment.py b/pay-api/tests/unit/api/test_payment.py index 114125990..7aceb0dac 100755 --- a/pay-api/tests/unit/api/test_payment.py +++ b/pay-api/tests/unit/api/test_payment.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_payment_request.py b/pay-api/tests/unit/api/test_payment_request.py index aa06aa51f..b7348b6e5 100755 --- a/pay-api/tests/unit/api/test_payment_request.py +++ b/pay-api/tests/unit/api/test_payment_request.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_receipt.py b/pay-api/tests/unit/api/test_receipt.py index a5ba50b64..f4ad0a0ee 100644 --- a/pay-api/tests/unit/api/test_receipt.py +++ b/pay-api/tests/unit/api/test_receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_refund.py b/pay-api/tests/unit/api/test_refund.py index d92c23bb1..69b15993c 100644 --- a/pay-api/tests/unit/api/test_refund.py +++ b/pay-api/tests/unit/api/test_refund.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_statement.py b/pay-api/tests/unit/api/test_statement.py index d8e7463c6..f29c80eb1 100755 --- a/pay-api/tests/unit/api/test_statement.py +++ b/pay-api/tests/unit/api/test_statement.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_statement_settings.py b/pay-api/tests/unit/api/test_statement_settings.py index 83c6bc3df..3bacd7f0c 100755 --- a/pay-api/tests/unit/api/test_statement_settings.py +++ b/pay-api/tests/unit/api/test_statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/api/test_transaction.py b/pay-api/tests/unit/api/test_transaction.py index 3d2a83d04..f1315518a 100755 --- a/pay-api/tests/unit/api/test_transaction.py +++ b/pay-api/tests/unit/api/test_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/conf/__init__.py b/pay-api/tests/unit/conf/__init__.py index e369ad9a8..47a9b4f52 100755 --- a/pay-api/tests/unit/conf/__init__.py +++ b/pay-api/tests/unit/conf/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/conf/test_configuration.py b/pay-api/tests/unit/conf/test_configuration.py index 4e3dcf323..fe7515de9 100755 --- a/pay-api/tests/unit/conf/test_configuration.py +++ b/pay-api/tests/unit/conf/test_configuration.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia. +# Copyright © 2019 Province of British Columbia. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/conf/test_version.py b/pay-api/tests/unit/conf/test_version.py index 9e6571073..c21d371ee 100755 --- a/pay-api/tests/unit/conf/test_version.py +++ b/pay-api/tests/unit/conf/test_version.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/factory/__init__.py b/pay-api/tests/unit/factory/__init__.py index b8fd2aced..02d5354ad 100644 --- a/pay-api/tests/unit/factory/__init__.py +++ b/pay-api/tests/unit/factory/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/factory/test_payment_system_factory.py b/pay-api/tests/unit/factory/test_payment_system_factory.py index 932b56394..3e52c2b4f 100644 --- a/pay-api/tests/unit/factory/test_payment_system_factory.py +++ b/pay-api/tests/unit/factory/test_payment_system_factory.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/__init__.py b/pay-api/tests/unit/models/__init__.py index c0d5410ee..4a0c382d6 100755 --- a/pay-api/tests/unit/models/__init__.py +++ b/pay-api/tests/unit/models/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_comment.py b/pay-api/tests/unit/models/test_comment.py index 26694f80b..8cd96b28d 100644 --- a/pay-api/tests/unit/models/test_comment.py +++ b/pay-api/tests/unit/models/test_comment.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_corp_type.py b/pay-api/tests/unit/models/test_corp_type.py index 5541b42ce..1a90df909 100644 --- a/pay-api/tests/unit/models/test_corp_type.py +++ b/pay-api/tests/unit/models/test_corp_type.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_fee_code.py b/pay-api/tests/unit/models/test_fee_code.py index d3bed9c4b..82a75f1e3 100644 --- a/pay-api/tests/unit/models/test_fee_code.py +++ b/pay-api/tests/unit/models/test_fee_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_fee_schedule.py b/pay-api/tests/unit/models/test_fee_schedule.py index b23f934fe..89d7b43fb 100644 --- a/pay-api/tests/unit/models/test_fee_schedule.py +++ b/pay-api/tests/unit/models/test_fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_filing_type.py b/pay-api/tests/unit/models/test_filing_type.py index 608a77240..f5cc35831 100644 --- a/pay-api/tests/unit/models/test_filing_type.py +++ b/pay-api/tests/unit/models/test_filing_type.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_invoice.py b/pay-api/tests/unit/models/test_invoice.py index 4878a3799..3b2e999c4 100644 --- a/pay-api/tests/unit/models/test_invoice.py +++ b/pay-api/tests/unit/models/test_invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_non_sufficient_funds.py b/pay-api/tests/unit/models/test_non_sufficient_funds.py index 6782ec2b1..8f88ce59f 100644 --- a/pay-api/tests/unit/models/test_non_sufficient_funds.py +++ b/pay-api/tests/unit/models/test_non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment.py b/pay-api/tests/unit/models/test_payment.py index e3b474096..fde0ebfe4 100644 --- a/pay-api/tests/unit/models/test_payment.py +++ b/pay-api/tests/unit/models/test_payment.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_account.py b/pay-api/tests/unit/models/test_payment_account.py index 9dd064c5e..0e6b35724 100644 --- a/pay-api/tests/unit/models/test_payment_account.py +++ b/pay-api/tests/unit/models/test_payment_account.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_method.py b/pay-api/tests/unit/models/test_payment_method.py index 5c6009315..e3042161e 100644 --- a/pay-api/tests/unit/models/test_payment_method.py +++ b/pay-api/tests/unit/models/test_payment_method.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_system.py b/pay-api/tests/unit/models/test_payment_system.py index 7e12a68ff..e5095f2d9 100644 --- a/pay-api/tests/unit/models/test_payment_system.py +++ b/pay-api/tests/unit/models/test_payment_system.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_payment_transaction.py b/pay-api/tests/unit/models/test_payment_transaction.py index b14788a11..4ad738bef 100644 --- a/pay-api/tests/unit/models/test_payment_transaction.py +++ b/pay-api/tests/unit/models/test_payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_receipt.py b/pay-api/tests/unit/models/test_receipt.py index 9acf4b405..b04303dcb 100644 --- a/pay-api/tests/unit/models/test_receipt.py +++ b/pay-api/tests/unit/models/test_receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_routing_slip.py b/pay-api/tests/unit/models/test_routing_slip.py index e4aff84b2..68943e440 100644 --- a/pay-api/tests/unit/models/test_routing_slip.py +++ b/pay-api/tests/unit/models/test_routing_slip.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/models/test_status_code.py b/pay-api/tests/unit/models/test_status_code.py index a85ec1ff2..d67bf3b80 100644 --- a/pay-api/tests/unit/models/test_status_code.py +++ b/pay-api/tests/unit/models/test_status_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/__init__.py b/pay-api/tests/unit/services/__init__.py index 051177240..c467e0241 100755 --- a/pay-api/tests/unit/services/__init__.py +++ b/pay-api/tests/unit/services/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_auth.py b/pay-api/tests/unit/services/test_auth.py index adba238fa..d1ec39d9f 100644 --- a/pay-api/tests/unit/services/test_auth.py +++ b/pay-api/tests/unit/services/test_auth.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_bcol_service.py b/pay-api/tests/unit/services/test_bcol_service.py index c1f856950..52cb3a044 100644 --- a/pay-api/tests/unit/services/test_bcol_service.py +++ b/pay-api/tests/unit/services/test_bcol_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_cfs_service.py b/pay-api/tests/unit/services/test_cfs_service.py index 14c74377b..94ab0d5b2 100644 --- a/pay-api/tests/unit/services/test_cfs_service.py +++ b/pay-api/tests/unit/services/test_cfs_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_code.py b/pay-api/tests/unit/services/test_code.py index 5232be98c..165838cb5 100644 --- a/pay-api/tests/unit/services/test_code.py +++ b/pay-api/tests/unit/services/test_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_comment.py b/pay-api/tests/unit/services/test_comment.py index e8bd43232..17beae748 100644 --- a/pay-api/tests/unit/services/test_comment.py +++ b/pay-api/tests/unit/services/test_comment.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_distribution_code.py b/pay-api/tests/unit/services/test_distribution_code.py index d4bcad620..26dd69c5c 100644 --- a/pay-api/tests/unit/services/test_distribution_code.py +++ b/pay-api/tests/unit/services/test_distribution_code.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_eft_service.py b/pay-api/tests/unit/services/test_eft_service.py index c236f925d..b3382b78f 100644 --- a/pay-api/tests/unit/services/test_eft_service.py +++ b/pay-api/tests/unit/services/test_eft_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_fee_schedule.py b/pay-api/tests/unit/services/test_fee_schedule.py index aed929a34..a0a8307a9 100644 --- a/pay-api/tests/unit/services/test_fee_schedule.py +++ b/pay-api/tests/unit/services/test_fee_schedule.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_flags.py b/pay-api/tests/unit/services/test_flags.py index b59a11aba..f1259206c 100644 --- a/pay-api/tests/unit/services/test_flags.py +++ b/pay-api/tests/unit/services/test_flags.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_gcp_queue.py b/pay-api/tests/unit/services/test_gcp_queue.py index 8ded5facb..6b293f1b5 100644 --- a/pay-api/tests/unit/services/test_gcp_queue.py +++ b/pay-api/tests/unit/services/test_gcp_queue.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,27 +18,29 @@ """ from unittest.mock import ANY, MagicMock, patch -from dataclasses import asdict -from dotenv import load_dotenv -from gcp_queue.gcp_queue import GcpQueue -import humps import pytest +from flask import Flask -from pay_api import create_app -from pay_api.services import gcp_queue_publisher -from pay_api.services.payment_transaction import PaymentToken +from pay_api.services.gcp_queue.gcp_queue import GcpQueue from pay_api.services.gcp_queue_publisher import QueueMessage, publish_to_queue -from pay_api.utils.enums import TransactionStatus -@pytest.fixture() +@pytest.fixture(autouse=True) +def setup(): + """Initialize app with test env for testing.""" + global app + app = Flask(__name__) + app.env = 'testing' + + +@pytest.fixture(autouse=True) def mock_publisher_client(): """Mock the PublisherClient used in GcpQueue.""" with patch('google.cloud.pubsub_v1.PublisherClient') as publisher: yield publisher.return_value -@pytest.fixture() +@pytest.fixture(autouse=True) def mock_credentials(): """Mock Credentials.""" with patch('google.auth.jwt.Credentials') as mock: @@ -46,7 +48,7 @@ def mock_credentials(): yield mock -def test_publish_to_queue_success(app, mock_credentials, mock_publisher_client): +def test_publish_to_queue_success(): """Test publishing to GCP PubSub Queue successfully.""" with patch.object(GcpQueue, 'publish') as mock_publisher: with app.app_context(): @@ -61,34 +63,17 @@ def test_publish_to_queue_success(app, mock_credentials, mock_publisher_client): mock_publisher.assert_called_once_with('projects/project-id/topics/topic', ANY) -def test_publish_to_queue_no_topic(app, mock_credentials, mock_publisher_client): +def test_publish_to_queue_no_topic(): """Test that publish_to_queue does not publish if no topic is set.""" with patch.object(GcpQueue, 'publish') as mock_publisher: - with app.app_context(): - queue_message = QueueMessage( - source='test-source', - message_type='test-message-type', - payload={'key': 'value'}, - topic=None - ) - publish_to_queue(queue_message) - mock_publisher.publish.assert_not_called() - - -@pytest.mark.skip(reason='ADHOC only test.') -def test_gcp_pubsub_connectivity(monkeypatch): - """Test that a queue can publish to gcp pubsub.""" - # We don't want any of the monkeypatches by the fixtures. - monkeypatch.undo() - load_dotenv('.env') - app_prod = create_app('production') - payload = humps.camelize(asdict(PaymentToken(55, TransactionStatus.COMPLETED.value, 55, 'NRO'))) - with app_prod.app_context(): - gcp_queue_publisher.publish_to_queue( - QueueMessage(source='test', message_type='bc.registry.payment', payload=payload, - topic=app_prod.config.get('NAMEX_PAY_TOPIC')) - ) - gcp_queue_publisher.publish_to_queue( - QueueMessage(source='test', message_type='test', payload={'key': 'value'}, - topic=app_prod.config.get('ACCOUNT_MAILER_TOPIC')) - ) + with patch.object(Flask, 'logger') as logger: + with app.app_context(): + queue_message = QueueMessage( + source='test-source', + message_type='test-message-type', + payload={'key': 'value'}, + topic=None + ) + publish_to_queue(queue_message) + mock_publisher.publish.assert_not_called() + logger.info.assert_called_once_with('Skipping queue message topic not set.') diff --git a/pay-api/tests/unit/services/test_hashing_service.py b/pay-api/tests/unit/services/test_hashing_service.py index b65b2defe..97a390115 100644 --- a/pay-api/tests/unit/services/test_hashing_service.py +++ b/pay-api/tests/unit/services/test_hashing_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_invoice.py b/pay-api/tests/unit/services/test_invoice.py index 1c97a686e..c0030af6e 100644 --- a/pay-api/tests/unit/services/test_invoice.py +++ b/pay-api/tests/unit/services/test_invoice.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_invoice_reference.py b/pay-api/tests/unit/services/test_invoice_reference.py index 3b34e3763..8d52fddda 100644 --- a/pay-api/tests/unit/services/test_invoice_reference.py +++ b/pay-api/tests/unit/services/test_invoice_reference.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_non_sufficient_funds.py b/pay-api/tests/unit/services/test_non_sufficient_funds.py index d10e33d31..8f639fdd6 100644 --- a/pay-api/tests/unit/services/test_non_sufficient_funds.py +++ b/pay-api/tests/unit/services/test_non_sufficient_funds.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_oauth_service.py b/pay-api/tests/unit/services/test_oauth_service.py index a49473ec8..e1a35f197 100644 --- a/pay-api/tests/unit/services/test_oauth_service.py +++ b/pay-api/tests/unit/services/test_oauth_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_pad_service.py b/pay-api/tests/unit/services/test_pad_service.py index 99cf83071..276b49938 100644 --- a/pay-api/tests/unit/services/test_pad_service.py +++ b/pay-api/tests/unit/services/test_pad_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment.py b/pay-api/tests/unit/services/test_payment.py index ecdce80d0..dbde1d521 100644 --- a/pay-api/tests/unit/services/test_payment.py +++ b/pay-api/tests/unit/services/test_payment.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_account.py b/pay-api/tests/unit/services/test_payment_account.py index da1a626dd..d6e465752 100644 --- a/pay-api/tests/unit/services/test_payment_account.py +++ b/pay-api/tests/unit/services/test_payment_account.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_line_item.py b/pay-api/tests/unit/services/test_payment_line_item.py index db802f04c..c5157f259 100644 --- a/pay-api/tests/unit/services/test_payment_line_item.py +++ b/pay-api/tests/unit/services/test_payment_line_item.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_service.py b/pay-api/tests/unit/services/test_payment_service.py index be9483799..727842b62 100644 --- a/pay-api/tests/unit/services/test_payment_service.py +++ b/pay-api/tests/unit/services/test_payment_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_system_service.py b/pay-api/tests/unit/services/test_payment_system_service.py index 619c34ca8..b36490955 100644 --- a/pay-api/tests/unit/services/test_payment_system_service.py +++ b/pay-api/tests/unit/services/test_payment_system_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_payment_transaction.py b/pay-api/tests/unit/services/test_payment_transaction.py index 2f6387be3..52e9c3f22 100644 --- a/pay-api/tests/unit/services/test_payment_transaction.py +++ b/pay-api/tests/unit/services/test_payment_transaction.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_receipt.py b/pay-api/tests/unit/services/test_receipt.py index 688c9c8c0..357aa0e35 100644 --- a/pay-api/tests/unit/services/test_receipt.py +++ b/pay-api/tests/unit/services/test_receipt.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_refund.py b/pay-api/tests/unit/services/test_refund.py index a4a06986f..d28e305e1 100644 --- a/pay-api/tests/unit/services/test_refund.py +++ b/pay-api/tests/unit/services/test_refund.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_routing_slip_service.py b/pay-api/tests/unit/services/test_routing_slip_service.py index ff283b40c..f62c8ae16 100644 --- a/pay-api/tests/unit/services/test_routing_slip_service.py +++ b/pay-api/tests/unit/services/test_routing_slip_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_statement.py b/pay-api/tests/unit/services/test_statement.py index 5700ed97f..99239f168 100644 --- a/pay-api/tests/unit/services/test_statement.py +++ b/pay-api/tests/unit/services/test_statement.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_statement_settings.py b/pay-api/tests/unit/services/test_statement_settings.py index c3dd9378d..fdabd32cb 100644 --- a/pay-api/tests/unit/services/test_statement_settings.py +++ b/pay-api/tests/unit/services/test_statement_settings.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/services/test_wire_service.py b/pay-api/tests/unit/services/test_wire_service.py index 528234770..c7a5427a3 100644 --- a/pay-api/tests/unit/services/test_wire_service.py +++ b/pay-api/tests/unit/services/test_wire_service.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/__init__.py b/pay-api/tests/unit/utils/__init__.py index 16727833c..a41ec6419 100755 --- a/pay-api/tests/unit/utils/__init__.py +++ b/pay-api/tests/unit/utils/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_error.py b/pay-api/tests/unit/utils/test_error.py index f0dd7b189..1b93a07ac 100755 --- a/pay-api/tests/unit/utils/test_error.py +++ b/pay-api/tests/unit/utils/test_error.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_logging.py b/pay-api/tests/unit/utils/test_logging.py index 0f07a8843..81f8de9cc 100755 --- a/pay-api/tests/unit/utils/test_logging.py +++ b/pay-api/tests/unit/utils/test_logging.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_util.py b/pay-api/tests/unit/utils/test_util.py index 80aae8383..f40af87a6 100644 --- a/pay-api/tests/unit/utils/test_util.py +++ b/pay-api/tests/unit/utils/test_util.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/unit/utils/test_util_cors.py b/pay-api/tests/unit/utils/test_util_cors.py index fea00d6f8..df4bbf050 100755 --- a/pay-api/tests/unit/utils/test_util_cors.py +++ b/pay-api/tests/unit/utils/test_util_cors.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/__init__.py b/pay-api/tests/utilities/__init__.py index af6a70494..eb431264d 100644 --- a/pay-api/tests/utilities/__init__.py +++ b/pay-api/tests/utilities/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/base_test.py b/pay-api/tests/utilities/base_test.py index adf30be69..0c322bce6 100644 --- a/pay-api/tests/utilities/base_test.py +++ b/pay-api/tests/utilities/base_test.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/decorators.py b/pay-api/tests/utilities/decorators.py index d6be57699..88deec93f 100644 --- a/pay-api/tests/utilities/decorators.py +++ b/pay-api/tests/utilities/decorators.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/tests/utilities/schema_assertions.py b/pay-api/tests/utilities/schema_assertions.py index 55cd2e6b3..24d3d1f8e 100755 --- a/pay-api/tests/utilities/schema_assertions.py +++ b/pay-api/tests/utilities/schema_assertions.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-api/wsgi.py b/pay-api/wsgi.py index 7c25417b6..d584bdf8a 100755 --- a/pay-api/wsgi.py +++ b/pay-api/wsgi.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/Makefile b/pay-queue/Makefile index 55b54a7d7..101a2de04 100644 --- a/pay-queue/Makefile +++ b/pay-queue/Makefile @@ -115,7 +115,7 @@ tag: push ## tag image ################################################################################# run: ## Run the project in local - poetry run flask run -p 5001 + . venv/bin/activate && python app.py ################################################################################# # Self Documenting Commands # diff --git a/pay-queue/README.md b/pay-queue/README.md index f2709a676..b98948f78 100755 --- a/pay-queue/README.md +++ b/pay-queue/README.md @@ -3,11 +3,11 @@ # Application Name -BC Registries Pay Queue +BC Registries Payment Reconciliation Queue ## Technology Stack Used * Python, Flask -* Postgres - SQLAlchemy, psycopg2-binary, alembic & Google PUB/SUB +* Postgres - SQLAlchemy, psycopg2-binary & alembic ## License diff --git a/pay-queue/app.py b/pay-queue/app.py index a1f9fdbe5..dd7bd16a7 100755 --- a/pay-queue/app.py +++ b/pay-queue/app.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -14,12 +14,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Initialize Flask app.""" - +"""Provides the WSGI entry point for running the application.""" import os + from pay_queue import create_app + app = create_app() if __name__ == '__main__': - server_port = os.environ.get('PORT', '5001') + server_port = os.environ.get('PORT', '8080') app.run(debug=False, port=server_port, host='0.0.0.0') diff --git a/pay-queue/devops/vaults.gcp.env b/pay-queue/devops/vaults.gcp.env index ee10ddf5f..fa6f68780 100644 --- a/pay-queue/devops/vaults.gcp.env +++ b/pay-queue/devops/vaults.gcp.env @@ -16,8 +16,9 @@ EFT_INVOICE_PREFIX="op://payment-external-services/$APP_ENV/eft/EFT_INVOICE_PREF AUDIENCE="op://gcp-queue/$APP_ENV/base/AUDIENCE" GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/base/GCP_AUTH_KEY" PUBLISHER_AUDIENCE="op://gcp-queue/$APP_ENV/base/PUBLISHER_AUDIENCE" -PAY_AUDIENCE_SUB="op://gcp-queue/$APP_ENV/base/PAY_AUDIENCE_SUB" -VERIFY_PUBSUB_EMAILS="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_EMAILS" +PAY_SUB_AUDIENCE="op://gcp-queue/$APP_ENV/base/PAY_SUB_AUDIENCE" +VERIFY_PUBSUB_EMAIL="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_EMAIL" +VERIFY_PUBSUB_VIA_JWT="op://gcp-queue/$APP_ENV/base/VERIFY_PUBSUB_VIA_JWT" DEBUG_REQUEST="op://gcp-queue/$APP_ENV/base/DEBUG_REQUEST" ACCOUNT_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/ACCOUNT_MAILER_TOPIC" SENTRY_ENABLE="op://sentry/$APP_ENV/relationship-api/SENTRY_ENABLE" @@ -25,3 +26,4 @@ SENTRY_DSN="op://sentry/$APP_ENV/relationship-api/SENTRY_DSN" LEGISLATIVE_TIMEZONE="op://relationship/$APP_ENV/pay-api/LEGISLATIVE_TIMEZONE" ACCOUNT_SECRET_KEY="op://relationship/$APP_ENV/pay-api/ACCOUNT_SECRET_KEY" DISABLE_EJV_ERROR_EMAIL="True" +DISABLE_PAD_SUCCESS_EMAIL="False" diff --git a/pay-queue/devops/vaults.json b/pay-queue/devops/vaults.json deleted file mode 100644 index 54912865c..000000000 --- a/pay-queue/devops/vaults.json +++ /dev/null @@ -1,58 +0,0 @@ -[ - { - "vault": "shared", - "application": [ - "encryption-key" - ] - }, - { - "vault": "minio", - "application": [ - "base", - "pay-queue" - ] - }, - { - "vault": "gcp-queue", - "application": [ - "base", - "account-events-listener", - "account-mailer", - "payment", - "pay-queue" - ] - }, - { - "vault": "payment-external-services", - "application": [ - "cfs" - ] - }, - { - "vault": "relationship", - "application": [ - "postgres-pay" - ] - }, - { - "vault": "sentry", - "application": [ - "relationship-api" - ] - }, - { - "vault": "launchdarkly", - "application": [ - "pay" - ] - }, - { - "vault": "gcp-queue", - "application": [ - "a083gt", - "authpay", - "gtksf3", - "topics" - ] - } -] diff --git a/pay-queue/logging.conf b/pay-queue/logging.conf new file mode 100644 index 000000000..ded5cb81c --- /dev/null +++ b/pay-queue/logging.conf @@ -0,0 +1,34 @@ +[loggers] +keys=root,api,asyncio + +[handlers] +keys=console + +[formatters] +keys=simple + +[logger_root] +level=DEBUG +handlers=console + +[logger_asyncio] +level=DEBUG +handlers=console +qualname=asyncio +propagate=0 + +[logger_api] +level=DEBUG +handlers=console +qualname=api +propagate=0 + +[handler_console] +class=StreamHandler +level=DEBUG +formatter=simple +args=(sys.stdout,) + +[formatter_simple] +format=%(asctime)s - %(name)s - %(levelname)s in %(module)s:%(filename)s:%(lineno)d - %(funcName)s: %(message)s +datefmt= diff --git a/pay-queue/poetry.lock b/pay-queue/poetry.lock index f9715fdb2..28dfadd78 100644 --- a/pay-queue/poetry.lock +++ b/pay-queue/poetry.lock @@ -89,13 +89,13 @@ files = [ [[package]] name = "astroid" -version = "3.2.2" +version = "3.1.0" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, - {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, ] [[package]] @@ -119,13 +119,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "autopep8" -version = "2.2.0" +version = "2.1.0" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" optional = false python-versions = ">=3.8" files = [ - {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, - {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"}, + {file = "autopep8-2.1.0-py2.py3-none-any.whl", hash = "sha256:2bb76888c5edbcafe6aabab3c47ba534f5a2c2d245c2eddced4a30c4b4946357"}, + {file = "autopep8-2.1.0.tar.gz", hash = "sha256:1fa8964e4618929488f4ec36795c7ff12924a68b8bf01366c094fc52f770b6e7"}, ] [package.dependencies] @@ -408,63 +408,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.3" +version = "7.5.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, + {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, + {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, + {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, + {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, + {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, + {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, + {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, + {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, + {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, + {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, + {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, + {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, + {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, + {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, ] [package.extras] @@ -709,13 +709,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-caching" -version = "2.3.0" +version = "2.1.0" description = "Adds caching support to Flask applications." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"}, - {file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"}, + {file = "Flask-Caching-2.1.0.tar.gz", hash = "sha256:b7500c145135836a952e3de3a80881d9654e327a29c852c9265607f5c449235c"}, + {file = "Flask_Caching-2.1.0-py3-none-any.whl", hash = "sha256:f02645a629a8c89800d96dc8f690a574a0d49dcd66c7536badc6d362ba46b716"}, ] [package.dependencies] @@ -737,25 +737,25 @@ files = [ Flask = ">=0.9" [[package]] -name = "flask-jwt-oidc" -version = "0.7.0" -description = "Opinionated flask oidc client" +name = "flask_jwt_oidc" +version = "0.5.0" +description = "Flask JWT OIDC" optional = false -python-versions = "^3.9" +python-versions = "*" files = [] develop = false [package.dependencies] -cachelib = "0.*" -Flask = ">=2" -python-jose = "^3.3.0" -six = "^1.16.0" +cachelib = "*" +flask = "*" +python-jose = "*" +six = "*" [package.source] type = "git" -url = "https://github.com/seeker25/flask-jwt-oidc.git" +url = "https://github.com/thorwolpert/flask-jwt-oidc.git" reference = "HEAD" -resolved_reference = "d208d4643e3b17358f7295bee0f955e67ba6ac88" +resolved_reference = "fc60d430a69e0d59e1d659d01aff78151ba498d0" [[package]] name = "flask-marshmallow" @@ -856,49 +856,27 @@ sqlalchemy = ">=2.0.16" [[package]] name = "freezegun" -version = "1.5.1" +version = "1.5.0" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, - {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, + {file = "freezegun-1.5.0-py3-none-any.whl", hash = "sha256:ec3f4ba030e34eb6cf7e1e257308aee2c60c3d038ff35996d7475760c9ff3719"}, + {file = "freezegun-1.5.0.tar.gz", hash = "sha256:200a64359b363aa3653d8aac289584078386c7c3da77339d257e46a01fb5c77c"}, ] [package.dependencies] python-dateutil = ">=2.7" -[[package]] -name = "gcp-queue" -version = "0.3.0" -description = "" -optional = false -python-versions = "^3.9" -files = [] -develop = false - -[package.dependencies] -flask = ">=1" -google-auth = "^2.28.2" -google-cloud-pubsub = "^2.20.2" -simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} - -[package.source] -type = "git" -url = "https://github.com/seeker25/sbc-connect-common.git" -reference = "main" -resolved_reference = "c0d1dea449ac6332510841caee5400ff8f550159" -subdirectory = "python/gcp-queue" - [[package]] name = "google-api-core" -version = "2.19.0" +version = "2.17.1" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, - {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, + {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, + {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, ] [package.dependencies] @@ -906,7 +884,6 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} -proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -917,13 +894,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.29.0" +version = "2.28.1" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, - {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, + {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, + {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, ] [package.dependencies] @@ -940,13 +917,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-pubsub" -version = "2.21.2" +version = "2.20.0" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, - {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, + {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"}, + {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"}, ] [package.dependencies] @@ -1068,76 +1045,84 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.64.0" +version = "1.62.1" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, - {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, - {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, - {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, - {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, - {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, - {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, - {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, - {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, - {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, - {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, - {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, - {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, - {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, - {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, - {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, - {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, - {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, - {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, - {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, - {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.64.0)"] +protobuf = ["grpcio-tools (>=1.62.1)"] [[package]] name = "grpcio-status" -version = "1.62.2" +version = "1.62.1" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, - {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, + {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, + {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.2" +grpcio = ">=1.62.1" protobuf = ">=4.21.6" [[package]] @@ -1292,26 +1277,28 @@ urllib3 = ">=1.26.0,<3" [[package]] name = "launchdarkly-server-sdk" -version = "8.2.1" +version = "9.2.2" description = "LaunchDarkly SDK for Python" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "launchdarkly-server-sdk-8.2.1.tar.gz", hash = "sha256:94adbd52f635ad2f1a8b4a835cbbe4ce77919a6915136b303eaca3e2a54903be"}, - {file = "launchdarkly_server_sdk-8.2.1-py3-none-any.whl", hash = "sha256:b7680a4d5856da133b0dad8eca820e48bb5f2fb6dc34ebbf7f1a3a681033b426"}, + {file = "launchdarkly_server_sdk-9.2.2-py3-none-any.whl", hash = "sha256:d7b544c60c7c2b431c8976dda472c68f910c7c0061b90c8c2ef0397d8f05c519"}, + {file = "launchdarkly_server_sdk-9.2.2.tar.gz", hash = "sha256:3785d0159c2a74434cd85c16dd88e41ac94bb5cd7563def4d781d99e59c4eba0"}, ] [package.dependencies] certifi = ">=2018.4.16" expiringdict = ">=1.1.4" +launchdarkly-eventsource = ">=1.1.0,<2.0.0" pyRFC3339 = ">=1.0" semver = ">=2.10.2" -urllib3 = ">=1.22.0,<3" +urllib3 = ">=1.26.0,<3" [package.extras] consul = ["python-consul (>=1.0.1)"] dynamodb = ["boto3 (>=1.9.71)"] redis = ["redis (>=2.10.5)"] +test-filesource = ["pyyaml (>=5.3.1)", "watchdog (>=3.0.0)"] [[package]] name = "lovely-pytest-docker" @@ -1467,13 +1454,13 @@ files = [ [[package]] name = "minio" -version = "7.2.7" +version = "7.2.5" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = "*" files = [ - {file = "minio-7.2.7-py3-none-any.whl", hash = "sha256:59d1f255d852fe7104018db75b3bebbd987e538690e680f7c5de835e422de837"}, - {file = "minio-7.2.7.tar.gz", hash = "sha256:473d5d53d79f340f3cd632054d0c82d2f93177ce1af2eac34a235bea55708d98"}, + {file = "minio-7.2.5-py3-none-any.whl", hash = "sha256:ed9176c96d4271cb1022b9ecb8a538b1e55b32ae06add6de16425cab99ef2304"}, + {file = "minio-7.2.5.tar.gz", hash = "sha256:59d8906e2da248a9caac34d4958a859cc3a44abbe6447910c82b5abfa9d6a2e1"}, ] [package.dependencies] @@ -1598,16 +1585,22 @@ dpath = "2.1.6" ecdsa = "0.18.0" expiringdict = "1.2.2" flask = "3.0.2" -flask-caching = "2.3.0" +flask-caching = "2.1.0" flask-cors = "4.0.0" -flask-jwt-oidc = {git = "https://github.com/seeker25/flask-jwt-oidc.git"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} flask-marshmallow = "1.2.0" flask-migrate = "4.0.7" flask-moment = "1.0.5" flask-script = "2.0.6" flask-sqlalchemy = "3.1.1" -gcp-queue = {git = "https://github.com/seeker25/sbc-connect-common.git", branch = "main", subdirectory = "python/gcp-queue"} +google-api-core = "2.17.1" +google-auth = "2.28.1" +google-cloud-pubsub = "2.20.0" +googleapis-common-protos = "1.63.0" greenlet = "3.0.3" +grpc-google-iam-v1 = "0.13.0" +grpcio = "1.62.1" +grpcio-status = "1.62.1" gunicorn = "21.2.0" holidays = "0.37" idna = "3.6" @@ -1616,7 +1609,7 @@ jaeger-client = "4.8.0" jinja2 = "3.1.3" jsonschema = "4.17.3" launchdarkly-eventsource = "1.1.1" -launchdarkly-server-sdk = "8.2.1" +launchdarkly-server-sdk = "9.2.2" mako = "1.3.2" markupsafe = "2.1.5" marshmallow = "3.21.1" @@ -1642,6 +1635,7 @@ rsa = "4.9" sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} semver = "3.0.2" sentry-sdk = "1.41.0" +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} six = "1.16.0" sql-versioning = {git = "https://github.com/bcgov/lear.git", branch = "feature-legal-name", subdirectory = "python/common/sql-versioning"} sqlalchemy = "2.0.28" @@ -1655,9 +1649,9 @@ werkzeug = "3.0.1" [package.source] type = "git" -url = "https://github.com/seeker25/sbc-pay.git" -reference = "sync-python-to-main" -resolved_reference = "1dae0d28e241a7bcdfc43dcaee1a36e35e77c31c" +url = "https://github.com/bcgov/sbc-pay.git" +reference = "feature-queue-python-upgrade" +resolved_reference = "031104546edc3c471f0d00bdbbf9c2025d468557" subdirectory = "pay-api" [[package]] @@ -1676,28 +1670,28 @@ flake8 = ">=5.0.0" [[package]] name = "pg8000" -version = "1.31.2" +version = "1.31.1" description = "PostgreSQL interface library" optional = false python-versions = ">=3.8" files = [ - {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, - {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, + {file = "pg8000-1.31.1-py3-none-any.whl", hash = "sha256:69aac9dba4114c9c8d0408232d54eaf7d06d271df7765caeed39960e057800e4"}, + {file = "pg8000-1.31.1.tar.gz", hash = "sha256:b11130d4c615dd3062ea8fed8143064a7978b7fe6d44f14b72261d43c8e27087"}, ] [package.dependencies] python-dateutil = ">=2.8.2" -scramp = ">=1.4.5" +scramp = ">=1.4.4" [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.2.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, ] [package.extras] @@ -1995,17 +1989,17 @@ files = [ [[package]] name = "pylint" -version = "3.2.2" +version = "3.1.0" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, - {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, ] [package.dependencies] -astroid = ">=3.2.2,<=3.3.0-dev0" +astroid = ">=3.1.0,<=3.2.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -2101,23 +2095,23 @@ files = [ [[package]] name = "pytest" -version = "8.2.1" +version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, - {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.4,<2.0" [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" @@ -2287,7 +2281,7 @@ jaeger-client = "*" type = "git" url = "https://github.com/bcgov/sbc-common-components.git" reference = "HEAD" -resolved_reference = "e770b4ab496e044d292500e62bc19a17079a73ec" +resolved_reference = "8871ffcce8cc2232a5d7a3adb6103dfaf0d7689f" subdirectory = "python" [[package]] @@ -2365,18 +2359,19 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "70.0.0" +version = "69.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, - {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, + {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, + {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simple-cloudevent" @@ -2593,13 +2588,13 @@ twisted = ["twisted"] [[package]] name = "tomlkit" -version = "0.12.5" +version = "0.12.4" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, - {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, ] [[package]] @@ -2670,4 +2665,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "91f9fa21463afa27ba2b73d8efcd0b07bac434743792659ba832a588fc331d50" +content-hash = "f7e978de8d7c39e508ff106d26b6a9fedac7896203b705955ed5b3a5d481c775" diff --git a/pay-queue/pyproject.toml b/pay-queue/pyproject.toml index 21af5378e..299044c9d 100644 --- a/pay-queue/pyproject.toml +++ b/pay-queue/pyproject.toml @@ -19,9 +19,12 @@ sqlalchemy = "^2.0.28" itsdangerous = "^2.1.2" jinja2 = "^3.1.3" protobuf = "4.25.3" -launchdarkly-server-sdk = "^8.2.1" +launchdarkly-server-sdk = "^9.2.2" cachecontrol = "^0.14.0" -pay-api = {git = "https://github.com/seeker25/sbc-pay.git", branch = "sync-python-to-main", subdirectory = "pay-api"} +sbc-common-components = {git = "https://github.com/bcgov/sbc-common-components.git", subdirectory = "python"} +pay-api = {git = "https://github.com/bcgov/sbc-pay.git", branch = "feature-queue-python-upgrade", subdirectory = "pay-api"} +flask-jwt-oidc = {git = "https://github.com/thorwolpert/flask-jwt-oidc.git"} +simple-cloudevent = {git = "https://github.com/daxiom/simple-cloudevent.py.git"} pg8000 = "^1.30.5" diff --git a/pay-queue/scripts/verify_license_headers.sh b/pay-queue/scripts/verify_license_headers.sh index a160d3ac8..028b95c63 100755 --- a/pay-queue/scripts/verify_license_headers.sh +++ b/pay-queue/scripts/verify_license_headers.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ # limitations under the License. -COPYRIGHT="Copyright © 2024 Province of British Columbia" +COPYRIGHT="Copyright © 2019 Province of British Columbia" RET=0 for file in $(find $@ -not \( -path */venv -prune \) -not \( -path */migrations -prune \) -not \( -path */tests -prune \) -not \( -path */.egg* -prune \) -name \*.py) diff --git a/pay-queue/setup.cfg b/pay-queue/setup.cfg index a3d8c7414..a09beb534 100644 --- a/pay-queue/setup.cfg +++ b/pay-queue/setup.cfg @@ -1,6 +1,6 @@ [metadata] -name = pay_queue -url = https://github.com/bcgov/sbc-pay/ +name = pay-queue +url = https://github.com/bcgov/sbc-pay/pay-queue author = SBC Relationships team author_email = classifiers = @@ -22,7 +22,7 @@ include_package_data = True packages = find: [options.package_data] -pay_queue = +reconciliations = [wheel] universal = 1 @@ -34,7 +34,6 @@ universal = 1 test = pytest [flake8] -ignore = B902 exclude = .git,*migrations* max-line-length = 120 docstring-min-length=10 @@ -46,7 +45,7 @@ max_line_length = 120 ignore = E501 docstring-min-length=10 notes=FIXME,XXX # TODO is ignored -match_dir = src/pay_queue +match_dir = src/reconciliations ignored-modules=flask_sqlalchemy sqlalchemy per-file-ignores = diff --git a/pay-queue/setup.py b/pay-queue/setup.py index 9bc1ac212..31939e558 100644 --- a/pay-queue/setup.py +++ b/pay-queue/setup.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia. +# Copyright © 2019 Province of British Columbia. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ def read(filepath): REQUIREMENTS = read_requirements('requirements.txt') setup( - name="pay_queue", + name="reconciliations", version=version, author_email='', packages=find_packages('src'), diff --git a/pay-queue/src/pay_queue/__init__.py b/pay-queue/src/pay_queue/__init__.py index 1fe3ebdcc..01c2cf845 100644 --- a/pay-queue/src/pay_queue/__init__.py +++ b/pay-queue/src/pay_queue/__init__.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""The pay-queue service. +"""The Reconciliations queue service. The service worker for applying payments, receipts and account balance to payment system. """ @@ -23,26 +23,21 @@ from flask import Flask from pay_api.models import db from pay_api.services.flags import flags -from pay_api.services.gcp_queue import queue -from pay_api.utils.cache import cache -from pay_api.utils.logging import setup_logging from pay_api.utils.run_version import get_run_version from sentry_sdk.integrations.flask import FlaskIntegration -from pay_queue import config +from pay_queue.config import CONFIGURATION from pay_queue.version import __version__ from .resources import register_endpoints - - -setup_logging(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first +from .services import queue def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')) -> Flask: """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.env = run_mode - app.config.from_object(config.CONFIGURATION[run_mode]) + app.config.from_object(CONFIGURATION[run_mode]) # Configure Sentry if dsn := app.config.get('SENTRY_DSN', None): @@ -53,25 +48,10 @@ def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')) -> Flask: send_default_pii=False, ) - queue.init_app(app) flags.init_app(app) db.init_app(app) + queue.init_app(app) register_endpoints(app) - build_cache(app) return app - - -def build_cache(app): - """Build cache.""" - cache.init_app(app) - with app.app_context(): - cache.clear() - if not app.config.get('TESTING', False): - try: - from pay_api.services.code import Code as CodeService # pylint: disable=import-outside-toplevel - CodeService.build_all_codes_cache() - except Exception as e: # NOQA pylint:disable=broad-except - app.logger.error('Error on caching ') - app.logger.error(e) diff --git a/pay-queue/src/pay_queue/config.py b/pay-queue/src/pay_queue/config.py index 1a284c1a5..cbceb71f4 100644 --- a/pay-queue/src/pay_queue/config.py +++ b/pay-queue/src/pay_queue/config.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ def get_named_config(config_name: str = 'production'): return app_config -class _Config(): # pylint: disable=too-few-public-methods,protected-access +class _Config(): # pylint: disable=too-few-public-methods """Base class configuration that should set reasonable defaults. Used as the base for all the other configurations. @@ -99,14 +99,19 @@ class _Config(): # pylint: disable=too-few-public-methods,protected-access # Disable EJV Error Email DISABLE_EJV_ERROR_EMAIL = os.getenv('DISABLE_EJV_ERROR_EMAIL', 'true').lower() == 'true' + # Disable PAD Success Email - Incase we need to reprocess records weeks/months later + DISABLE_PAD_SUCCESS_EMAIL = os.getenv('DISABLE_PAD_SUCCESS_EMAIL', 'false').lower() == 'true' - # PUB/SUB - PUB: account-mailer-dev, auth-event-dev, SUB to ftp-poller-payment-reconciliation-dev, business-events - ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', 'account-mailer-dev') - AUTH_EVENT_TOPIC = os.getenv('AUTH_EVENT_TOPIC', 'auth-event-dev') - GCP_AUTH_KEY = os.getenv('AUTHPAY_GCP_AUTH_KEY', None) - # If blank in PUBSUB, this should match the https endpoint the subscription is pushing to. - PAY_AUDIENCE_SUB = os.getenv('PAY_AUDIENCE_SUB', None) - VERIFY_PUBSUB_EMAILS = f'{os.getenv("AUTHPAY_SERVICE_ACCOUNT")},{os.getenv("BUSINESS_SERVICE_ACCOUNT")}'.split(',') + # GCP PubSub + AUDIENCE = os.getenv('AUDIENCE', None) + GCP_AUTH_KEY = os.getenv('GCP_AUTH_KEY', None) + PUBLISHER_AUDIENCE = os.getenv('PUBLISHER_AUDIENCE', None) + ACCOUNT_MAILER_TOPIC = os.getenv('ACCOUNT_MAILER_TOPIC', None) + PAY_SUB_AUDIENCE = os.getenv('PAY_SUB_AUDIENCE', None) + VERIFY_PUBSUB_EMAIL = os.getenv('VERIFY_PUBSUB_EMAIL', None) + + VERIFY_PUBSUB_VIA_JWT = os.getenv('VERIFY_PUBSUB_VIA_JWT', 'true').lower() == 'true' + VERIFY_PUBSUB_VIA_JWT = os.getenv('DEBUG_REQUEST', 'true').lower() == 'true' class DevConfig(_Config): # pylint: disable=too-few-public-methods @@ -152,13 +157,11 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods ACCOUNT_SECRET_KEY = os.getenv('ACCOUNT_SECRET_KEY', 'test') # Secrets for integration tests - TEST_GCP_PROJECT_NAME = 'pay-queue-dev' + TEST_GCP_PROJECT_NAME = 'abdefg-dev' # Needs to have ftp-poller-dev in it. TEST_GCP_TOPICS = ['account-mailer-dev', 'ftp-poller-dev', 'business-identifier-update-pay-dev'] TEST_PUSH_ENDPOINT_PORT = 5020 TEST_PUSH_ENDPOINT = os.getenv('TEST_PUSH_ENDPOINT', f'http://host.docker.internal:{str(TEST_PUSH_ENDPOINT_PORT)}/') - GCP_AUTH_KEY = None - DISABLE_EJV_ERROR_EMAIL = False class ProdConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/pay-queue/src/pay_queue/enums.py b/pay-queue/src/pay_queue/enums.py index f23c76288..cf16aabf2 100644 --- a/pay-queue/src/pay_queue/enums.py +++ b/pay-queue/src/pay_queue/enums.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/src/pay_queue/external/__init__.py b/pay-queue/src/pay_queue/external/__init__.py deleted file mode 100644 index de7538dc3..000000000 --- a/pay-queue/src/pay_queue/external/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright © 2024 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""External to gcp auth.""" diff --git a/pay-queue/src/pay_queue/external/gcp_auth.py b/pay-queue/src/pay_queue/external/gcp_auth.py index 398b9ca5f..31f575ac6 100644 --- a/pay-queue/src/pay_queue/external/gcp_auth.py +++ b/pay-queue/src/pay_queue/external/gcp_auth.py @@ -19,16 +19,15 @@ def verify_jwt(session): claims = id_token.verify_oauth2_token( jwt_token, Request(session=session), - audience=current_app.config.get('PAY_AUDIENCE_SUB') + audience=current_app.config.get('PAY_SUB_AUDIENCE') ) - required_emails = current_app.config.get('VERIFY_PUBSUB_EMAILS') - if claims.get('email_verified') and claims.get('email') in required_emails: - return None - else: + # Check if the email is verified and matches the configured email + required_email = current_app.config.get('VERIFY_PUBSUB_EMAIL') + if not claims.get('email_verified') or claims.get('email') != required_email: return 'Email not verified or does not match', 401 except Exception as e: - current_app.logger.info(f'Invalid token {e}') return f'Invalid token: {e}', 400 + return None def ensure_authorized_queue_user(f): @@ -36,7 +35,12 @@ def ensure_authorized_queue_user(f): @functools.wraps(f) def decorated_function(*args, **kwargs): # Use CacheControl to avoid re-fetching certificates for every request. - if verify_jwt(CacheControl(Session())): - abort(HTTPStatus.UNAUTHORIZED) + if current_app.config.get('DEBUG_REQUEST') is True: + current_app.logger.info(f'Headers: {request.headers}') + verifyJWT = current_app.config.get('VERIFY_PUBSUB_VIA_JWT', True) + current_app.logger.info(f'verifyJWT: {verifyJWT}') + if verifyJWT is True: + if message := verify_jwt(CacheControl(Session())): + abort(HTTPStatus.UNAUTHORIZED) return f(*args, **kwargs) return decorated_function diff --git a/pay-queue/src/pay_queue/minio.py b/pay-queue/src/pay_queue/minio.py index d8e8f4eec..ad58ae8cd 100644 --- a/pay-queue/src/pay_queue/minio.py +++ b/pay-queue/src/pay_queue/minio.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/src/pay_queue/resources/worker.py b/pay-queue/src/pay_queue/resources/worker.py index aee57b242..d429ec693 100644 --- a/pay-queue/src/pay_queue/resources/worker.py +++ b/pay-queue/src/pay_queue/resources/worker.py @@ -12,17 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. """Worker resource to handle incoming queue pushes from gcp.""" - -import dataclasses -import json from http import HTTPStatus -from flask import Blueprint, current_app, request -from pay_api.services.gcp_queue_publisher import queue -from sbc_common_components.utils.enums import QueueMessageTypes +from flask import Blueprint, request +from pay_api.utils.enums import MessageType from pay_queue.external.gcp_auth import ensure_authorized_queue_user -from pay_queue.services import update_temporary_identifier +from pay_queue.services import queue, update_temporary_identifier from pay_queue.services.cgi_reconciliations import reconcile_distributions from pay_queue.services.eft.eft_reconciliation import reconcile_eft_payments from pay_queue.services.payment_reconciliations import reconcile_payments @@ -35,27 +31,22 @@ @ensure_authorized_queue_user def worker(): """Worker to handle incoming queue pushes.""" - ce = queue.get_simple_cloud_event(request, wrapped=True) - if not ce: + if not (ce := queue.get_simple_cloud_event(request)): + # Return a 200, so event is removed from the Queue return {}, HTTPStatus.OK - try: - current_app.logger.info('Event Message Received: %s ', json.dumps(dataclasses.asdict(ce))) - if ce.type == QueueMessageTypes.CAS_MESSAGE_TYPE.value: + match ce.type: + case MessageType.CAS_UPLOADED.value: reconcile_payments(ce.data) - elif ce.type == QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value: + case MessageType.CGI_ACK_RECEIVED.value: reconcile_distributions(ce.data) - elif ce.type == QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value: + case MessageType.CGI_FEEDBACK_RECEIVED.value: reconcile_distributions(ce.data, is_feedback=True) - elif ce.type == QueueMessageTypes.EFT_FILE_UPLOADED.value: + case MessageType.EFT_FILE_UPLOADED.value: reconcile_eft_payments(ce.data) - elif ce.type in [QueueMessageTypes.INCORPORATION.value, QueueMessageTypes.REGISTRATION.value]: + case MessageType.INCORPORATION.value | MessageType.REGISTRATION.value: update_temporary_identifier(ce.data) - else: + case _: raise Exception('Invalid queue message type') # pylint: disable=broad-exception-raised - return {}, HTTPStatus.OK - except Exception: # pylint: disable=broad-exception-caught - current_app.logger.error('Failed to process queue message: %s', HTTPStatus.INTERNAL_SERVER_ERROR) - # Optionally, return an error status code or message - return {}, HTTPStatus.OK + return {}, HTTPStatus.OK diff --git a/pay-queue/src/pay_queue/services/__init__.py b/pay-queue/src/pay_queue/services/__init__.py index 45b466b91..c86353543 100644 --- a/pay-queue/src/pay_queue/services/__init__.py +++ b/pay-queue/src/pay_queue/services/__init__.py @@ -33,4 +33,9 @@ # POSSIBILITY OF SUCH DAMAGE. """This module provides Queue type services.""" +from pay_api.services.gcp_queue import GcpQueue + from .identifier_updater import update_temporary_identifier + + +queue = GcpQueue() diff --git a/pay-queue/src/pay_queue/services/cgi_reconciliations.py b/pay-queue/src/pay_queue/services/cgi_reconciliations.py index 7ec308a8d..9926acb86 100644 --- a/pay-queue/src/pay_queue/services/cgi_reconciliations.py +++ b/pay-queue/src/pay_queue/services/cgi_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,9 +32,8 @@ from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage from pay_api.utils.enums import ( - DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, - PaymentSystem, QueueSources, RoutingSlipStatus) -from sbc_common_components.utils.enums import QueueMessageTypes + DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, + PaymentStatus, PaymentSystem, QueueSources, RoutingSlipStatus) from sentry_sdk import capture_message from pay_queue import config @@ -91,46 +90,25 @@ def _update_acknowledgement(msg: Dict[str, any]): def _update_feedback(msg: Dict[str, any]): # pylint:disable=too-many-locals, too-many-statements # Read the file and find records from the database, and update status. - file_name: str = msg.get('fileName') minio_location: str = msg.get('location') file = get_object(minio_location, file_name) content = file.data.decode('utf-8-sig') group_batches: List[str] = _group_batches(content) + has_errors, already_processed = _process_ejv_feedback(group_batches['EJV'], file_name) - if _is_processed_or_processing(group_batches['EJV'], file_name): - return - - has_errors = _process_ejv_feedback(group_batches['EJV']) - has_errors = _process_ap_feedback(group_batches['AP']) or has_errors + if not already_processed: + has_errors = _process_ap_feedback(group_batches['AP']) or has_errors - if has_errors and not APP_CONFIG.DISABLE_EJV_ERROR_EMAIL: - _publish_mailer_events(file_name, minio_location) - current_app.logger.info('Feedback file processing completed.') + if has_errors and not APP_CONFIG.DISABLE_EJV_ERROR_EMAIL: + _publish_mailer_events(file_name, minio_location) + current_app.logger.info('> update_feedback') -def _is_processed_or_processing(group_batches, file_name) -> bool: - """Check to see if file has already been processed. Mark them as processing.""" - for group_batch in group_batches: - ejv_file: Optional[EjvFileModel] = None - for line in group_batch.splitlines(): - is_batch_group: bool = line[2:4] == 'BG' - if is_batch_group: - batch_number = int(line[15:24]) - ejv_file = EjvFileModel.find_by_id(batch_number) - if ejv_file.feedback_file_ref: - current_app.logger.info( - 'EJV file id %s with feedback file %s is already processing or has been processed. Skipping.', - batch_number, file_name) - return True - ejv_file.feedback_file_ref = file_name - ejv_file.save() - return False - - -def _process_ejv_feedback(group_batches) -> bool: # pylint:disable=too-many-locals +def _process_ejv_feedback(group_batches, file_name) -> bool: # pylint:disable=too-many-locals """Process EJV Feedback contents.""" has_errors = False + already_processed = False for group_batch in group_batches: ejv_file: Optional[EjvFileModel] = None receipt_number: Optional[str] = None @@ -143,6 +121,13 @@ def _process_ejv_feedback(group_batches) -> bool: # pylint:disable=too-many-loc if is_batch_group: batch_number = int(line[15:24]) ejv_file = EjvFileModel.find_by_id(batch_number) + if ejv_file.feedback_file_ref: + current_app.logger.info( + 'EJV file id %s with feedback file %s has already been processed, skipping.', + batch_number, file_name) + already_processed = True + return has_errors, already_processed + ejv_file.feedback_file_ref = file_name elif is_batch_header: return_code = line[7:11] return_message = line[11:161] @@ -170,7 +155,7 @@ def _process_ejv_feedback(group_batches) -> bool: # pylint:disable=too-many-loc has_errors = _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number) db.session.commit() - return has_errors + return has_errors, already_processed def _process_jv_details_feedback(ejv_file, has_errors, line, receipt_number): # pylint:disable=too-many-locals @@ -266,7 +251,7 @@ def _fix_invoice_line(line): # Check for zeros within 300->315 range. Bump them over with spaces. if (zero_position := line[300:315].find('0')) > -1: spaces_to_insert = 15 - zero_position - return line[:300 + zero_position] + (' ' * spaces_to_insert) + line[300 + zero_position:] + return line[:300+zero_position] + (' ' * spaces_to_insert) + line[300+zero_position:] return line @@ -331,7 +316,7 @@ def _publish_mailer_events(file_name: str, minio_location: str): gcp_queue_publisher.publish_to_queue( QueueMessage( source=QueueSources.PAY_QUEUE.value, - message_type=QueueMessageTypes.EJV_FAILED.value, + message_type=MessageType.EJV_FAILED.value, payload=payload, topic=current_app.config.get('ACCOUNT_MAILER_TOPIC') ) diff --git a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py index 8ff86f2b9..dd0774b1b 100644 --- a/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py +++ b/pay-queue/src/pay_queue/services/eft/eft_reconciliation.py @@ -64,9 +64,8 @@ def reconcile_eft_payments(msg: Dict[str, any]): # pylint: disable=too-many-loc eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( EFTFileModel.file_ref == file_name).one_or_none() - if eft_file_model and eft_file_model.status_code in \ - [EFTProcessStatus.IN_PROGRESS.value, EFTProcessStatus.COMPLETED.value]: - current_app.logger.info('File: %s already %s.', file_name, str(eft_file_model.status_code)) + if eft_file_model and eft_file_model.status_code == EFTProcessStatus.COMPLETED.value: + current_app.logger.info('File: %s already completed processing on %s.', file_name, eft_file_model.completed_on) return # There is no existing EFT File record - instantiate one diff --git a/pay-queue/src/pay_queue/services/payment_reconciliations.py b/pay-queue/src/pay_queue/services/payment_reconciliations.py index 82105187d..de5cefeb7 100644 --- a/pay-queue/src/pay_queue/services/payment_reconciliations.py +++ b/pay-queue/src/pay_queue/services/payment_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,9 +37,9 @@ from pay_api.services.non_sufficient_funds import NonSufficientFundsService from pay_api.services.payment_transaction import PaymentTransaction as PaymentTransactionService from pay_api.utils.enums import ( - CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, PaymentMethod, PaymentStatus, QueueSources) + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, LineItemStatus, MessageType, PaymentMethod, PaymentStatus, + QueueSources) from pay_api.utils.util import get_topic_for_corp_type -from sbc_common_components.utils.enums import QueueMessageTypes from sentry_sdk import capture_message from pay_queue import config @@ -191,9 +191,12 @@ def reconcile_payments(msg: Dict[str, any]): cas_settlement: CasSettlementModel = db.session.query(CasSettlementModel) \ .filter(CasSettlementModel.file_name == file_name).one_or_none() - if cas_settlement: - current_app.logger.info('File: %s has been processed or processing in progress. Skipping file. ' - 'Removing this row will allow processing to be restarted.', file_name) + if cas_settlement and not cas_settlement.processed_on: + current_app.logger.info('File: %s has attempted to be processed before.', file_name) + elif cas_settlement and cas_settlement.processed_on: + current_app.logger.info('File: %s already processed on: %s. Skipping file.', + file_name, cas_settlement.processed_on) + return else: current_app.logger.info('Creating cas_settlement record for file: %s', file_name) cas_settlement = _create_cas_settlement(file_name) @@ -274,13 +277,15 @@ def _process_consolidated_invoices(row): level='error') return _process_paid_invoices(inv_references, row) + if not APP_CONFIG.DISABLE_PAD_SUCCESS_EMAIL: + _publish_mailer_events(MessageType.PAD_PAYMENT_SUCCESS.value, payment_account, row) elif target_txn_status.lower() == Status.NOT_PAID.value.lower() \ or record_type in (RecordType.PADR.value, RecordType.PAYR.value): current_app.logger.info('NOT PAID. NSF identified.') # NSF Condition. Publish to account events for NSF. if _process_failed_payments(row): # Send mailer and account events to update status and send email notification - _publish_account_events(QueueMessageTypes.NSF_LOCK_ACCOUNT.value, payment_account, row) + _publish_account_events(MessageType.NSF_LOCK_ACCOUNT.value, payment_account, row) else: current_app.logger.error('Target Transaction Type is received as %s for PAD, and cannot process %s.', target_txn, row) @@ -588,7 +593,7 @@ def _publish_payment_event(inv: InvoiceModel): gcp_queue_publisher.publish_to_queue( QueueMessage( source=QueueSources.PAY_QUEUE.value, - message_type=QueueMessageTypes.PAYMENT.value, + message_type=MessageType.PAYMENT.value, payload=payload, topic=get_topic_for_corp_type(inv.corp_type_code) ) @@ -633,13 +638,13 @@ def _publish_online_banking_mailer_events(rows: List[Dict[str, str]], paid_amoun credit_amount: float = 0 if credit_rows: - message_type = QueueMessageTypes.ONLINE_BANKING_OVER_PAYMENT.value + message_type = 'bc.registry.payment.OverPaid' for row in credit_rows: credit_amount += float(_get_row_value(row, Column.APP_AMOUNT)) elif under_pay_rows: - message_type = QueueMessageTypes.ONLINE_BANKING_UNDER_PAYMENT.value + message_type = 'bc.registry.payment.UnderPaid' else: - message_type = QueueMessageTypes.ONLINE_BANKING_PAYMENT.value + message_type = 'bc.registry.payment.Payment' payload = { 'accountId': pay_account.auth_account_id, @@ -676,7 +681,7 @@ def _publish_account_events(message_type: str, pay_account: PaymentAccountModel, source=QueueSources.PAY_QUEUE.value, message_type=message_type, payload=payload, - topic=current_app.config.get('AUTH_EVENT_TOPIC') + topic=current_app.config.get('AUTH_QUEUE_TOPIC') ) ) except Exception as e: # NOQA pylint: disable=broad-except diff --git a/pay-queue/src/pay_queue/version.py b/pay-queue/src/pay_queue/version.py index c29b19b02..3b723bf04 100644 --- a/pay-queue/src/pay_queue/version.py +++ b/pay-queue/src/pay_queue/version.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/__init__.py b/pay-queue/tests/__init__.py index b2425e245..3e44a42f5 100644 --- a/pay-queue/tests/__init__.py +++ b/pay-queue/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/conftest.py b/pay-queue/tests/conftest.py index 8c5fdebba..c691641c5 100644 --- a/pay-queue/tests/conftest.py +++ b/pay-queue/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ # limitations under the License. """Common setup and fixtures for the pytest suite used by this service.""" import os -from concurrent.futures import CancelledError import pytest from flask_migrate import Migrate, upgrade @@ -31,6 +30,9 @@ def app(): """Return a session-wide application configured in TEST mode.""" _app = create_app('testing') + _app.config['GCP_AUTH_KEY'] = 'xxxxx' + _app.config['AUDIENCE'] = 'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber' + _app.config['PUBLISHER_AUDIENCE'] = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher' return _app @@ -143,7 +145,7 @@ def initialize_pubsub(app): except NotFound: pass publisher.create_topic(name=topic_path) - subscription_path = subscriber.subscription_path(project, f'{topic}_subscription') + subscription_path = subscriber.subscription_path(project, f'{topic}_subscription') try: subscriber.delete_subscription(subscription=subscription_path) except NotFound: @@ -155,19 +157,3 @@ def initialize_pubsub(app): 'push_config': push_config, } ) - - -@pytest.fixture(autouse=True) -def mock_pub_sub_call(mocker): - """Mock pub sub call.""" - class PublisherMock: - """Publisher Mock.""" - - def __init__(self, *args, **kwargs): - pass - - def publish(self, *args, **kwargs): - """Publish mock.""" - raise CancelledError('This is a mock') - - mocker.patch('google.cloud.pubsub_v1.PublisherClient', PublisherMock) diff --git a/pay-queue/tests/integration/__init__.py b/pay-queue/tests/integration/__init__.py index d95cabaff..44812fe3c 100644 --- a/pay-queue/tests/integration/__init__.py +++ b/pay-queue/tests/integration/__init__.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/integration/factory.py b/pay-queue/tests/integration/factory.py index 744cb377c..e7ccc4bdd 100644 --- a/pay-queue/tests/integration/factory.py +++ b/pay-queue/tests/integration/factory.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pay-queue/tests/integration/test_cgi_reconciliations.py b/pay-queue/tests/integration/test_cgi_reconciliations.py index 19ac6a16e..f308d9f46 100644 --- a/pay-queue/tests/integration/test_cgi_reconciliations.py +++ b/pay-queue/tests/integration/test_cgi_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -33,9 +33,8 @@ from pay_api.models import RoutingSlip as RoutingSlipModel from pay_api.models import db from pay_api.utils.enums import ( - CfsAccountStatus, DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, + CfsAccountStatus, DisbursementStatus, EjvFileType, EJVLinkType, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus, RoutingSlipStatus) -from sbc_common_components.utils.enums import QueueMessageTypes from tests.integration.utils import add_file_event_to_queue_and_process @@ -45,7 +44,7 @@ from .utils import upload_to_minio -def test_successful_partner_ejv_reconciliations(session, app, client): +def test_successful_partner_ejv_reconciliations(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -108,7 +107,7 @@ def test_successful_partner_ejv_reconciliations(session, app, client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -150,7 +149,7 @@ def test_successful_partner_ejv_reconciliations(session, app, client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -159,7 +158,7 @@ def test_successful_partner_ejv_reconciliations(session, app, client): assert invoice.disbursement_status_code == DisbursementStatus.COMPLETED.value -def test_failed_partner_ejv_reconciliations(session, app, client): +def test_failed_partner_ejv_reconciliations(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -224,7 +223,7 @@ def test_failed_partner_ejv_reconciliations(session, app, client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -266,7 +265,7 @@ def test_failed_partner_ejv_reconciliations(session, app, client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -277,7 +276,7 @@ def test_failed_partner_ejv_reconciliations(session, app, client): assert disbursement_distribution_code.stop_ejv -def test_successful_partner_reversal_ejv_reconciliations(session, app, client): +def test_successful_partner_reversal_ejv_reconciliations(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -343,7 +342,7 @@ def test_successful_partner_reversal_ejv_reconciliations(session, app, client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -385,7 +384,7 @@ def test_successful_partner_reversal_ejv_reconciliations(session, app, client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -395,7 +394,7 @@ def test_successful_partner_reversal_ejv_reconciliations(session, app, client): assert invoice.disbursement_date == datetime(2023, 5, 29) -def test_succesful_payment_ejv_reconciliations(session, app, client): +def test_succesful_payment_ejv_reconciliations(client): """Test Reconciliations worker.""" # 1. Create EJV payment accounts # 2. Create invoice and related records @@ -462,9 +461,10 @@ def test_succesful_payment_ejv_reconciliations(session, app, client): ejv_header: EjvHeaderModel = EjvHeaderModel(disbursement_status_code=DisbursementStatus.UPLOADED.value, ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() - EjvLinkModel(link_id=inv.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value - ).save() + EjvLinkModel( + link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) service_fee_amount = f'{line.service_fees:.2f}'.zfill(15) @@ -511,7 +511,7 @@ def test_succesful_payment_ejv_reconciliations(session, app, client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -527,7 +527,7 @@ def test_succesful_payment_ejv_reconciliations(session, app, client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -555,7 +555,7 @@ def test_succesful_payment_ejv_reconciliations(session, app, client): assert payment[0][0].paid_amount == inv_total_amount -def test_succesful_payment_reversal_ejv_reconciliations(session, app, client): +def test_succesful_payment_reversal_ejv_reconciliations(client): """Test Reconciliations worker.""" # 1. Create EJV payment accounts # 2. Create invoice and related records @@ -621,8 +621,8 @@ def test_succesful_payment_reversal_ejv_reconciliations(session, app, client): ejv_file_id=ejv_file.id, payment_account_id=jv_acc.id).save() EjvLinkModel( - link_id=inv.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + link_id=inv.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() inv_total = f'{inv.total:.2f}'.zfill(15) pay_line_amount = f'{line.total:.2f}'.zfill(15) @@ -669,7 +669,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(session, app, client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -685,7 +685,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(session, app, client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -711,7 +711,7 @@ def test_succesful_payment_reversal_ejv_reconciliations(session, app, client): assert payment[0][0].paid_amount == inv_total_amount -def test_successful_refund_reconciliations(session, app, client): +def test_successful_refund_reconciliations(client): """Test Reconciliations worker.""" # 1. Create a routing slip. # 2. Mark the routing slip for refund. @@ -759,7 +759,7 @@ def test_successful_refund_reconciliations(session, app, client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -834,7 +834,7 @@ def test_successful_refund_reconciliations(session, app, client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -844,7 +844,7 @@ def test_successful_refund_reconciliations(session, app, client): assert routing_slip.status == RoutingSlipStatus.REFUND_COMPLETED.value -def test_failed_refund_reconciliations(session, app, client): +def test_failed_refund_reconciliations(client): """Test Reconciliations worker.""" # 1. Create a routing slip. # 2. Mark the routing slip for refund. @@ -892,7 +892,7 @@ def test_failed_refund_reconciliations(session, app, client): # Now upload the ACK file to minio and publish message. upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -968,7 +968,7 @@ def test_failed_refund_reconciliations(session, app, client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) # Query EJV File and assert the status is changed ejv_file = EjvFileModel.find_by_id(ejv_file_id) @@ -980,7 +980,7 @@ def test_failed_refund_reconciliations(session, app, client): assert routing_slip_2.status == RoutingSlipStatus.REFUND_REJECTED.value -def test_prevent_duplicate_ack(session, app, client): +def test_prevent_duplicate_ack(client): """Assert processing completes when existing ack.""" file_ref = f'INBOX.{datetime.now()}' # Upload an acknowledgement file @@ -994,18 +994,18 @@ def test_prevent_duplicate_ack(session, app, client): jv_file.write('') jv_file.close() - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value # Nothing should change, because it's already processed this ACK. ejv.disbursement_status_code = DisbursementStatus.UPLOADED.value - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) assert ejv.ack_file_ref == ack_file_name assert ejv.disbursement_status_code == DisbursementStatus.UPLOADED.value -def test_successful_ap_disbursement(session, app, client): +def test_successful_ap_disbursement(client): """Test Reconciliations worker for ap disbursement.""" # 1. Create invoice. # 2. Create a AP reconciliation file. @@ -1048,12 +1048,12 @@ def test_successful_ap_disbursement(session, app, client): ejv_file_id=ejv_file.id, payment_account_id=account.id).save() EjvLinkModel( - link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, - ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value + link_id=invoice.id, link_type=EJVLinkType.INVOICE.value, + ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() EjvLinkModel( - link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, + link_id=refund_invoice.id, link_type=EJVLinkType.INVOICE.value, ejv_header_id=ejv_header.id, disbursement_status_code=DisbursementStatus.UPLOADED.value ).save() @@ -1065,7 +1065,7 @@ def test_successful_ap_disbursement(session, app, client): upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1138,7 +1138,7 @@ def test_successful_ap_disbursement(session, app, client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value @@ -1154,7 +1154,7 @@ def test_successful_ap_disbursement(session, app, client): assert refund.gl_posted is not None -def test_failure_ap_disbursement(session, app, client): +def test_failure_ap_disbursement(client): """Test Reconciliations worker for ap disbursement.""" # 1. Create invoice. # 2. Create a AP reconciliation file. @@ -1212,7 +1212,7 @@ def test_failure_ap_disbursement(session, app, client): upload_to_minio(str.encode(''), ack_file_name) - add_file_event_to_queue_and_process(client, ack_file_name, QueueMessageTypes.CGI_ACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, ack_file_name, MessageType.CGI_ACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.ACKNOWLEDGED.value @@ -1288,7 +1288,7 @@ def test_failure_ap_disbursement(session, app, client): with open(feedback_file_name, 'rb') as f: upload_to_minio(f.read(), feedback_file_name) - add_file_event_to_queue_and_process(client, feedback_file_name, QueueMessageTypes.CGI_FEEDBACK_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, feedback_file_name, MessageType.CGI_FEEDBACK_RECEIVED.value) ejv_file = EjvFileModel.find_by_id(ejv_file_id) assert ejv_file.disbursement_status_code == DisbursementStatus.COMPLETED.value diff --git a/pay-queue/tests/integration/test_eft_reconciliation.py b/pay-queue/tests/integration/test_eft_reconciliation.py index 97fb9520c..114ae89e2 100644 --- a/pay-queue/tests/integration/test_eft_reconciliation.py +++ b/pay-queue/tests/integration/test_eft_reconciliation.py @@ -28,8 +28,7 @@ from pay_api.models import EFTTransaction as EFTTransactionModel from pay_api.models import Invoice as InvoiceModel from pay_api.models import PaymentAccount as PaymentAccountModel -from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, PaymentMethod -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.utils.enums import EFTFileLineType, EFTProcessStatus, EFTShortnameStatus, MessageType, PaymentMethod from pay_queue.services.eft.eft_enums import EFTConstants from tests.integration.factory import factory_create_eft_account, factory_invoice @@ -37,7 +36,7 @@ from tests.utilities.factory_utils import factory_eft_header, factory_eft_record, factory_eft_trailer -def test_eft_tdi17_fail_header(session, app, client): +def test_eft_tdi17_fail_header(client): """Test EFT Reconciliations properly fails for a bad EFT header.""" # Generate file with invalid header file_name: str = 'test_eft_tdi17.txt' @@ -46,7 +45,7 @@ def test_eft_tdi17_fail_header(session, app, client): create_and_upload_eft_file(file_name, [header]) - add_file_event_to_queue_and_process(client, file_name, QueueMessageTypes.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name, MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -89,7 +88,7 @@ def test_eft_tdi17_fail_header(session, app, client): assert not bool(eft_transactions) -def test_eft_tdi17_fail_trailer(session, app, client): +def test_eft_tdi17_fail_trailer(client): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' @@ -100,9 +99,7 @@ def test_eft_tdi17_fail_trailer(session, app, client): create_and_upload_eft_file(file_name, [header, trailer]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -145,7 +142,7 @@ def test_eft_tdi17_fail_trailer(session, app, client): assert not bool(eft_transactions) -def test_eft_tdi17_fail_transactions(session, app, client): +def test_eft_tdi17_fail_transactions(client): """Test EFT Reconciliations properly fails for a bad EFT trailer.""" # Generate file with invalid trailer file_name: str = 'test_eft_tdi17.txt' @@ -164,9 +161,7 @@ def test_eft_tdi17_fail_transactions(session, app, client): create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -204,15 +199,13 @@ def test_eft_tdi17_fail_transactions(session, app, client): assert eft_transactions[0].error_messages[0] == 'Invalid transaction deposit amount CAD.' -def test_eft_tdi17_basic_process(session, app, client): +def test_eft_tdi17_basic_process(client): """Test EFT Reconciliations worker is able to create basic EFT processing records.""" # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_basic_tdi17_file(file_name) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -288,20 +281,14 @@ def test_eft_tdi17_basic_process(session, app, client): assert not eft_credit_invoice_links -def test_eft_tdi17_process(session, app, client): +def test_eft_tdi17_process(client): """Test EFT Reconciliations worker.""" payment_account, eft_shortname, invoice = create_test_data() - - assert payment_account is not None - assert eft_shortname is not None - assert invoice is not None # Generate happy path file file_name: str = 'test_eft_tdi17.txt' generate_tdi17_file(file_name) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -356,6 +343,7 @@ def test_eft_tdi17_process(session, app, client): # NOTE THIS NEEDS TO BE RE-WRITTEN INSIDE OF THE JOB. # today = datetime.now().date() + # # Assert Invoice is paid # invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id) # expected_amount = 100 @@ -422,7 +410,7 @@ def test_eft_tdi17_process(session, app, client): # assert eft_credit_invoice_links[0].invoice_id == invoice.id -def test_eft_tdi17_rerun(session, app, client): +def test_eft_tdi17_rerun(client): """Test EFT Reconciliations can be re-executed with a corrected file.""" payment_account, eft_shortname, invoice = create_test_data() @@ -443,9 +431,7 @@ def test_eft_tdi17_rerun(session, app, client): create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Assert EFT File record was created eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( @@ -484,9 +470,7 @@ def test_eft_tdi17_rerun(session, app, client): jv_number='002425669', transaction_date='') create_and_upload_eft_file(file_name, [header, transaction_1, trailer]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.EFT_FILE_UPLOADED.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.EFT_FILE_UPLOADED.value) # Check file is completed after correction eft_file_model: EFTFileModel = db.session.query(EFTFileModel).filter( diff --git a/pay-queue/tests/integration/test_payment_reconciliations.py b/pay-queue/tests/integration/test_payment_reconciliations.py index d63a3d4aa..8e6850413 100644 --- a/pay-queue/tests/integration/test_payment_reconciliations.py +++ b/pay-queue/tests/integration/test_payment_reconciliations.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,8 +26,8 @@ from pay_api.models import Payment as PaymentModel from pay_api.models import PaymentAccount as PaymentAccountModel from pay_api.models import Receipt as ReceiptModel -from pay_api.utils.enums import CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.utils.enums import ( + CfsAccountStatus, InvoiceReferenceStatus, InvoiceStatus, MessageType, PaymentMethod, PaymentStatus) from pay_queue.enums import RecordType, SourceTransaction, Status, TargetTransaction @@ -37,7 +37,7 @@ from .utils import add_file_event_to_queue_and_process, create_and_upload_settlement_file -def test_online_banking_reconciliations(session, app, client): +def test_online_banking_reconciliations(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -67,9 +67,7 @@ def test_online_banking_reconciliations(session, app, client): TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -83,7 +81,7 @@ def test_online_banking_reconciliations(session, app, client): assert payment.invoice_number == invoice_number -def test_online_banking_reconciliations_over_payment(session, app, client): +def test_online_banking_reconciliations_over_payment(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -115,9 +113,7 @@ def test_online_banking_reconciliations_over_payment(session, app, client): over_payment_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, over_payment_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -131,7 +127,7 @@ def test_online_banking_reconciliations_over_payment(session, app, client): assert payment.invoice_number is None # No invoice_number if payment is not for 1 invoice -def test_online_banking_reconciliations_with_credit(session, app, client): +def test_online_banking_reconciliations_with_credit(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -163,9 +159,7 @@ def test_online_banking_reconciliations_with_credit(session, app, client): credit_row = [RecordType.ONAC.value, SourceTransaction.EFT_WIRE.value, '555566677', 100001, date, credit_amount, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -179,7 +173,7 @@ def test_online_banking_reconciliations_with_credit(session, app, client): assert payment.invoice_number == invoice_number -def test_online_banking_reconciliations_overflows_credit(session, app, client): +def test_online_banking_reconciliations_overflows_credit(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -216,9 +210,7 @@ def test_online_banking_reconciliations_overflows_credit(session, app, client): Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [inv_row, credit_row, onac_row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -232,7 +224,7 @@ def test_online_banking_reconciliations_overflows_credit(session, app, client): assert payment.invoice_number is None -def test_online_banking_under_payment(session, app, client): +def test_online_banking_under_payment(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -264,9 +256,7 @@ def test_online_banking_under_payment(session, app, client): TargetTransaction.INV.value, invoice_number, total, total - paid_amount, Status.PARTIAL.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id) @@ -281,7 +271,7 @@ def test_online_banking_under_payment(session, app, client): assert payment.invoice_number == invoice_number -def test_pad_reconciliations(session, app, client): +def test_pad_reconciliations(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoices and related records @@ -323,9 +313,7 @@ def test_pad_reconciliations(session, app, client): 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -347,7 +335,7 @@ def test_pad_reconciliations(session, app, client): assert rcpt1.receipt_date == rcpt2.receipt_date -def test_pad_reconciliations_with_credit_memo(session, app, client): +def test_pad_reconciliations_with_credit_memo(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoices and related records @@ -393,9 +381,7 @@ def test_pad_reconciliations_with_credit_memo(session, app, client): pad_row = [RecordType.PAD.value, SourceTransaction.PAD.value, receipt_number, 100001, date, total - credit_amount, cfs_account_number, 'INV', invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [credit_row, pad_row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -417,7 +403,7 @@ def test_pad_reconciliations_with_credit_memo(session, app, client): assert rcpt1.receipt_date == rcpt2.receipt_date -def test_pad_nsf_reconciliations(session, app, client): +def test_pad_nsf_reconciliations(client): """Test Reconciliations worker for NSF.""" # 1. Create payment account # 2. Create invoices and related records @@ -460,9 +446,7 @@ def test_pad_nsf_reconciliations(session, app, client): 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -481,7 +465,7 @@ def test_pad_nsf_reconciliations(session, app, client): assert cfs_account.status == CfsAccountStatus.FREEZE.value -def test_pad_reversal_reconciliations(session, app, client): +def test_pad_reversal_reconciliations(client): """Test Reconciliations worker for NSF.""" # 1. Create payment account # 2. Create invoices and related records for a completed payment @@ -533,9 +517,7 @@ def test_pad_reversal_reconciliations(session, app, client): 'INV', invoice_number, total, total, Status.NOT_PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED updated_invoice1 = InvoiceModel.find_by_id(invoice1_id) @@ -559,7 +541,7 @@ def test_pad_reversal_reconciliations(session, app, client): @pytest.mark.asyncio -async def test_eft_wire_reconciliations(session, app, client): +async def test_eft_wire_reconciliations(client): """Test Reconciliations worker.""" # 1. Create payment account # 2. Create invoice and related records @@ -600,9 +582,7 @@ async def test_eft_wire_reconciliations(session, app, client): row = [RecordType.EFTP.value, SourceTransaction.EFT_WIRE.value, eft_wire_receipt, 100001, date, total, cfs_account_number, TargetTransaction.INV.value, invoice_number, total, 0, Status.PAID.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # The invoice should be in PAID status and Payment should be completed updated_invoice = InvoiceModel.find_by_id(invoice_id) @@ -615,7 +595,7 @@ async def test_eft_wire_reconciliations(session, app, client): @pytest.mark.asyncio -async def test_credits(session, app, client, monkeypatch): +async def test_credits(client, monkeypatch): """Test Reconciliations worker.""" # 1. Create payment account. # 2. Create EFT/WIRE payment db record. @@ -677,9 +657,7 @@ def mock_cms(cfs_account: CfsAccountModel, cfs_account_number, TargetTransaction.RECEIPT.value, eft_wire_receipt, onac_amount, 0, Status.ON_ACC.value] create_and_upload_settlement_file(file_name, [row]) - add_file_event_to_queue_and_process(client, - file_name=file_name, - message_type=QueueMessageTypes.CAS_MESSAGE_TYPE.value) + add_file_event_to_queue_and_process(client, file_name=file_name, message_type=MessageType.CAS_UPLOADED.value) # Look up credit file and make sure the credits are recorded. pay_account = PaymentAccountModel.find_by_id(pay_account_id) diff --git a/pay-queue/tests/integration/test_worker_queue.py b/pay-queue/tests/integration/test_worker_queue.py index 79d1002e2..932aa48bd 100644 --- a/pay-queue/tests/integration/test_worker_queue.py +++ b/pay-queue/tests/integration/test_worker_queue.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ from .utils import helper_add_identifier_event_to_queue -def test_update_payment(session, app, client): +def test_update_payment(client): """Assert that the update internal payment records works.""" # vars old_identifier = 'T000000000' diff --git a/pay-queue/tests/integration/utils.py b/pay-queue/tests/integration/utils.py index 07b9e7c7e..58d190d4b 100644 --- a/pay-queue/tests/integration/utils.py +++ b/pay-queue/tests/integration/utils.py @@ -1,4 +1,4 @@ -# Copyright © 2024 Province of British Columbia +# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,8 +27,7 @@ from minio import Minio from pay_api.services import gcp_queue_publisher from pay_api.services.gcp_queue_publisher import QueueMessage -from pay_api.utils.enums import QueueSources -from sbc_common_components.utils.enums import QueueMessageTypes +from pay_api.utils.enums import MessageType, QueueSources from simple_cloudevent import SimpleCloudEvent, to_queue_message @@ -99,7 +98,7 @@ def upload_to_minio(value_as_bytes, file_name: str): os.stat(file_name).st_size) -def forward_incoming_message_to_test_instance(session, app, client): +def forward_incoming_message_to_test_instance(client): """Forward incoming http message to test instance.""" # Note this is a bit different than how the queue could behave, it could send multiples. # This just receives one HTTP request and forwards it to the test instance. @@ -123,7 +122,7 @@ def forward_incoming_message_to_test_instance(session, app, client): assert tries > 0 -def add_file_event_to_queue_and_process(client, file_name: str, message_type: str, use_pubsub_emulator=False): +def add_file_event_to_queue_and_process(client, file_name: str, message_type: str, use_pubsub_emulator=True): """Add event to the Queue.""" queue_payload = { 'fileName': file_name, @@ -147,7 +146,7 @@ def add_file_event_to_queue_and_process(client, file_name: str, message_type: st def helper_add_identifier_event_to_queue(client, old_identifier: str = 'T1234567890', new_identifier: str = 'BC1234567890'): """Add event to the Queue.""" - message_type = QueueMessageTypes.INCORPORATION.value + message_type = MessageType.INCORPORATION.value queue_payload = { 'filing': { 'header': {'filingId': '12345678'}, diff --git a/report-api/requirements.txt b/report-api/requirements.txt index 4e81b8787..3956cf9ad 100644 --- a/report-api/requirements.txt +++ b/report-api/requirements.txt @@ -21,7 +21,7 @@ flask-restx==1.1.0 fonttools==4.43.0 gunicorn==20.1.0 html5lib==1.1 -idna==3.7 +idna==3.4 importlib-resources==5.12.0 itsdangerous==2.0.1 jaeger-client==4.8.0 From dcaf68d0cf323e00911b3ee8a68329610725d71a Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 09:55:39 -0700 Subject: [PATCH 72/87] Lint fixes --- jobs/notebook-report/notebookreport.py | 3 +++ report-api/src/api/services/report_service.py | 1 + 2 files changed, 4 insertions(+) diff --git a/jobs/notebook-report/notebookreport.py b/jobs/notebook-report/notebookreport.py index 9af2644ed..c8fff20e4 100644 --- a/jobs/notebook-report/notebookreport.py +++ b/jobs/notebook-report/notebookreport.py @@ -50,6 +50,9 @@ def send_email(file_processing, emailtype, errormessage, partner_code=None): message = MIMEMultipart() date_str = datetime.strftime(datetime.now() - timedelta(1), '%Y-%m-%d') ext = '' + filenames = [] + subject = '' + recipients = '' if not Config.ENVIRONMENT == 'prod': ext = ' on ' + Config.ENVIRONMENT diff --git a/report-api/src/api/services/report_service.py b/report-api/src/api/services/report_service.py index c8a84d159..7ffa07809 100644 --- a/report-api/src/api/services/report_service.py +++ b/report-api/src/api/services/report_service.py @@ -27,6 +27,7 @@ def format_datetime(value, format='short'): # pylint: disable=redefined-builtin """Filter to format datetime globally.""" + dt_format = '%m-%d-%Y' if format == 'full': dt_format = '%m-%d-%Y %I:%M %p' elif format == 'short': From 2a3214ab85cece79cdd129d5ac61cf8f89f4b975 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 10:02:01 -0700 Subject: [PATCH 73/87] Small lint fixes --- bcol-api/src/bcol_api/utils/trace.py | 8 -------- pay-api/src/pay_api/services/statement_settings.py | 2 +- report-api/src/api/utils/logging.py | 4 ++-- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/bcol-api/src/bcol_api/utils/trace.py b/bcol-api/src/bcol_api/utils/trace.py index ece264f83..b62a1646d 100644 --- a/bcol-api/src/bcol_api/utils/trace.py +++ b/bcol-api/src/bcol_api/utils/trace.py @@ -12,11 +12,3 @@ # See the License for the specific language governing permissions and # limitations under the License. """Bring in the Tracer.""" -# from sbc_common_components.tracing.api_tracer import ApiTracer -# from sbc_common_components.tracing.api_tracing import ApiTracing - - -# initialize tracer -# API_TRACER = ApiTracer('BCOL API Services') -# tracing = ApiTracing( # pylint: disable=invalid-name; lower case name as used by convention in most Flask apps -# API_TRACER.tracer) diff --git a/pay-api/src/pay_api/services/statement_settings.py b/pay-api/src/pay_api/services/statement_settings.py index c233b96f3..fcdb203bd 100644 --- a/pay-api/src/pay_api/services/statement_settings.py +++ b/pay-api/src/pay_api/services/statement_settings.py @@ -168,7 +168,7 @@ def update_statement_settings(auth_account_id: str, frequency: str): max_frequency = StatementSettings._find_longest_frequency(current_statements_settings.frequency, frequency) last_date = StatementSettings._get_end_of(max_frequency) - current_statements_settings.to_date = last_date # TODO Should be date not date time? + current_statements_settings.to_date = last_date current_statements_settings.save() new_statements_settings = StatementSettingsModel(frequency=frequency, diff --git a/report-api/src/api/utils/logging.py b/report-api/src/api/utils/logging.py index 8568f87dd..afa83de96 100755 --- a/report-api/src/api/utils/logging.py +++ b/report-api/src/api/utils/logging.py @@ -21,6 +21,6 @@ def setup_logging(conf): """Create the services logger.""" if conf and path.isfile(conf): logging.config.fileConfig(conf) - print(f'Configure logging, from conf:{conf}', file=sys.stdout) + print(f'Configure logging, from conf: {conf}', file=sys.stdout) else: - print(f'Unable to configure logging, attempted conf:{conf}', file=sys.stderr) + print(f'Unable to configure logging, attempted conf: {conf}', file=sys.stderr) From 4e0b5ce31bca1bb07412deec8e9576e09f86b811 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 10:05:52 -0700 Subject: [PATCH 74/87] Remove duplicate literal --- pay-api/src/pay_api/resources/v1/eft_short_names.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pay-api/src/pay_api/resources/v1/eft_short_names.py b/pay-api/src/pay_api/resources/v1/eft_short_names.py index 1fadbc58f..0be018dd7 100644 --- a/pay-api/src/pay_api/resources/v1/eft_short_names.py +++ b/pay-api/src/pay_api/resources/v1/eft_short_names.py @@ -105,8 +105,7 @@ def get_eft_shortname(short_name_id: int): current_app.logger.info('get_eft_shortname') @@ -140,8 +139,7 @@ def get_eft_shortname_links(short_name_id: int): try: if not EFTShortnameService.find_by_short_name_id(short_name_id): - response, status = {'message': 'The requested EFT short name could not be found.'}, \ - HTTPStatus.NOT_FOUND + response, status = {}, HTTPStatus.NOT_FOUND else: response, status = EFTShortnameService.get_shortname_links(short_name_id), HTTPStatus.OK except BusinessException as exception: @@ -161,8 +159,7 @@ def post_eft_shortname_link(short_name_id: int): try: if not EFTShortnameService.find_by_short_name_id(short_name_id): - response, status = {'message': 'The requested EFT short name could not be found.'}, \ - HTTPStatus.NOT_FOUND + response, status = {}, HTTPStatus.NOT_FOUND else: account_id = request_json.get('accountId', None) response, status = EFTShortnameService.create_shortname_link(short_name_id, account_id), HTTPStatus.OK @@ -183,8 +180,7 @@ def delete_eft_shortname_link(short_name_id: int, short_name_link_id: int): try: link = EFTShortnameService.find_link_by_id(short_name_link_id) if not link or link['short_name_id'] != short_name_id: - response, status = {'message': 'The requested EFT short name could not be found.'}, \ - HTTPStatus.NOT_FOUND + response, status = {}, HTTPStatus.NOT_FOUND else: EFTShortnameService.delete_shortname_link(short_name_link_id) response, status = None, HTTPStatus.ACCEPTED From 4b8c72a22397eaf26e680451be30979097b93c7e Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 10:06:35 -0700 Subject: [PATCH 75/87] fix hint --- pay-api/src/pay_api/services/eft_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-api/src/pay_api/services/eft_service.py b/pay-api/src/pay_api/services/eft_service.py index 8b13b750b..657ba6271 100644 --- a/pay-api/src/pay_api/services/eft_service.py +++ b/pay-api/src/pay_api/services/eft_service.py @@ -64,7 +64,7 @@ def update_account(self, name: str, cfs_account: CfsAccountModel, payment_info: return cfs_account def create_invoice(self, payment_account: PaymentAccount, line_items: List[PaymentLineItem], invoice: Invoice, - **kwargs) -> InvoiceReference: + **kwargs) -> None: """Do nothing here, we create invoice references on the create CFS_INVOICES job.""" return From b466443c09dba3c9fa0ccb760bf972c77b49d217 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 10:07:58 -0700 Subject: [PATCH 76/87] remove redundant return --- pay-api/src/pay_api/services/deposit_service.py | 4 +--- pay-api/src/pay_api/services/eft_service.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pay-api/src/pay_api/services/deposit_service.py b/pay-api/src/pay_api/services/deposit_service.py index 964cc6664..6ae9f7f07 100644 --- a/pay-api/src/pay_api/services/deposit_service.py +++ b/pay-api/src/pay_api/services/deposit_service.py @@ -46,6 +46,4 @@ def create_account(self, identifier: str, contact_info: Dict[str, Any], payment_ def create_invoice(self, payment_account: PaymentAccount, line_items: List[PaymentLineItem], invoice: Invoice, **kwargs) -> InvoiceReference: - """Return a static invoice number for direct pay.""" - current_app.logger.debug(' None: """Do nothing here, we create invoice references on the create CFS_INVOICES job.""" - return def apply_credit(self, invoice: Invoice, From 578d6763be35d311692f971796251179cd661ce1 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 10:10:05 -0700 Subject: [PATCH 77/87] Codecov fixes --- .../src/pay_api/services/eft_short_names.py | 2 +- .../pay_api/services/gcp_queue/gcp_queue.py | 180 ------------------ 2 files changed, 1 insertion(+), 181 deletions(-) delete mode 100644 pay-api/src/pay_api/services/gcp_queue/gcp_queue.py diff --git a/pay-api/src/pay_api/services/eft_short_names.py b/pay-api/src/pay_api/services/eft_short_names.py index 9d1da9b71..ced443315 100644 --- a/pay-api/src/pay_api/services/eft_short_names.py +++ b/pay-api/src/pay_api/services/eft_short_names.py @@ -110,7 +110,7 @@ def delete_shortname_link(cls, short_name_link_id: int): current_app.logger.debug('>delete_shortname_link') @classmethod - def get_shortname_links(cls, short_name_id: int) -> List[EFTShortnameLinksModel]: + def get_shortname_links(cls, short_name_id: int) -> dict: """Get EFT short name account links.""" current_app.logger.debug(' Optional[dict]: - """Return the envelope.""" - with suppress(Exception): - if (envelope := request.get_json()) and GcpQueue.is_valid_envelope(envelope): - return envelope - return None - - @staticmethod - def get_simple_cloud_event(request: LocalProxy, return_raw: bool = False) -> type[SimpleCloudEvent | dict | None]: - """Return a SimpleCloudEvent if one is in session from the PubSub call. - - Parameters - ------------ - request: LocalProxy - An active Flask request object - return_raw: bool, Optional = False - Flag to return the raw data on error, if it exists - Return - ----------- - ce_returned: boolean - if a ce is returned == True - SimpleCloudEvent | - dict | - None - the second value returned is either a: - SimpleCloudEvent -or- - None - if there is no SimpleCloudEvent - - dict - if return_raw was set to true and it's not a SimpleCloudEvent -or- - """ - if not (envelope := GcpQueue.get_envelope(request)): - return None - - if ( - (message := envelope.get('message')) and - (raw_data := message.get('data')) and - (str_data := base64.b64decode(raw_data)) - ): - try: - return from_queue_message(str_data) - except ( # pylint: disable=broad-exception-caught - CloudEventVersionException, - InvalidCloudEventError, - ValueError, - Exception, - ): - if return_raw and str_data: - return str_data - return None - - def publish(self, topic: str, payload: bytes): - """Send payload to the queue.""" - if not (publisher := self.publisher): - raise Exception('missing setup arguments') # pylint: disable=W0719 - - try: - future = publisher.publish(topic, payload) - - return future.result() - except (CancelledError, TimeoutError) as error: - raise Exception('Unable to post to queue', error) from error # pylint: disable=W0719 - - @staticmethod - def to_queue_message(ce: SimpleCloudEvent): - """Return a byte string of the CloudEvent in JSON format.""" - return to_queue_message(ce) - - @staticmethod - def from_queue_message(data: dict): - """Convert a queue message back to a simple CloudEvent.""" - return from_queue_message(data) From 05d4f8c88f5432c0ae4bc52e471132ba31b095bb Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 10:12:04 -0700 Subject: [PATCH 78/87] more lint fixes --- pay-api/src/pay_api/services/deposit_service.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pay-api/src/pay_api/services/deposit_service.py b/pay-api/src/pay_api/services/deposit_service.py index 6ae9f7f07..e66d74845 100644 --- a/pay-api/src/pay_api/services/deposit_service.py +++ b/pay-api/src/pay_api/services/deposit_service.py @@ -14,8 +14,6 @@ """Service to manage CFS EFT/Wire/Direct Deposit Payments.""" from typing import Any, Dict, List -from flask import current_app - from pay_api.models import CfsAccount as CfsAccountModel from pay_api.services.base_payment_system import PaymentSystemService from pay_api.services.cfs_service import CFSService From 6f62e366e6d22e4f75e292fd83951133c4a2ca98 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 10:18:39 -0700 Subject: [PATCH 79/87] Fix CD --- .github/workflows/pay-api-cd-gcp.yml | 6 +- .github/workflows/pay-queue-gcp-cd.yml | 7 +- .github/workflows/payment-jobs-cd-gcp.yml | 32 ++++++ .github/workflows/payment-jobs-cd.yml | 118 ++++++++++++++++++---- 4 files changed, 138 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/payment-jobs-cd-gcp.yml diff --git a/.github/workflows/pay-api-cd-gcp.yml b/.github/workflows/pay-api-cd-gcp.yml index a5fe4c8dc..b33b75ff0 100644 --- a/.github/workflows/pay-api-cd-gcp.yml +++ b/.github/workflows/pay-api-cd-gcp.yml @@ -1,9 +1,9 @@ -name: Pay API CD +name: Pay API CD GCP on: push: branches: - - feature-queue-python-upgrade + - feature-gcp paths: - "pay-api/**" workflow_dispatch: @@ -27,4 +27,4 @@ jobs: working_directory: "./pay-api" secrets: WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/.github/workflows/pay-queue-gcp-cd.yml b/.github/workflows/pay-queue-gcp-cd.yml index 73e2b0518..b66871c61 100644 --- a/.github/workflows/pay-queue-gcp-cd.yml +++ b/.github/workflows/pay-queue-gcp-cd.yml @@ -1,10 +1,9 @@ -name: Pay Queue CD +name: Pay Queue CD GCP on: push: branches: - - main - - feature* + - feature-gcp paths: - "pay-queue/**" - "pay-api/src/pay_api/models/**" @@ -30,4 +29,4 @@ jobs: working_directory: "./pay-queue" secrets: WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/.github/workflows/payment-jobs-cd-gcp.yml b/.github/workflows/payment-jobs-cd-gcp.yml new file mode 100644 index 000000000..ea77e4190 --- /dev/null +++ b/.github/workflows/payment-jobs-cd-gcp.yml @@ -0,0 +1,32 @@ +name: Payment Jobs CD GCP + +on: + push: + branches: + - feature-gcp + paths: + - "jobs/payment-jobs/**" + - "pay-api/src/pay_api/models/**" + - "pay-api/src/pay_api/services/cfs_service.py" + workflow_dispatch: + inputs: + target: + description: "Deploy To" + required: true + type: choice + options: + - dev + - test + - sandbox + - prod + +jobs: + payment-jobs-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-job-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "payment-jobs" + working_directory: "./jobs/payment-jobs" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/.github/workflows/payment-jobs-cd.yml b/.github/workflows/payment-jobs-cd.yml index 467f61096..33d7a921f 100644 --- a/.github/workflows/payment-jobs-cd.yml +++ b/.github/workflows/payment-jobs-cd.yml @@ -4,30 +4,112 @@ on: push: branches: - main - - feature* paths: - "jobs/payment-jobs/**" - "pay-api/src/pay_api/models/**" - "pay-api/src/pay_api/services/cfs_service.py" workflow_dispatch: inputs: - target: - description: "Deploy To" + environment: + description: "Environment (dev/test/prod)" required: true - type: choice - options: - - dev - - test - - sandbox - - prod + default: "dev" + +defaults: + run: + shell: bash + working-directory: ./jobs/payment-jobs + +env: + APP_NAME: "payment-job" + TAG_NAME: "dev" jobs: - payment-jobs-cd: - uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-job-cd.yaml@main - with: - target: ${{ inputs.target }} - app_name: "payment-jobs" - working_directory: "./jobs/payment-jobs" - secrets: - WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} + payment-jobs-cd-by-push: + runs-on: ubuntu-20.04 + + if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' + environment: + name: "dev" + + steps: + - uses: actions/checkout@v3 + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Payment Job Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} + + payment-jobs-cd-by-dispatch: + runs-on: ubuntu-20.04 + + if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' + environment: + name: "${{ github.event.inputs.environment }}" + + steps: + - uses: actions/checkout@v3 + - name: Set env by input + run: | + echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Payment Job Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} From f0885f2690072a2e101b7dcd2d5e4575899ba189 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 10:25:53 -0700 Subject: [PATCH 80/87] Put back pay-api cd --- .github/workflows/pay-api-cd.yml | 118 ++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 18 deletions(-) diff --git a/.github/workflows/pay-api-cd.yml b/.github/workflows/pay-api-cd.yml index 0d2bb5020..09a994b61 100644 --- a/.github/workflows/pay-api-cd.yml +++ b/.github/workflows/pay-api-cd.yml @@ -4,28 +4,110 @@ on: push: branches: - main - - feature* paths: - "pay-api/**" workflow_dispatch: inputs: - target: - description: "Deploy To" + environment: + description: "Environment (dev/test/prod)" required: true - type: choice - options: - - dev - - test - - sandbox - - prod + default: "dev" + +defaults: + run: + shell: bash + working-directory: ./pay-api + +env: + APP_NAME: "pay-api" + TAG_NAME: "dev" jobs: - pay-api-cd: - uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main - with: - target: ${{ inputs.target }} - app_name: "pay-api" - working_directory: "./pay-api" - secrets: - WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} + pay-api-cd-by-push: + runs-on: ubuntu-20.04 + + if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' + environment: + name: "dev" + + steps: + - uses: actions/checkout@v3 + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Pay API Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} + + pay-api-cd-by-dispatch: + runs-on: ubuntu-20.04 + + if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' + environment: + name: "${{ github.event.inputs.environment }}" + + steps: + - uses: actions/checkout@v3 + - name: Set env by input + run: | + echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Pay API Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} From 12c620caef665ff97b46bc518cba9a2c12755ffe Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 11:32:27 -0700 Subject: [PATCH 81/87] update poetry, will fix error building --- pay-admin/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-admin/Dockerfile b/pay-admin/Dockerfile index 9b1480f8c..ad6a87170 100644 --- a/pay-admin/Dockerfile +++ b/pay-admin/Dockerfile @@ -32,7 +32,7 @@ ENV APP_ENV=${APP_ENV} \ PIP_DEFAULT_TIMEOUT=100 \ PIP_ROOT_USER_ACTION=ignore \ # poetry: - POETRY_VERSION=1.3.2 \ + POETRY_VERSION=1.8.3 \ POETRY_NO_INTERACTION=1 \ POETRY_VIRTUALENVS_CREATE=false \ POETRY_CACHE_DIR='/var/cache/pypoetry' \ From 7f4cede6914eb1a0ab1d9287976478715df5b44e Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 11:37:58 -0700 Subject: [PATCH 82/87] update psycopg2-binary --- jobs/notebook-report/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jobs/notebook-report/requirements.txt b/jobs/notebook-report/requirements.txt index 4a095802e..016fe67b3 100644 --- a/jobs/notebook-report/requirements.txt +++ b/jobs/notebook-report/requirements.txt @@ -1,6 +1,6 @@ jupyter SQLAlchemy==1.3.16 -psycopg2-binary==2.8.5 +psycopg2-binary==2.9.9 ipython-sql simplejson pandas From 49e1d82a1842121b6bcad06613d14fe12e5a76e6 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 11:43:15 -0700 Subject: [PATCH 83/87] fix CD --- .github/workflows/pay-admin-cd-gcp.yml | 30 +++++++ .github/workflows/pay-admin-cd.yml | 119 +++++++++++++++++++++---- 2 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/pay-admin-cd-gcp.yml diff --git a/.github/workflows/pay-admin-cd-gcp.yml b/.github/workflows/pay-admin-cd-gcp.yml new file mode 100644 index 000000000..484c0ae28 --- /dev/null +++ b/.github/workflows/pay-admin-cd-gcp.yml @@ -0,0 +1,30 @@ +name: Pay Admin CD GCP + +on: + push: + branches: + - feature-gcp + paths: + - "pay-admin/**" + workflow_dispatch: + inputs: + target: + description: "Deploy To" + required: true + type: choice + options: + - dev + - test + - sandbox + - prod + +jobs: + pay-admin-cd: + uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main + with: + target: ${{ inputs.target }} + app_name: "pay-admin" + working_directory: "./pay-admin" + secrets: + WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} diff --git a/.github/workflows/pay-admin-cd.yml b/.github/workflows/pay-admin-cd.yml index fb9d18ed8..17059c411 100644 --- a/.github/workflows/pay-admin-cd.yml +++ b/.github/workflows/pay-admin-cd.yml @@ -4,28 +4,111 @@ on: push: branches: - main - - feature* paths: - "pay-admin/**" + - "pay-api/src/pay_api/models/**" workflow_dispatch: inputs: - target: - description: "Deploy To" + environment: + description: "Environment (dev/test/prod)" required: true - type: choice - options: - - dev - - test - - sandbox - - prod + default: "dev" + +defaults: + run: + shell: bash + working-directory: ./pay-admin + +env: + APP_NAME: "pay-admin" + TAG_NAME: "dev" jobs: - pay-admin-cd: - uses: bcgov/bcregistry-sre/.github/workflows/cloud-run-service-cd.yaml@main - with: - target: ${{ inputs.target }} - app_name: "pay-admin" - working_directory: "./pay-admin" - secrets: - WORKLOAD_IDENTIFY_POOLS_PROVIDER: ${{ secrets.WORKLOAD_IDENTIFY_POOLS_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} \ No newline at end of file + pay-admin-cd-by-push: + runs-on: ubuntu-20.04 + + if: github.event_name == 'push' && github.repository == 'bcgov/sbc-pay' + environment: + name: "dev" + + steps: + - uses: actions/checkout@v3 + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Pay Admin Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} + + pay-admin-cd-by-dispatch: + runs-on: ubuntu-20.04 + + if: github.event_name == 'workflow_dispatch' && github.repository == 'bcgov/sbc-pay' + environment: + name: "${{ github.event.inputs.environment }}" + + steps: + - uses: actions/checkout@v3 + - name: Set env by input + run: | + echo "TAG_NAME=${{ github.event.inputs.environment }}" >> $GITHUB_ENV + + - name: Login Openshift + shell: bash + run: | + oc login --server=${{secrets.OPENSHIFT4_LOGIN_REGISTRY}} --token=${{secrets.OPENSHIFT4_SA_TOKEN}} + + - name: CD Flow + shell: bash + env: + OPS_REPOSITORY: ${{ secrets.OPS_REPOSITORY }} + OPENSHIFT_DOCKER_REGISTRY: ${{ secrets.OPENSHIFT4_DOCKER_REGISTRY }} + OPENSHIFT_SA_NAME: ${{ secrets.OPENSHIFT4_SA_NAME }} + OPENSHIFT_SA_TOKEN: ${{ secrets.OPENSHIFT4_SA_TOKEN }} + OPENSHIFT_REPOSITORY: ${{ secrets.OPENSHIFT4_REPOSITORY }} + TAG_NAME: ${{ env.TAG_NAME }} + run: | + make cd + + - name: Watch new rollout (trigger by image change in Openshift) + shell: bash + run: | + oc rollout status dc/${{ env.APP_NAME }}-${{ env.TAG_NAME }} -n ${{ secrets.OPENSHIFT4_REPOSITORY }}-${{ env.TAG_NAME }} -w + + - name: Rocket.Chat Notification + uses: RocketChat/Rocket.Chat.GitHub.Action.Notification@master + if: failure() + with: + type: ${{ job.status }} + job_name: "*Pay Admin Built and Deployed to ${{env.TAG_NAME}}*" + channel: "#registries-bot" + url: ${{ secrets.ROCKETCHAT_WEBHOOK }} + commit: true + token: ${{ secrets.GITHUB_TOKEN }} From ab42afc78d51db214105105276cfa4f329130461 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 11:50:26 -0700 Subject: [PATCH 84/87] remove requirements.txt --- bcol-api/requirements.txt | 49 --------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 bcol-api/requirements.txt diff --git a/bcol-api/requirements.txt b/bcol-api/requirements.txt deleted file mode 100644 index f9b2fad15..000000000 --- a/bcol-api/requirements.txt +++ /dev/null @@ -1,49 +0,0 @@ -Flask-Moment==1.0.5 -Flask-Script==2.0.6 -Flask==3.0.2 -Jinja2==3.1.3 -MarkupSafe==2.1.5 -Werkzeug==3.0.1 -aniso8601==9.0.1 -attrs==23.2.0 -blinker==1.7.0 -cachelib==0.12.0 -certifi==2024.2.2 -charset-normalizer==3.3.2 -click==8.1.7 -ecdsa==0.18.0 -flask-jwt-oidc==0.3.0 -flask-restx==1.3.0 -gunicorn==21.2.0 -idna==3.6 -importlib_resources==6.1.3 -isodate==0.6.1 -itsdangerous==2.1.2 -jaeger-client==4.8.0 -jsonschema==4.17.3 -lxml==5.1.0 -opentracing==2.4.0 -packaging==24.0 -platformdirs==4.2.0 -psycopg2-binary==2.9.9 -pyasn1-modules==0.3.0 -pyasn1==0.5.1 -pycountry==23.12.11 -pyrsistent==0.20.0 -python-dotenv==1.0.1 -python-jose==3.3.0 -python-ldap==3.4.4 -pytz==2024.1 -requests-file==2.0.0 -requests-toolbelt==1.0.0 -requests==2.32.2 -rsa==4.9 -sentry-sdk==1.41.0 -six==1.16.0 -threadloop==1.0.2 -thrift==0.16.0 -tornado==6.4 -urllib3==2.2.1 -zeep==4.2.1 --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -git+https://github.com/thorwolpert/flask-jwt-oidc.git From 95acc7f6d3675d02c4accc45cce56a384d7cf104 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 11:53:39 -0700 Subject: [PATCH 85/87] use latest poetry --- bcol-api/Dockerfile | 2 +- jobs/ftp-poller/Dockerfile | 2 +- jobs/payment-jobs/Dockerfile | 2 +- pay-api/Dockerfile | 2 +- pay-queue/Dockerfile | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bcol-api/Dockerfile b/bcol-api/Dockerfile index c849df874..7c8145bf7 100644 --- a/bcol-api/Dockerfile +++ b/bcol-api/Dockerfile @@ -32,7 +32,7 @@ ENV APP_ENV=${APP_ENV} \ PIP_DEFAULT_TIMEOUT=100 \ PIP_ROOT_USER_ACTION=ignore \ # poetry: - POETRY_VERSION=1.3.2 \ + POETRY_VERSION=1.8.3 \ POETRY_NO_INTERACTION=1 \ POETRY_VIRTUALENVS_CREATE=false \ POETRY_CACHE_DIR='/var/cache/pypoetry' \ diff --git a/jobs/ftp-poller/Dockerfile b/jobs/ftp-poller/Dockerfile index fd4eada0e..616ea93d0 100644 --- a/jobs/ftp-poller/Dockerfile +++ b/jobs/ftp-poller/Dockerfile @@ -54,7 +54,7 @@ ENV APP_ENV=${APP_ENV} \ PIP_DEFAULT_TIMEOUT=100 \ PIP_ROOT_USER_ACTION=ignore \ # poetry: - POETRY_VERSION=1.3.2 \ + POETRY_VERSION=1.8.3 \ POETRY_NO_INTERACTION=1 \ POETRY_VIRTUALENVS_CREATE=false \ POETRY_CACHE_DIR='/var/cache/pypoetry' \ diff --git a/jobs/payment-jobs/Dockerfile b/jobs/payment-jobs/Dockerfile index a2f8fd6d8..981cae2db 100644 --- a/jobs/payment-jobs/Dockerfile +++ b/jobs/payment-jobs/Dockerfile @@ -67,7 +67,7 @@ ENV APP_ENV=${APP_ENV} \ PIP_DEFAULT_TIMEOUT=100 \ PIP_ROOT_USER_ACTION=ignore \ # poetry: - POETRY_VERSION=1.3.2 \ + POETRY_VERSION=1.8.3 \ POETRY_NO_INTERACTION=1 \ POETRY_VIRTUALENVS_CREATE=false \ POETRY_CACHE_DIR='/var/cache/pypoetry' \ diff --git a/pay-api/Dockerfile b/pay-api/Dockerfile index 25fcc6c90..cf3e9d235 100644 --- a/pay-api/Dockerfile +++ b/pay-api/Dockerfile @@ -32,7 +32,7 @@ ENV APP_ENV=${APP_ENV} \ PIP_DEFAULT_TIMEOUT=100 \ PIP_ROOT_USER_ACTION=ignore \ # poetry: - POETRY_VERSION=1.3.2 \ + POETRY_VERSION=1.8.3 \ POETRY_NO_INTERACTION=1 \ POETRY_VIRTUALENVS_CREATE=false \ POETRY_CACHE_DIR='/var/cache/pypoetry' \ diff --git a/pay-queue/Dockerfile b/pay-queue/Dockerfile index c857284c1..e402d1323 100644 --- a/pay-queue/Dockerfile +++ b/pay-queue/Dockerfile @@ -31,7 +31,7 @@ ENV APP_ENV=${APP_ENV} \ PIP_DEFAULT_TIMEOUT=100 \ PIP_ROOT_USER_ACTION=ignore \ # poetry: - POETRY_VERSION=1.3.2 \ + POETRY_VERSION=1.8.3 \ POETRY_NO_INTERACTION=1 \ POETRY_VIRTUALENVS_CREATE=false \ POETRY_CACHE_DIR='/var/cache/pypoetry' \ From a79ded034b05133a8fa302cfe661b9c78c68770f Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 11:54:01 -0700 Subject: [PATCH 86/87] remove more requirements --- bcol-api/requirements/dev.txt | 25 ------------------------ bcol-api/requirements/prod.txt | 19 ------------------ bcol-api/requirements/repo-libraries.txt | 2 -- 3 files changed, 46 deletions(-) delete mode 100755 bcol-api/requirements/dev.txt delete mode 100755 bcol-api/requirements/prod.txt delete mode 100644 bcol-api/requirements/repo-libraries.txt diff --git a/bcol-api/requirements/dev.txt b/bcol-api/requirements/dev.txt deleted file mode 100755 index 9016fe016..000000000 --- a/bcol-api/requirements/dev.txt +++ /dev/null @@ -1,25 +0,0 @@ -# Everything the developer needs in addition to the production requirements --r prod.txt - -# Testing -pytest -pytest-mock -requests -pyhamcrest -pytest-cov - -# Lint and code style -flake8 -flake8-blind-except -flake8-debugger -flake8-docstrings -flake8-isort -flake8-quotes -pep8-naming -autopep8 -coverage -pylint -pylint-flask -pydocstyle -lovely-pytest-docker -isort diff --git a/bcol-api/requirements/prod.txt b/bcol-api/requirements/prod.txt deleted file mode 100755 index 4a8fe04dc..000000000 --- a/bcol-api/requirements/prod.txt +++ /dev/null @@ -1,19 +0,0 @@ -gunicorn -Flask -Flask-Script -Flask-Moment -flask-restx -flask-jwt-oidc -python-dotenv -psycopg2-binary -jsonschema==4.17.3 -requests -zeep -python-ldap -sentry-sdk[flask] -attrs -Werkzeug -jaeger-client -pycountry -itsdangerous -Jinja2 diff --git a/bcol-api/requirements/repo-libraries.txt b/bcol-api/requirements/repo-libraries.txt deleted file mode 100644 index 36fdc23c0..000000000 --- a/bcol-api/requirements/repo-libraries.txt +++ /dev/null @@ -1,2 +0,0 @@ --e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python -git+https://github.com/thorwolpert/flask-jwt-oidc.git From 29a69f65c7cfbe09df7bd9a799e9bbf343b9a2d1 Mon Sep 17 00:00:00 2001 From: Travis Semple Date: Fri, 31 May 2024 12:10:39 -0700 Subject: [PATCH 87/87] Put back gunicorn config, comment for GCP. --- pay-api/gunicorn_config.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pay-api/gunicorn_config.py b/pay-api/gunicorn_config.py index 5a05fdb3b..484760c33 100755 --- a/pay-api/gunicorn_config.py +++ b/pay-api/gunicorn_config.py @@ -18,12 +18,13 @@ import os # https://docs.gunicorn.org/en/stable/settings.html#workers -workers = int(os.environ.get('GUNICORN_PROCESSES', '1')) # gunicorn default - 1 +workers = int(os.environ.get('GUNICORN_PROCESSES', '3')) # gunicorn default - 1 worker_class = os.environ.get('GUNICORN_WORKER_CLASS', 'sync') # gunicorn default - sync worker_connections = int(os.environ.get('GUNICORN_WORKER_CONNECIONS', '1000')) # gunicorn default - 1000 -threads = int(os.environ.get('GUNICORN_THREADS', '8')) # gunicorn default - 1 -timeout = int(os.environ.get('GUNICORN_TIMEOUT', '0')) # gunicorn default - 30 +threads = int(os.environ.get('GUNICORN_THREADS', '1')) # gunicorn default - 1 +timeout = int(os.environ.get('GUNICORN_TIMEOUT', '100')) # gunicorn default - 30 keepalive = int(os.environ.get('GUNICORN_KEEPALIVE', '2')) # gunicorn default - 2 +# WHEN MIGRATING TO GCP - GUNICORN_THREADS = 8, GUNICORN_TIMEOUT = 0, GUNICORN_PROCESSES = 1 forwarded_allow_ips = '*' # pylint: disable=invalid-name