diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index c6a2f6b839..49963dcbfa 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,7 +2,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.10@sha256:ef9cc483a593c95 ARG KUBENS_VERSION="0.9.4" ARG OCTANT_VERSION="0.25.1" -ENV POETRY_VERSION="1.3.2" +ENV POETRY_VERSION="1.7.1" # Install packages RUN apt-get update \ diff --git a/.devcontainer/scripts/notify-dev-entrypoint.sh b/.devcontainer/scripts/notify-dev-entrypoint.sh index 22cb552d2d..e2f99ea29a 100755 --- a/.devcontainer/scripts/notify-dev-entrypoint.sh +++ b/.devcontainer/scripts/notify-dev-entrypoint.sh @@ -34,8 +34,10 @@ cd /workspace echo -e "fpath+=/.zfunc" >> ~/.zshrc echo -e "autoload -Uz compinit && compinit" -pip install poetry==${POETRY_VERSION} \ - && poetry --version +pip install poetry==${POETRY_VERSION} +export PATH=$PATH:/home/vscode/.local/bin/ +which poetry +poetry --version # Initialize poetry autocompletions mkdir ~/.zfunc diff --git a/.env.enc.aws b/.env.enc.aws deleted file mode 100644 index 5c496d4640..0000000000 Binary files a/.env.enc.aws and /dev/null differ diff --git a/.github/workflows/build_and_push_performance_test.yml b/.github/workflows/build_and_push_performance_test.yml index 9afa851ff6..326cec3389 100644 --- a/.github/workflows/build_and_push_performance_test.yml +++ b/.github/workflows/build_and_push_performance_test.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 + - uses: dorny/paths-filter@7267a8516b6f92bdb098633497bad573efdbf271 # v2.12.0 id: filter with: filters: | diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e9b4547ef3..7976f87fde 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,15 +27,15 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Initialize CodeQL - uses: github/codeql-action/init@1500a131381b66de0c52ac28abb13cd79f4b7ecc # v2.22.12 + uses: github/codeql-action/init@2f93e4319b2f04a2efc38fa7f78bd681bc3f7b2f # v2.23.2 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@1500a131381b66de0c52ac28abb13cd79f4b7ecc # v2.22.12 + uses: github/codeql-action/autobuild@2f93e4319b2f04a2efc38fa7f78bd681bc3f7b2f # v2.23.2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1500a131381b66de0c52ac28abb13cd79f4b7ecc # v2.22.12 + uses: github/codeql-action/analyze@2f93e4319b2f04a2efc38fa7f78bd681bc3f7b2f # v2.23.2 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index d499643c63..f612283a7c 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -25,7 +25,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@75cb7af1033cfb77c9fc7d8abc30420008f558f4 + uses: ossf/scorecard-action@0ae0fb3a2ca18a43d6dea9c07cfb9bd01d17eae1 with: results_file: ossf-results.json results_format: json diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index b689c1b236..8c52bdd979 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -16,7 +16,7 @@ jobs: python-version: '3.10' - name: Upgrade pip run: python -m pip install --upgrade pip - - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + - uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c # v3.3.3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7170ed1dce..8d5c023e27 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -25,7 +25,7 @@ jobs: python-version: '3.10' - name: Upgrade pip run: python -m pip install --upgrade pip - - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + - uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c # v3.3.3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} @@ -33,10 +33,10 @@ jobs: ${{ runner.os }}-pip- - name: Install poetry env: - POETRY_VERSION: "1.3.2" + POETRY_VERSION: "1.7.1" run: pip install poetry==${POETRY_VERSION} && poetry --version - name: Check poetry.lock aligns with pyproject.toml - run: poetry lock --check + run: poetry check --lock - name: Install requirements run: poetry install --with test - name: Run tests @@ -67,7 +67,7 @@ jobs: run: | cp -f .env.example .env - name: Checks for new endpoints against AWS WAF rules - uses: cds-snc/notification-utils/.github/actions/waffles@69b6eee0c715fa4c3fe9ac48e4ccd6e3d4c1e239 # 52.0.18 + uses: cds-snc/notification-utils/.github/actions/waffles@06a40db6286f525fe3551e029418458d33342592 # 52.1.0 with: app-loc: '/github/workspace' app-libs: '/github/workspace/env/site-packages' diff --git a/.gitignore b/.gitignore index 1951ac3868..08663fb12a 100644 --- a/.gitignore +++ b/.gitignore @@ -72,10 +72,9 @@ target/ # Mac *.DS_Store environment.sh -.envrc -.env -.env_* -.env.lambda +.env* +!.env.devcontainer +!.env.example celerybeat-schedule celerybeat-schedule.* diff --git a/Makefile b/Makefile index 0b1064d543..3f98e79566 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ freeze-requirements: .PHONY: test-requirements test-requirements: - poetry lock --check + poetry check --lock .PHONY: coverage coverage: venv ## Create coverage report diff --git a/app/celery/celery.py b/app/celery/celery.py index b1758f73c0..be5a5593aa 100644 --- a/app/celery/celery.py +++ b/app/celery/celery.py @@ -48,6 +48,7 @@ def init_app(self, app): "beat_schedule": app.config["CELERYBEAT_SCHEDULE"], "imports": app.config["CELERY_IMPORTS"], "task_serializer": app.config["CELERY_TASK_SERIALIZER"], + "enable_utc": app.config["CELERY_ENABLE_UTC"], "timezone": app.config["CELERY_TIMEZONE"], "broker_transport_options": app.config["BROKER_TRANSPORT_OPTIONS"], "task_queues": app.config["CELERY_QUEUES"], diff --git a/app/celery/tasks.py b/app/celery/tasks.py index 9dfc90ce54..df10c1db29 100644 --- a/app/celery/tasks.py +++ b/app/celery/tasks.py @@ -78,7 +78,6 @@ persist_notifications, send_notification_to_queue, ) -from app.notifications.validators import check_service_over_daily_message_limit from app.types import VerifiedNotification from app.utils import get_csv_max_rows, get_delivery_queue_for_template from app.v2.errors import ( @@ -300,8 +299,6 @@ def save_smss(self, service_id: Optional[str], signed_notifications: List[Signed current_app.logger.debug(f"Sending following sms notifications to AWS: {notification_id_queue.keys()}") for notification_obj in saved_notifications: try: - if not current_app.config["FF_EMAIL_DAILY_LIMIT"]: - check_service_over_daily_message_limit(notification_obj.key_type, service) queue = notification_id_queue.get(notification_obj.id) or get_delivery_queue_for_template(template) send_notification_to_queue( notification_obj, @@ -423,8 +420,6 @@ def try_to_send_notifications_to_queue(notification_id_queue, service, saved_not research_mode = service.research_mode # type: ignore for notification_obj in saved_notifications: try: - if not current_app.config["FF_EMAIL_DAILY_LIMIT"]: - check_service_over_daily_message_limit(notification_obj.key_type, service) queue = notification_id_queue.get(notification_obj.id) or get_delivery_queue_for_template(template) send_notification_to_queue( notification_obj, diff --git a/app/config.py b/app/config.py index 10ec7f5a6f..d08a88b854 100644 --- a/app/config.py +++ b/app/config.py @@ -359,7 +359,7 @@ class Config(object): "queue_name_prefix": NOTIFICATION_QUEUE_PREFIX, } CELERY_ENABLE_UTC = True - CELERY_TIMEZONE = os.getenv("TIMEZONE", "America/Toronto") + CELERY_TIMEZONE = os.getenv("TIMEZONE", "UTC") CELERY_ACCEPT_CONTENT = ["json"] CELERY_TASK_SERIALIZER = "json" CELERY_IMPORTS = ( @@ -438,87 +438,54 @@ class Config(object): # app/celery/nightly_tasks.py "timeout-sending-notifications": { "task": "timeout-sending-notifications", - "schedule": crontab(hour=0, minute=5), + "schedule": crontab(hour=5, minute=5), # 00:05 EST in UTC "options": {"queue": QueueNames.PERIODIC}, }, "create-nightly-billing": { "task": "create-nightly-billing", - "schedule": crontab(hour=0, minute=15), + "schedule": crontab(hour=5, minute=15), # 00:15 EST in UTC "options": {"queue": QueueNames.REPORTING}, }, "create-nightly-notification-status": { "task": "create-nightly-notification-status", - "schedule": crontab(hour=0, minute=30), # after 'timeout-sending-notifications' + "schedule": crontab(hour=5, minute=30), # 00:30 EST in UTC, after 'timeout-sending-notifications' "options": {"queue": QueueNames.REPORTING}, }, "delete-sms-notifications": { "task": "delete-sms-notifications", - "schedule": crontab(hour=4, minute=15), # after 'create-nightly-notification-status' + "schedule": crontab(hour=9, minute=15), # 4:15 EST in UTC, after 'create-nightly-notification-status' "options": {"queue": QueueNames.PERIODIC}, }, "delete-email-notifications": { "task": "delete-email-notifications", - "schedule": crontab(hour=4, minute=30), # after 'create-nightly-notification-status' + "schedule": crontab(hour=9, minute=30), # 4:30 EST in UTC, after 'create-nightly-notification-status' "options": {"queue": QueueNames.PERIODIC}, }, "delete-letter-notifications": { "task": "delete-letter-notifications", - "schedule": crontab(hour=4, minute=45), # after 'create-nightly-notification-status' + "schedule": crontab(hour=9, minute=45), # 4:45 EST in UTC, after 'create-nightly-notification-status' "options": {"queue": QueueNames.PERIODIC}, }, "delete-inbound-sms": { "task": "delete-inbound-sms", - "schedule": crontab(hour=1, minute=40), + "schedule": crontab(hour=6, minute=40), # 1:40 EST in UTC "options": {"queue": QueueNames.PERIODIC}, }, "send-daily-performance-platform-stats": { "task": "send-daily-performance-platform-stats", - "schedule": crontab(hour=2, minute=0), + "schedule": crontab(hour=7, minute=0), # 2:00 EST in UTC "options": {"queue": QueueNames.PERIODIC}, }, "remove_transformed_dvla_files": { "task": "remove_transformed_dvla_files", - "schedule": crontab(hour=3, minute=40), + "schedule": crontab(hour=8, minute=40), # 3:40 EST in UTC "options": {"queue": QueueNames.PERIODIC}, }, "remove_sms_email_jobs": { "task": "remove_sms_email_jobs", - "schedule": crontab(hour=4, minute=0), + "schedule": crontab(hour=9, minute=0), # 4:00 EST in UTC "options": {"queue": QueueNames.PERIODIC}, }, - # 'remove_letter_jobs': { - # 'task': 'remove_letter_jobs', - # 'schedule': crontab(hour=4, minute=20), - # since we mark jobs as archived - # 'options': {'queue': QueueNames.PERIODIC}, - # }, - # 'check-templated-letter-state': { - # 'task': 'check-templated-letter-state', - # 'schedule': crontab(day_of_week='mon-fri', hour=9, minute=0), - # 'options': {'queue': QueueNames.PERIODIC} - # }, - # 'check-precompiled-letter-state': { - # 'task': 'check-precompiled-letter-state', - # 'schedule': crontab(day_of_week='mon-fri', hour='9,15', minute=0), - # 'options': {'queue': QueueNames.PERIODIC} - # }, - # 'raise-alert-if-letter-notifications-still-sending': { - # 'task': 'raise-alert-if-letter-notifications-still-sending', - # 'schedule': crontab(hour=16, minute=30), - # 'options': {'queue': QueueNames.PERIODIC} - # }, - # The collate-letter-pdf does assume it is called in an hour that BST does not make a - # difference to the truncate date which translates to the filename to process - # 'collate-letter-pdfs-for-day': { - # 'task': 'collate-letter-pdfs-for-day', - # 'schedule': crontab(hour=17, minute=50), - # 'options': {'queue': QueueNames.PERIODIC} - # }, - # 'raise-alert-if-no-letter-ack-file': { - # 'task': 'raise-alert-if-no-letter-ack-file', - # 'schedule': crontab(hour=23, minute=00), - # 'options': {'queue': QueueNames.PERIODIC} - # }, } CELERY_QUEUES: List[Any] = [] CELERY_DELIVER_SMS_RATE_LIMIT = os.getenv("CELERY_DELIVER_SMS_RATE_LIMIT", "1/s") @@ -595,8 +562,6 @@ class Config(object): # Feature flag to enable custom retry policies such as lowering retry period for certain priority lanes. FF_CELERY_CUSTOM_TASK_PARAMS = env.bool("FF_CELERY_CUSTOM_TASK_PARAMS", True) FF_CLOUDWATCH_METRICS_ENABLED = env.bool("FF_CLOUDWATCH_METRICS_ENABLED", False) - # Feature flags for email_daily_limit - FF_EMAIL_DAILY_LIMIT = env.bool("FF_EMAIL_DAILY_LIMIT", False) FF_SALESFORCE_CONTACT = env.bool("FF_SALESFORCE_CONTACT", False) # SRE Tools auth keys @@ -704,8 +669,6 @@ class Test(Development): CRM_ORG_LIST_URL = "https://test-url.com" FAILED_LOGIN_LIMIT = 0 - FF_EMAIL_DAILY_LIMIT = False - class Production(Config): NOTIFY_EMAIL_DOMAIN = os.getenv("NOTIFY_EMAIL_DOMAIN", "notification.canada.ca") diff --git a/app/dao/api_key_dao.py b/app/dao/api_key_dao.py index 679467f4f4..f6a707bf76 100644 --- a/app/dao/api_key_dao.py +++ b/app/dao/api_key_dao.py @@ -68,6 +68,13 @@ def expire_api_key(service_id, api_key_id): db.session.add(api_key) +@transactional +def update_last_used_api_key(api_key_id, last_used=None) -> None: + api_key = ApiKey.query.filter_by(id=api_key_id).one() + api_key.last_used_timestamp = last_used if last_used else datetime.utcnow() + db.session.add(api_key) + + @transactional @version_class(ApiKey) def update_compromised_api_key_info(service_id, api_key_id, compromised_info): diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index bf886fbfd6..8fdb52ec70 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -1,7 +1,6 @@ from datetime import datetime, time, timedelta from flask import current_app -from notifications_utils.timezones import convert_local_timezone_to_utc from sqlalchemy import Date, case, func from sqlalchemy.dialects.postgresql import insert from sqlalchemy.sql.expression import extract, literal @@ -39,8 +38,8 @@ def fetch_notification_status_for_day(process_day, service_id=None): - start_date = convert_local_timezone_to_utc(datetime.combine(process_day, time.min)) - end_date = convert_local_timezone_to_utc(datetime.combine(process_day + timedelta(days=1), time.min)) + start_date = datetime.combine(process_day, time.min) + end_date = datetime.combine(process_day + timedelta(days=1), time.min) # use notification_history if process day is older than 7 days # this is useful if we need to rebuild the ft_billing table for a date older than 7 days ago. current_app.logger.info("Fetch ft_notification_status for {} to {}".format(start_date, end_date)) @@ -239,13 +238,19 @@ def fetch_notification_status_for_service_for_day(bst_day, service_id): def fetch_notification_status_for_service_for_today_and_7_previous_days(service_id, by_template=False, limit_days=7): - ft_start_date = utc_midnight_n_days_ago(limit_days) + if limit_days == 1: + ft_start_date = utc_midnight_n_days_ago(limit_days - 1) + # For daily stats, service limits reset at 12:00am UTC each night, so we need to fetch the data from 12:00 UTC to now + start = utc_midnight_n_days_ago(0) + end = datetime.utcnow() + else: + ft_start_date = utc_midnight_n_days_ago(limit_days) - # The nightly task that populates ft_notification_status counts collects notifications from - # 5AM the day before to 5AM of the current day. So we need to match that timeframe when - # we fetch notifications for the current day. - start = (tz_aware_midnight_n_days_ago(1) + timedelta(hours=5)).replace(minute=0, second=0, microsecond=0) - end = (tz_aware_midnight_n_days_ago(0) + timedelta(hours=5)).replace(minute=0, second=0, microsecond=0) + # The nightly task that populates ft_notification_status counts collects notifications from + # 5AM the day before to 5AM of the current day. So we need to match that timeframe when + # we fetch notifications for the current day. + start = (tz_aware_midnight_n_days_ago(1) + timedelta(hours=5)).replace(minute=0, second=0, microsecond=0) + end = (tz_aware_midnight_n_days_ago(0) + timedelta(hours=5)).replace(minute=0, second=0, microsecond=0) stats_for_7_days = db.session.query( FactNotificationStatus.notification_type.label("notification_type"), @@ -336,24 +341,21 @@ def get_total_notifications_sent_for_api_key(api_key_id): def get_last_send_for_api_key(api_key_id): """ + SELECT last_used_timestamp as last_notification_created + FROM api_keys + WHERE id = 'api_key_id'; + + If last_used_timestamp is null, then check notifications table/ or notification_history. SELECT max(created_at) as last_notification_created FROM notifications WHERE api_key_id = 'api_key_id' GROUP BY api_key_id; """ - notification_table = ( - db.session.query(func.max(Notification.created_at).label("last_notification_created")) - .filter(Notification.api_key_id == api_key_id) - .all() + # Fetch last_used_timestamp from api_keys table + api_key_table = ( + db.session.query(ApiKey.last_used_timestamp.label("last_notification_created")).filter(ApiKey.id == api_key_id).all() ) - if not notification_table[0][0]: - notification_table = ( - db.session.query(func.max(NotificationHistory.created_at).label("last_notification_created")) - .filter(NotificationHistory.api_key_id == api_key_id) - .all() - ) - notification_table = [] if notification_table[0][0] is None else notification_table - return notification_table + return [] if api_key_table[0][0] is None else api_key_table def get_api_key_ranked_by_notifications_created(n_days_back): diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index a39af634f1..9e20bca476 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -27,6 +27,7 @@ from app import create_uuid, db, signer_personalisation from app.aws.s3 import get_s3_bucket_objects, remove_s3_object from app.dao.dao_utils import transactional +from app.dao.date_util import utc_midnight_n_days_ago from app.errors import InvalidRequest from app.letters.utils import LETTERS_PDF_FILE_LOCATION_STRUCTURE from app.models import ( @@ -51,11 +52,7 @@ Service, ServiceDataRetention, ) -from app.utils import ( - escape_special_characters, - get_local_timezone_midnight_in_utc, - midnight_n_days_ago, -) +from app.utils import escape_special_characters, get_local_timezone_midnight_in_utc @transactional @@ -335,7 +332,7 @@ def get_notifications_for_service( filters = [Notification.service_id == service_id] if limit_days is not None: - filters.append(Notification.created_at >= midnight_n_days_ago(limit_days)) + filters.append(Notification.created_at >= utc_midnight_n_days_ago(limit_days)) if older_than is not None: older_than_created_at = db.session.query(Notification.created_at).filter(Notification.id == older_than).as_scalar() diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index 80d1f25c35..082a2566a1 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -23,6 +23,7 @@ CROWN_ORGANISATION_TYPES, EMAIL_TYPE, INTERNATIONAL_SMS_TYPE, + JOB_STATUS_SCHEDULED, KEY_TYPE_TEST, NHS_ORGANISATION_TYPES, NON_CROWN_ORGANISATION_TYPES, @@ -424,20 +425,24 @@ def dao_fetch_todays_stats_for_service(service_id): def fetch_todays_total_message_count(service_id): midnight = get_midnight(datetime.now(tz=pytz.utc)) + scheduled = ( + db.session.query(func.coalesce(func.sum(Job.notification_count), 0).label("count")).filter( + Job.service_id == service_id, + Job.job_status == JOB_STATUS_SCHEDULED, + Job.scheduled_for >= midnight, + Job.scheduled_for < midnight + timedelta(days=1), + ) + ).first() + result = ( - db.session.query(func.count(Notification.id).label("count")) - .filter( + db.session.query(func.coalesce(func.count(Notification.id), 0).label("count")).filter( Notification.service_id == service_id, Notification.key_type != KEY_TYPE_TEST, - Notification.created_at > midnight, + Notification.created_at >= midnight, ) - .group_by( - Notification.notification_type, - Notification.status, - ) - .first() - ) - return 0 if result is None else result.count + ).first() + + return result.count + scheduled.count def fetch_todays_total_sms_count(service_id): @@ -461,17 +466,25 @@ def fetch_service_email_limit(service_id: uuid.UUID) -> int: def fetch_todays_total_email_count(service_id: uuid.UUID) -> int: midnight = get_midnight(datetime.now(tz=pytz.utc)) + scheduled = ( + db.session.query(func.coalesce(func.sum(Job.notification_count), 0).label("total_scheduled_notifications")).filter( + Job.service_id == service_id, + Job.job_status == JOB_STATUS_SCHEDULED, + Job.scheduled_for > midnight, + Job.scheduled_for < midnight + timedelta(hours=23, minutes=59, seconds=59), + ) + ).first() + result = ( - db.session.query(func.count(Notification.id).label("total_email_notifications")) - .filter( + db.session.query(func.coalesce(func.count(Notification.id), 0).label("total_email_notifications")).filter( Notification.service_id == service_id, Notification.key_type != KEY_TYPE_TEST, Notification.created_at > midnight, Notification.notification_type == "email", ) - .first() - ) - return 0 if result is None else result.total_email_notifications + ).first() + + return result.total_email_notifications + scheduled.total_scheduled_notifications def _stats_for_service_query(service_id): diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index bc6bbb67bc..33590c7667 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -1,7 +1,6 @@ import base64 import os import re -import urllib.request from datetime import datetime from typing import Dict from uuid import UUID @@ -17,6 +16,8 @@ SMSMessageTemplate, ) from unidecode import unidecode +from urllib3 import PoolManager +from urllib3.util import Retry from app import bounce_rate_client, clients, document_download_client, statsd_client from app.celery.research_mode_tasks import send_email_response, send_sms_response @@ -231,27 +232,29 @@ def send_email_to_provider(notification: Notification): check_file_url(personalisation_data[key]["document"], notification.id) sending_method = personalisation_data[key]["document"].get("sending_method") direct_file_url = personalisation_data[key]["document"]["direct_file_url"] + filename = personalisation_data[key]["document"].get("filename") + mime_type = personalisation_data[key]["document"].get("mime_type") document_id = personalisation_data[key]["document"]["id"] scan_verdict_response = document_download_client.check_scan_verdict(service.id, document_id, sending_method) check_for_malware_errors(scan_verdict_response.status_code, notification) current_app.logger.info(f"scan_verdict for document_id {document_id} is {scan_verdict_response.json()}") if sending_method == "attach": try: - req = urllib.request.Request(direct_file_url) - with urllib.request.urlopen(req) as response: - buffer = response.read() - filename = personalisation_data[key]["document"].get("filename") - mime_type = personalisation_data[key]["document"].get("mime_type") - attachments.append( - { - "name": filename, - "data": buffer, - "mime_type": mime_type, - } - ) + retries = Retry(total=5) + http = PoolManager(retries=retries) + + response = http.request("GET", url=direct_file_url) + attachments.append( + { + "name": filename, + "data": response.data, + "mime_type": mime_type, + } + ) except Exception as e: current_app.logger.error(f"Could not download and attach {direct_file_url}\nException: {e}") - del personalisation_data[key] + del personalisation_data[key] + else: personalisation_data[key] = personalisation_data[key]["document"]["url"] diff --git a/app/email_limit_utils.py b/app/email_limit_utils.py index 7086d5c6f0..0fb8bf3364 100644 --- a/app/email_limit_utils.py +++ b/app/email_limit_utils.py @@ -3,6 +3,7 @@ from flask import current_app from notifications_utils.clients.redis import email_daily_count_cache_key +from notifications_utils.decorators import requires_feature from app import redis_store from app.dao.services_dao import fetch_todays_total_email_count @@ -20,9 +21,15 @@ def fetch_todays_email_count(service_id: UUID) -> int: return int(total_email_count) +@requires_feature("REDIS_ENABLED") def increment_todays_email_count(service_id: UUID, increment_by: int) -> None: - if not current_app.config["REDIS_ENABLED"]: - return fetch_todays_email_count(service_id) # to make sure it's set in redis cache_key = email_daily_count_cache_key(service_id) redis_store.incrby(cache_key, increment_by) + + +@requires_feature("REDIS_ENABLED") +def decrement_todays_email_count(service_id: UUID, decrement_by: int) -> None: + fetch_todays_email_count(service_id) + cache_key = email_daily_count_cache_key(service_id) + redis_store.decrby(cache_key, decrement_by) diff --git a/app/job/rest.py b/app/job/rest.py index 3c443ec4ba..950f1554c9 100644 --- a/app/job/rest.py +++ b/app/job/rest.py @@ -1,3 +1,5 @@ +from datetime import datetime + import dateutil from flask import Blueprint, current_app, jsonify, request from notifications_utils.recipients import RecipientCSV @@ -20,6 +22,7 @@ from app.dao.notifications_dao import get_notifications_for_job from app.dao.services_dao import dao_fetch_service_by_id from app.dao.templates_dao import dao_get_template_by_id +from app.email_limit_utils import decrement_todays_email_count from app.errors import InvalidRequest, register_errors from app.models import ( EMAIL_TYPE, @@ -67,7 +70,7 @@ def cancel_job(service_id, job_id): job = dao_get_future_scheduled_job_by_id_and_service_id(job_id, service_id) job.job_status = JOB_STATUS_CANCELLED dao_update_job(job) - + decrement_todays_email_count(service_id, job.notification_count) return get_job_by_service_and_job_id(service_id, job_id) @@ -137,15 +140,23 @@ def create_job(service_id): raise InvalidRequest("Create job is not allowed: service is inactive ", 403) data = request.get_json() - data.update({"service": service_id}) + try: data.update(**get_job_metadata_from_s3(service_id, data["id"])) except KeyError: raise InvalidRequest({"id": ["Missing data for required field."]}, status_code=400) + if data.get("valid") != "True": + raise InvalidRequest("File is not valid, can't create job", 400) + data["template"] = data.pop("template_id") + template = dao_get_template_by_id(data["template"]) + template_errors = unarchived_template_schema.validate({"archived": template.archived}) + + if template_errors: + raise InvalidRequest(template_errors, status_code=400) job = get_job_from_s3(service_id, data["id"]) recipient_csv = RecipientCSV( @@ -170,22 +181,14 @@ def create_job(service_id): if not is_test_notification: check_sms_daily_limit(service, len(recipient_csv)) + increment_sms_daily_count_send_warnings_if_needed(service, len(recipient_csv)) - if template.template_type == EMAIL_TYPE: + elif template.template_type == EMAIL_TYPE: check_email_daily_limit(service, len(list(recipient_csv.get_rows()))) + scheduled_for = datetime.fromisoformat(data.get("scheduled_for")) if data.get("scheduled_for") else None - if data.get("valid") != "True": - raise InvalidRequest("File is not valid, can't create job", 400) - - errors = unarchived_template_schema.validate({"archived": template.archived}) - - if errors: - raise InvalidRequest(errors, status_code=400) - - if template.template_type == SMS_TYPE and not is_test_notification: - increment_sms_daily_count_send_warnings_if_needed(service, len(recipient_csv)) - elif template.template_type == EMAIL_TYPE: - increment_email_daily_count_send_warnings_if_needed(service, len(list(recipient_csv.get_rows()))) + if scheduled_for is None or not scheduled_for.date() > datetime.today().date(): + increment_email_daily_count_send_warnings_if_needed(service, len(list(recipient_csv.get_rows()))) data.update({"template_version": template.version}) diff --git a/app/models.py b/app/models.py index fd640e1473..704ccf798a 100644 --- a/app/models.py +++ b/app/models.py @@ -918,6 +918,7 @@ class ApiKey(BaseModel, Versioned): created_by = db.relationship("User") created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey("users.id"), index=True, nullable=False) compromised_key_info = db.Column(JSONB(none_as_null=True), nullable=True, default={}) + last_used_timestamp = db.Column(db.DateTime, index=False, unique=False, nullable=True, default=None) __table_args__ = ( Index( diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index 56a2dc98d3..d3dc3304c8 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -15,6 +15,7 @@ from app.celery import provider_tasks from app.celery.letters_pdf_tasks import create_letters_pdf from app.config import QueueNames +from app.dao.api_key_dao import update_last_used_api_key from app.dao.notifications_dao import ( bulk_insert_notifications, dao_create_notification, @@ -133,6 +134,8 @@ def persist_notification( if redis_store.get(redis.daily_limit_cache_key(service.id)): redis_store.incr(redis.daily_limit_cache_key(service.id)) current_app.logger.info("{} {} created at {}".format(notification_type, notification_id, notification_created_at)) + if api_key_id: + update_last_used_api_key(api_key_id, notification_created_at) return notification @@ -298,6 +301,7 @@ def persist_notifications(notifications: List[VerifiedNotification]) -> List[Not """ lofnotifications = [] + api_key_last_used = None for notification in notifications: notification_created_at = notification.get("created_at") or datetime.utcnow() @@ -357,7 +361,15 @@ def persist_notifications(notifications: List[VerifiedNotification]) -> List[Not notification.get("notification_created_at"), # type: ignore ) ) + # If the bulk message is sent using an api key, we want to keep track of the last time the api key was used + # We will only update the api key once + api_key_id = notification.get("api_key_id") + if api_key_id: + api_key_last_used = datetime.utcnow() + if api_key_last_used: + update_last_used_api_key(api_key_id, api_key_last_used) bulk_insert_notifications(lofnotifications) + return lofnotifications diff --git a/app/notifications/rest.py b/app/notifications/rest.py index 3a4f80c9d7..e3ec327df6 100644 --- a/app/notifications/rest.py +++ b/app/notifications/rest.py @@ -10,6 +10,7 @@ EMAIL_TYPE, INTERNATIONAL_SMS_TYPE, KEY_TYPE_TEAM, + KEY_TYPE_TEST, LETTER_TYPE, SMS_TYPE, NotificationType, @@ -21,6 +22,7 @@ simulated_recipient, ) from app.notifications.validators import ( + check_email_daily_limit, check_rate_limiting, check_template_is_active, check_template_is_for_notification_type, @@ -102,12 +104,17 @@ def send_notification(notification_type: NotificationType): raise InvalidRequest(errors, status_code=400) current_app.logger.info(f"POST to V1 API: send_notification, service_id: {authenticated_service.id}") + check_rate_limiting(authenticated_service, api_user) template = templates_dao.dao_get_template_by_id_and_service_id( template_id=notification_form["template"], service_id=authenticated_service.id ) + simulated = simulated_recipient(notification_form["to"], notification_type) + if not simulated != api_user.key_type == KEY_TYPE_TEST and notification_type == EMAIL_TYPE: + check_email_daily_limit(authenticated_service, 1) + check_template_is_for_notification_type(notification_type, template.template_type) check_template_is_active(template) @@ -124,7 +131,6 @@ def send_notification(notification_type: NotificationType): _service_can_send_internationally(authenticated_service, notification_form["to"]) # Do not persist or send notification to the queue if it is a simulated recipient - simulated = simulated_recipient(notification_form["to"], notification_type) notification_model = persist_notification( template_id=template.id, template_version=template.version, diff --git a/app/notifications/validators.py b/app/notifications/validators.py index 3aee7486d6..a144c22229 100644 --- a/app/notifications/validators.py +++ b/app/notifications/validators.py @@ -227,17 +227,12 @@ def increment_sms_daily_count_send_warnings_if_needed(service: Service, requeste def increment_email_daily_count_send_warnings_if_needed(service: Service, requested_email=0) -> None: - if not current_app.config["FF_EMAIL_DAILY_LIMIT"]: - return - increment_todays_email_count(service.id, requested_email) send_warning_email_limit_emails_if_needed(service) def check_rate_limiting(service: Service, api_key: ApiKey): check_service_over_api_rate_limit_and_update_rate(service, api_key) - if not current_app.config["FF_EMAIL_DAILY_LIMIT"]: - check_service_over_daily_message_limit(api_key.key_type, service) def warn_about_daily_message_limit(service: Service, messages_sent): diff --git a/app/service/rest.py b/app/service/rest.py index d3c790d841..8c198b0151 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -342,9 +342,7 @@ def update_service(service_id): def _warn_service_users_about_message_limit_changed(service_id, data): send_notification_to_service_users( service_id=service_id, - template_id=current_app.config["DAILY_EMAIL_LIMIT_UPDATED_TEMPLATE_ID"] - if current_app.config["FF_EMAIL_DAILY_LIMIT"] - else current_app.config["DAILY_LIMIT_UPDATED_TEMPLATE_ID"], + template_id=current_app.config["DAILY_EMAIL_LIMIT_UPDATED_TEMPLATE_ID"], personalisation={ "service_name": data["name"], "message_limit_en": "{:,}".format(data["message_limit"]), diff --git a/app/service/send_notification.py b/app/service/send_notification.py index 813ce2f138..77cd1313e3 100644 --- a/app/service/send_notification.py +++ b/app/service/send_notification.py @@ -67,13 +67,11 @@ def send_one_off_notification(service_id, post_data): _, template_with_content = validate_template(template.id, personalisation, service, template.template_type) - if not current_app.config["FF_EMAIL_DAILY_LIMIT"]: - check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) if template.template_type == SMS_TYPE: is_test_notification = simulated_recipient(post_data["to"], template.template_type) if not is_test_notification: check_sms_daily_limit(service, 1) - elif template.template_type == EMAIL_TYPE and current_app.config["FF_EMAIL_DAILY_LIMIT"]: + elif template.template_type == EMAIL_TYPE: check_email_daily_limit(service, 1) # 1 email validate_and_format_recipient( @@ -86,13 +84,11 @@ def send_one_off_notification(service_id, post_data): validate_created_by(service, post_data["created_by"]) - if not current_app.config["FF_EMAIL_DAILY_LIMIT"]: - pass # will remove this soon, don't bother refactoring if template.template_type == SMS_TYPE: is_test_notification = simulated_recipient(post_data["to"], template.template_type) if not is_test_notification: increment_sms_daily_count_send_warnings_if_needed(service, 1) - elif template.template_type == EMAIL_TYPE and current_app.config["FF_EMAIL_DAILY_LIMIT"]: + elif template.template_type == EMAIL_TYPE: increment_email_daily_count_send_warnings_if_needed(service, 1) # 1 email sender_id = post_data.get("sender_id", None) diff --git a/app/v2/notifications/post_notifications.py b/app/v2/notifications/post_notifications.py index d9d333c0e0..9a1640b9a2 100644 --- a/app/v2/notifications/post_notifications.py +++ b/app/v2/notifications/post_notifications.py @@ -223,7 +223,10 @@ def post_bulk(): if template.template_type == EMAIL_TYPE and api_user.key_type != KEY_TYPE_TEST: check_email_daily_limit(authenticated_service, len(list(recipient_csv.get_rows()))) - increment_email_daily_count_send_warnings_if_needed(authenticated_service, len(list(recipient_csv.get_rows()))) + scheduled_for = datetime.fromisoformat(form.get("scheduled_for")) if form.get("scheduled_for") else None + + if scheduled_for is None or not scheduled_for.date() > datetime.today().date(): + increment_email_daily_count_send_warnings_if_needed(authenticated_service, len(list(recipient_csv.get_rows()))) if template.template_type == SMS_TYPE: # calculate the number of simulated recipients diff --git a/ci/Dockerfile b/ci/Dockerfile index 3b6c11f11f..144e6bb07f 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -1,10 +1,9 @@ FROM python:3.10-alpine3.16@sha256:afe68972cc00883d70b3760ee0ffbb7375cf09706c122dda7063ffe64c5be21b ENV PYTHONDONTWRITEBYTECODE 1 -ENV POETRY_VERSION "1.3.2" ENV APP_VENV="/app/.venv" ENV POETRY_HOME="/opt/poetry" -ENV POETRY_VERSION="1.3.2" +ENV POETRY_VERSION="1.7.1" ENV POETRY_VIRTUALENVS_CREATE="false" ENV PATH="${APP_VENV}/bin:${POETRY_HOME}/bin:$PATH" diff --git a/ci/Dockerfile.lambda b/ci/Dockerfile.lambda index bf18db168a..c827b08c24 100644 --- a/ci/Dockerfile.lambda +++ b/ci/Dockerfile.lambda @@ -5,7 +5,7 @@ ENV PYTHONDONTWRITEBYTECODE 1 ENV TASK_ROOT /app ENV APP_VENV="${TASK_ROOT}/.venv" ENV POETRY_HOME="/opt/poetry" -ENV POETRY_VERSION="1.3.2" +ENV POETRY_VERSION="1.7.1" ENV POETRY_VIRTUALENVS_CREATE="false" ENV PATH="${APP_VENV}/bin:${POETRY_HOME}/bin:$PATH" diff --git a/ci/Dockerfile.test b/ci/Dockerfile.test index ccdc2d7208..e068dfbfd5 100644 --- a/ci/Dockerfile.test +++ b/ci/Dockerfile.test @@ -3,10 +3,10 @@ FROM python:3.10-alpine@sha256:860f632e67178d9e90c7dfa9844a5e02098220bff5716d3c2fe1870325f00853 ENV PYTHONDONTWRITEBYTECODE 1 -ENV POETRY_VERSION "1.3.2" +ENV POETRY_VERSION "1.7.1" ARG APP_VENV="/app/.venv" ARG POETRY_HOME="/opt/poetry" -ARG POETRY_VERSION="1.3.2" +ARG POETRY_VERSION="1.7.1" ARG POETRY_VIRTUALENVS_CREATE="false" ENV PATH="${APP_VENV}/bin:${POETRY_HOME}/bin:$PATH" diff --git a/local/Dockerfile b/local/Dockerfile index 11444b22cf..f4ea41376c 100644 --- a/local/Dockerfile +++ b/local/Dockerfile @@ -1,7 +1,7 @@ FROM python:3.10-alpine@sha256:860f632e67178d9e90c7dfa9844a5e02098220bff5716d3c2fe1870325f00853 ENV PYTHONDONTWRITEBYTECODE 1 -ENV POETRY_VERSION "1.3.2" +ENV POETRY_VERSION "1.7.1" RUN apk add --no-cache bash build-base git gcc musl-dev postgresql-dev g++ make libffi-dev libmagic libcurl curl-dev && rm -rf /var/cache/apk/* diff --git a/migrations/versions/0443_add_apikey_last_used_column.py b/migrations/versions/0443_add_apikey_last_used_column.py new file mode 100644 index 0000000000..23d532d983 --- /dev/null +++ b/migrations/versions/0443_add_apikey_last_used_column.py @@ -0,0 +1,22 @@ +""" +Revision ID: 0443_add_apikey_last_used_column +Revises: 0442_add_heartbeat_templates +Create Date: 2022-09-21 00:00:00 +""" +from datetime import datetime + +import sqlalchemy as sa +from alembic import op + +revision = "0443_add_apikey_last_used_column" +down_revision = "0442_add_heartbeat_templates" + + +def upgrade(): + op.add_column("api_keys", sa.Column("last_used_timestamp", sa.DateTime(), nullable=True)) + op.add_column("api_keys_history", sa.Column("last_used_timestamp", sa.DateTime(), nullable=True)) + + +def downgrade(): + op.drop_column("api_keys", "last_used_timestamp") + op.drop_column("api_keys_history", "last_used_timestamp") diff --git a/migrations/versions/0444_add_index_n_history2.py b/migrations/versions/0444_add_index_n_history2.py new file mode 100644 index 0000000000..2b3604d984 --- /dev/null +++ b/migrations/versions/0444_add_index_n_history2.py @@ -0,0 +1,40 @@ +""" + +Revision ID: 0439_add_index_n_history +Revises: 0438_sms_templates_msgs_left +Create Date: 2023-10-05 00:00:00 + +""" +from datetime import datetime + +from alembic import op + +revision = "0444_add_index_n_history2" +down_revision = "0443_add_apikey_last_used_column" + + +def index_exists(name): + connection = op.get_bind() + result = connection.execute( + "SELECT exists(SELECT 1 from pg_indexes where indexname = '{}') as ix_exists;".format(name) + ).first() + return result.ix_exists + + +# option 1 +def upgrade(): + op.execute("COMMIT") + if not index_exists("ix_notification_history_api_key_id_created"): + op.create_index( + op.f("ix_notification_history_api_key_id_created"), + "notification_history", + ["api_key_id", "created_at"], + postgresql_concurrently=True, + ) + + +def downgrade(): + op.execute("COMMIT") + op.drop_index( + op.f("ix_notification_history_api_key_id_created"), table_name="notification_history", postgresql_concurrently=True + ) diff --git a/poetry.lock b/poetry.lock index 0711b8deed..cd2b5ca1ba 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 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 = "aiohttp" version = "3.9.1" description = "Async http client/server framework (asyncio)" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -101,7 +100,6 @@ speedups = ["Brotli", "aiodns", "brotlicffi"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -116,7 +114,6 @@ frozenlist = ">=1.1.0" name = "alembic" version = "1.12.1" description = "A database migration tool for SQLAlchemy." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -136,7 +133,6 @@ tz = ["python-dateutil"] name = "amqp" version = "5.2.0" description = "Low-level AMQP client for Python (fork of amqplib)." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -151,7 +147,6 @@ vine = ">=5.0.0,<6.0.0" name = "annotated-types" version = "0.6.0" description = "Reusable constraint types to use with typing.Annotated" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -163,7 +158,6 @@ files = [ name = "apig-wsgi" version = "2.18.0" description = "Wrap a WSGI application in an AWS Lambda handler function for running on API Gateway or an ALB." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -175,7 +169,6 @@ files = [ name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -187,7 +180,6 @@ files = [ name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -206,7 +198,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "aws-embedded-metrics" version = "1.0.8" description = "AWS Embedded Metrics Package" -category = "main" optional = false python-versions = "*" files = [ @@ -219,18 +210,17 @@ aiohttp = "*" [[package]] name = "awscli" -version = "1.32.19" +version = "1.32.25" description = "Universal Command Line Environment for AWS." -category = "main" optional = false python-versions = ">= 3.8" files = [ - {file = "awscli-1.32.19-py3-none-any.whl", hash = "sha256:1ce62c1db13cd20c876ebf497b6b4c3bc66697d49d21680b7ea88c6704809d90"}, - {file = "awscli-1.32.19.tar.gz", hash = "sha256:c4255f546cfa3bea233efe78972d2ef173654f615dae6848b44ea0b52ffd94dd"}, + {file = "awscli-1.32.25-py3-none-any.whl", hash = "sha256:eea617961175e8bd1bd3aeda706948c89dc353fc934e81c156fe1ea484ca7a31"}, + {file = "awscli-1.32.25.tar.gz", hash = "sha256:091bbdb852b984d81fb5d8bf00100edd9e40750c77e1542f7ce3ac952a01df6d"}, ] [package.dependencies] -botocore = "1.34.19" +botocore = "1.34.25" colorama = ">=0.2.5,<0.4.5" docutils = ">=0.10,<0.17" PyYAML = ">=3.10,<6.1" @@ -241,7 +231,6 @@ s3transfer = ">=0.10.0,<0.11.0" name = "awscli-cwlogs" version = "1.4.6" description = "AWSCLI CloudWatch Logs plugin" -category = "main" optional = false python-versions = "*" files = [ @@ -258,7 +247,6 @@ six = ">=1.1.0" name = "bcrypt" version = "4.1.1" description = "Modern password hashing for your software and your servers" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -289,7 +277,6 @@ typecheck = ["mypy"] name = "billiard" version = "4.2.0" description = "Python multiprocessing fork with improvements and bugfixes" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -301,7 +288,6 @@ files = [ name = "black" version = "23.7.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -347,7 +333,6 @@ uvloop = ["uvloop (>=0.15.2)"] name = "bleach" version = "6.0.0" description = "An easy safelist-based HTML-sanitizing tool." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -366,7 +351,6 @@ css = ["tinycss2 (>=1.1.0,<1.2)"] name = "blinker" version = "1.7.0" description = "Fast, simple object-to-object and broadcast signaling" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -378,7 +362,6 @@ files = [ name = "boto" version = "2.49.0" description = "Amazon Web Services Library" -category = "main" optional = false python-versions = "*" files = [ @@ -388,18 +371,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.19" +version = "1.34.25" description = "The AWS SDK for Python" -category = "main" optional = false python-versions = ">= 3.8" files = [ - {file = "boto3-1.34.19-py3-none-any.whl", hash = "sha256:4c76ef92af7dbdcea21b196a2699671e82e8814d4cfe570c48eda477dd1aeb19"}, - {file = "boto3-1.34.19.tar.gz", hash = "sha256:95d2c2bde86a0934d4c461020c50fc1344b444f167654e215f1de549bc77fc0f"}, + {file = "boto3-1.34.25-py3-none-any.whl", hash = "sha256:87532469188f1eeef4dca67dffbd3f0cc1d51cef7d5e5b5dc95d3b8125f8446e"}, + {file = "boto3-1.34.25.tar.gz", hash = "sha256:1b415e0553679ea05b9e2aed3eb271431011a67a165e3e0aefa323e13b8b7e92"}, ] [package.dependencies] -botocore = ">=1.34.19,<1.35.0" +botocore = ">=1.34.25,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -408,14 +390,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.19" +version = "1.34.25" description = "Low-level, data-driven core of boto 3." -category = "main" optional = false python-versions = ">= 3.8" files = [ - {file = "botocore-1.34.19-py3-none-any.whl", hash = "sha256:a4a39c7092960f5da2439efc5f6220730dab634aaff4c1444bbd1dfa43bc28cc"}, - {file = "botocore-1.34.19.tar.gz", hash = "sha256:64352b2f05de5c6ab025c1d5232880c22775356dcc5a53d798a6f65db847e826"}, + {file = "botocore-1.34.25-py3-none-any.whl", hash = "sha256:35dfab5bdb4620f73ac7c557c4e0d012429706d8760b100f099feea34b5505f8"}, + {file = "botocore-1.34.25.tar.gz", hash = "sha256:a39070bb760bd9545b0eef52a8bcb2d03918206e67a5a786ea4bd6f4bd949edd"}, ] [package.dependencies] @@ -430,7 +411,6 @@ crt = ["awscrt (==0.19.19)"] name = "brotli" version = "1.1.0" description = "Python bindings for the Brotli compression library" -category = "dev" optional = false python-versions = "*" files = [ @@ -523,7 +503,6 @@ files = [ name = "cachelib" version = "0.10.2" description = "A collection of cache libraries in the same API interface." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -535,7 +514,6 @@ files = [ name = "cachetools" version = "4.2.4" description = "Extensible memoizing collections and decorators" -category = "main" optional = false python-versions = "~=3.5" files = [ @@ -545,36 +523,35 @@ files = [ [[package]] name = "celery" -version = "5.3.1" +version = "5.3.6" description = "Distributed Task Queue." -category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "celery-5.3.1-py3-none-any.whl", hash = "sha256:27f8f3f3b58de6e0ab4f174791383bbd7445aff0471a43e99cfd77727940753f"}, - {file = "celery-5.3.1.tar.gz", hash = "sha256:f84d1c21a1520c116c2b7d26593926581191435a03aa74b77c941b93ca1c6210"}, + {file = "celery-5.3.6-py3-none-any.whl", hash = "sha256:9da4ea0118d232ce97dff5ed4974587fb1c0ff5c10042eb15278487cdd27d1af"}, + {file = "celery-5.3.6.tar.gz", hash = "sha256:870cc71d737c0200c397290d730344cc991d13a057534353d124c9380267aab9"}, ] [package.dependencies] -billiard = ">=4.1.0,<5.0" +billiard = ">=4.2.0,<5.0" boto3 = {version = ">=1.26.143", optional = true, markers = "extra == \"sqs\""} click = ">=8.1.2,<9.0" click-didyoumean = ">=0.3.0" click-plugins = ">=1.1.1" click-repl = ">=0.2.0" kombu = [ - {version = ">=5.3.1,<6.0"}, + {version = ">=5.3.4,<6.0"}, {version = ">=5.3.0", extras = ["sqs"], optional = true, markers = "extra == \"sqs\""}, ] pycurl = {version = ">=7.43.0.5", optional = true, markers = "sys_platform != \"win32\" and platform_python_implementation == \"CPython\" and extra == \"sqs\""} python-dateutil = ">=2.8.2" tzdata = ">=2022.7" urllib3 = {version = ">=1.26.16", optional = true, markers = "extra == \"sqs\""} -vine = ">=5.0.0,<6.0" +vine = ">=5.1.0,<6.0" [package.extras] -arangodb = ["pyArango (>=2.0.1)"] -auth = ["cryptography (==41.0.1)"] +arangodb = ["pyArango (>=2.0.2)"] +auth = ["cryptography (==41.0.5)"] azureblockblob = ["azure-storage-blob (>=12.15.0)"] brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] cassandra = ["cassandra-driver (>=3.25.0,<4)"] @@ -584,32 +561,31 @@ couchbase = ["couchbase (>=3.0.0)"] couchdb = ["pycouchdb (==1.14.2)"] django = ["Django (>=2.2.28)"] dynamodb = ["boto3 (>=1.26.143)"] -elasticsearch = ["elasticsearch (<8.0)"] +elasticsearch = ["elastic-transport (<=8.10.0)", "elasticsearch (<=8.11.0)"] eventlet = ["eventlet (>=0.32.0)"] gevent = ["gevent (>=1.5.0)"] librabbitmq = ["librabbitmq (>=2.0.0)"] memcache = ["pylibmc (==1.6.3)"] mongodb = ["pymongo[srv] (>=4.0.2)"] -msgpack = ["msgpack (==1.0.5)"] +msgpack = ["msgpack (==1.0.7)"] pymemcache = ["python-memcached (==1.59)"] pyro = ["pyro4 (==4.82)"] pytest = ["pytest-celery (==0.0.0)"] -redis = ["redis (>=4.5.2,!=4.5.5)"] +redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] s3 = ["boto3 (>=1.26.143)"] slmq = ["softlayer-messaging (>=1.0.3)"] -solar = ["ephem (==4.1.4)"] +solar = ["ephem (==4.1.5)"] sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.0)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] yaml = ["PyYAML (>=3.10)"] zookeeper = ["kazoo (>=1.3.1)"] -zstd = ["zstandard (==0.21.0)"] +zstd = ["zstandard (==0.22.0)"] [[package]] name = "certifi" version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -621,7 +597,6 @@ files = [ name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = "*" files = [ @@ -698,7 +673,6 @@ pycparser = "*" name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -798,7 +772,6 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -813,7 +786,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "click-datetime" version = "0.2" description = "Datetime type support for click." -category = "main" optional = false python-versions = "*" files = [ @@ -831,7 +803,6 @@ dev = ["wheel"] name = "click-didyoumean" version = "0.3.0" description = "Enables git-like *did-you-mean* feature in click" -category = "main" optional = false python-versions = ">=3.6.2,<4.0.0" files = [ @@ -846,7 +817,6 @@ click = ">=7" name = "click-plugins" version = "1.1.1" description = "An extension module for click to enable registering CLI commands via setuptools entry-points." -category = "main" optional = false python-versions = "*" files = [ @@ -864,7 +834,6 @@ dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] name = "click-repl" version = "0.3.0" description = "REPL plugin for Click" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -883,7 +852,6 @@ testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -895,7 +863,6 @@ files = [ name = "configargparse" version = "1.7" description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -911,7 +878,6 @@ yaml = ["PyYAML"] name = "coverage" version = "5.5" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" files = [ @@ -979,7 +945,6 @@ toml = ["toml"] name = "coveralls" version = "3.3.1" description = "Show coverage stats online via coveralls.io" -category = "dev" optional = false python-versions = ">= 3.5" files = [ @@ -988,7 +953,7 @@ files = [ ] [package.dependencies] -coverage = ">=4.1,<6.0.0 || >6.1,<6.1.1 || >6.1.1,<7.0" +coverage = ">=4.1,<6.0.dev0 || >6.1,<6.1.1 || >6.1.1,<7.0" docopt = ">=0.6.1" requests = ">=1.0.0" @@ -997,55 +962,62 @@ yaml = ["PyYAML (>=3.10)"] [[package]] name = "cryptography" -version = "41.0.7" +version = "42.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf"}, - {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1"}, - {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157"}, - {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406"}, - {file = "cryptography-41.0.7-cp37-abi3-win32.whl", hash = "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"}, - {file = "cryptography-41.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309"}, - {file = "cryptography-41.0.7.tar.gz", hash = "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc"}, + {file = "cryptography-42.0.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:de5086cd475d67113ccb6f9fae6d8fe3ac54a4f9238fd08bfdb07b03d791ff0a"}, + {file = "cryptography-42.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:935cca25d35dda9e7bd46a24831dfd255307c55a07ff38fd1a92119cffc34857"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20100c22b298c9eaebe4f0b9032ea97186ac2555f426c3e70670f2517989543b"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eb6368d5327d6455f20327fb6159b97538820355ec00f8cc9464d617caecead"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:39d5c93e95bcbc4c06313fc6a500cee414ee39b616b55320c1904760ad686938"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3d96ea47ce6d0055d5b97e761d37b4e84195485cb5a38401be341fabf23bc32a"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d1998e545081da0ab276bcb4b33cce85f775adb86a516e8f55b3dac87f469548"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93fbee08c48e63d5d1b39ab56fd3fdd02e6c2431c3da0f4edaf54954744c718f"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:90147dad8c22d64b2ff7331f8d4cddfdc3ee93e4879796f837bdbb2a0b141e0c"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4dcab7c25e48fc09a73c3e463d09ac902a932a0f8d0c568238b3696d06bf377b"}, + {file = "cryptography-42.0.3-cp37-abi3-win32.whl", hash = "sha256:1e935c2900fb53d31f491c0de04f41110351377be19d83d908c1fd502ae8daa5"}, + {file = "cryptography-42.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:762f3771ae40e111d78d77cbe9c1035e886ac04a234d3ee0856bf4ecb3749d54"}, + {file = "cryptography-42.0.3-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3ec384058b642f7fb7e7bff9664030011ed1af8f852540c76a1317a9dd0d20"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35772a6cffd1f59b85cb670f12faba05513446f80352fe811689b4e439b5d89e"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04859aa7f12c2b5f7e22d25198ddd537391f1695df7057c8700f71f26f47a129"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c3d1f5a1d403a8e640fa0887e9f7087331abb3f33b0f2207d2cc7f213e4a864c"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:df34312149b495d9d03492ce97471234fd9037aa5ba217c2a6ea890e9166f151"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:de4ae486041878dc46e571a4c70ba337ed5233a1344c14a0790c4c4be4bbb8b4"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0fab2a5c479b360e5e0ea9f654bcebb535e3aa1e493a715b13244f4e07ea8eec"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25b09b73db78facdfd7dd0fa77a3f19e94896197c86e9f6dc16bce7b37a96504"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d5cf11bc7f0b71fb71af26af396c83dfd3f6eed56d4b6ef95d57867bf1e4ba65"}, + {file = "cryptography-42.0.3-cp39-abi3-win32.whl", hash = "sha256:0fea01527d4fb22ffe38cd98951c9044400f6eff4788cf52ae116e27d30a1ba3"}, + {file = "cryptography-42.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:2619487f37da18d6826e27854a7f9d4d013c51eafb066c80d09c63cf24505306"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ead69ba488f806fe1b1b4050febafdbf206b81fa476126f3e16110c818bac396"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:20180da1b508f4aefc101cebc14c57043a02b355d1a652b6e8e537967f1e1b46"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5fbf0f3f0fac7c089308bd771d2c6c7b7d53ae909dce1db52d8e921f6c19bb3a"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c23f03cfd7d9826cdcbad7850de67e18b4654179e01fe9bc623d37c2638eb4ef"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db0480ffbfb1193ac4e1e88239f31314fe4c6cdcf9c0b8712b55414afbf80db4"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:6c25e1e9c2ce682d01fc5e2dde6598f7313027343bd14f4049b82ad0402e52cd"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9541c69c62d7446539f2c1c06d7046aef822940d248fa4b8962ff0302862cc1f"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b797099d221df7cce5ff2a1d272761d1554ddf9a987d3e11f6459b38cd300fd"}, + {file = "cryptography-42.0.3.tar.gz", hash = "sha256:069d2ce9be5526a44093a0991c450fe9906cdf069e0e7cd67d9dee49a62b9ebe"}, ] [package.dependencies] -cffi = ">=1.12" +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)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] nox = ["nox"] -pep8test = ["black", "check-sdist", "mypy", "ruff"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] name = "docopt" version = "0.6.2" description = "Pythonic argument parser, that will make you smile" -category = "main" optional = false python-versions = "*" files = [ @@ -1056,7 +1028,6 @@ files = [ name = "docutils" version = "0.16" description = "Docutils -- Python Documentation Utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1068,7 +1039,6 @@ files = [ name = "environs" version = "9.5.0" description = "simplified environment variable parsing" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1090,7 +1060,6 @@ tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] name = "exceptiongroup" version = "1.2.0" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1105,7 +1074,6 @@ test = ["pytest (>=6)"] name = "execnet" version = "2.0.2" description = "execnet: rapid multi-Python deployment" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1120,7 +1088,6 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] name = "fido2" version = "0.9.3" description = "Python based FIDO 2.0 library" -category = "main" optional = false python-versions = ">=2.7.6,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" files = [ @@ -1138,7 +1105,6 @@ pcsc = ["pyscard"] name = "filelock" version = "3.13.1" description = "A platform independent file lock." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1155,7 +1121,6 @@ typing = ["typing-extensions (>=4.8)"] name = "flake8" version = "6.1.0" description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -1172,7 +1137,6 @@ pyflakes = ">=3.1.0,<3.2.0" name = "flask" version = "2.3.3" description = "A simple framework for building complex web applications." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1195,7 +1159,6 @@ dotenv = ["python-dotenv"] name = "flask-basicauth" version = "0.2.0" description = "HTTP basic access authentication for Flask." -category = "dev" optional = false python-versions = "*" files = [ @@ -1209,7 +1172,6 @@ Flask = "*" name = "flask-bcrypt" version = "1.0.1" description = "Brcrypt hashing for Flask." -category = "main" optional = false python-versions = "*" files = [ @@ -1225,7 +1187,6 @@ Flask = "*" name = "flask-cors" version = "4.0.0" description = "A Flask extension adding a decorator for CORS support" -category = "dev" optional = false python-versions = "*" files = [ @@ -1240,7 +1201,6 @@ Flask = ">=0.9" name = "flask-marshmallow" version = "0.14.0" description = "Flask + marshmallow for beautiful APIs" -category = "main" optional = false python-versions = "*" files = [ @@ -1264,7 +1224,6 @@ tests = ["flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-s name = "flask-migrate" version = "2.7.0" description = "SQLAlchemy database migrations for Flask applications using Alembic" -category = "main" optional = false python-versions = "*" files = [ @@ -1281,7 +1240,6 @@ Flask-SQLAlchemy = ">=1.0" name = "flask-redis" version = "0.4.0" description = "A nice way to use Redis in your Flask app" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1301,7 +1259,6 @@ tests = ["coverage", "pytest", "pytest-mock"] name = "Flask-SQLAlchemy" version = "2.3.2.dev20231128" description = "Adds SQLAlchemy support to your Flask application" -category = "main" optional = false python-versions = "*" files = [] @@ -1321,7 +1278,6 @@ resolved_reference = "500e732dd1b975a56ab06a46bd1a20a21e682262" name = "freezegun" version = "1.2.2" description = "Let your Python tests travel through time" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1336,7 +1292,6 @@ python-dateutil = ">=2.7" name = "frozenlist" version = "1.4.0" description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1407,7 +1362,6 @@ files = [ name = "future" version = "0.18.3" description = "Clean single-source support for Python 3 and 2" -category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1416,46 +1370,56 @@ files = [ [[package]] name = "gevent" -version = "23.9.0" +version = "23.9.1" description = "Coroutine-based network library" -category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "gevent-23.9.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:1b9ff212cddf245d44651600970c5370c4168f710d4856d8b2350d734dfff1a6"}, - {file = "gevent-23.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03d7452ec45c71ef84cacbf199dac582af2948fb37c638aa6e7232fe5c2840f1"}, - {file = "gevent-23.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41199f10877f368822058b47aca3276e5a4c453e2c4df814a44f23c1bf059488"}, - {file = "gevent-23.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3778609919477ae6eccbf3b10308015f5620c428dbf0dbc6cbeb8a135c9d603a"}, - {file = "gevent-23.9.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2894d82111ed27b7f80dcbf8c713bb9c9c2c7e8890606cf548f6320fb2ae328f"}, - {file = "gevent-23.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1578b5c6da4e88f5cade5682b13635d17231ade6c549d8498cc95776954675c4"}, - {file = "gevent-23.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1ecd70495749ba1ece8e312e41e98d3371f5aa1e98922b47b3635c8263157938"}, - {file = "gevent-23.9.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:259438ec75b31b199d99941294e50f705d92d20d59578244a882a57c2f0f52b3"}, - {file = "gevent-23.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6eecefb35ceaa33693b971d18e98bfc7e63c9488f1b83eddedb8087a567066"}, - {file = "gevent-23.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17abeca1a8b61a0ce135127f8328265a317efaa3eda047199d65129ca68f0268"}, - {file = "gevent-23.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06a927940f9348b504888f93c8d85c2d8e2277ffd2105734148c0082ade17c20"}, - {file = "gevent-23.9.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:96a3ce33e77277995436656e6f7712f4dcbe9dafa527f245ebbe3de44fd3d4bb"}, - {file = "gevent-23.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:169784ef1594ddac0a773e85e5f996f6f8512abdbe248bff16ed8a22fafecadb"}, - {file = "gevent-23.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:faad7ed513672083eabe1e45d1dbfbbcd7bde9c0da6ef3598e975128c8aee427"}, - {file = "gevent-23.9.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:863a244311e986e924baa1516d821c37fcdb4e091e42f86f9d17d13323893c6d"}, - {file = "gevent-23.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44f1d3a05ce1d041d514b8151aec82e4649e5f8f6e2d837eb9bfc2607d7b64d"}, - {file = "gevent-23.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6179e5419044f8e49ab1fd2165391ffd94071362e03a04d2e499a64b619e105"}, - {file = "gevent-23.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291c380fd708c5e04d63fcb8427da21600fda202ffe14ba59a454efd1715f8d5"}, - {file = "gevent-23.9.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:117904bdb139adeb3378a0631e81fc5939b54edce16ddf21288bde111ab31d47"}, - {file = "gevent-23.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70bd1e7a6f571837beb190e3a330f289d10ba4d84fa980ffe778b97fb887455c"}, - {file = "gevent-23.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d76871f16df200c9454d3f9695b6cd97e7b80102fdf3e4b69b1e2900f9bd0eee"}, - {file = "gevent-23.9.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:436d0e9c1185b15f0204f7e2a687af2fc5307b1fed8ce011b86732ad53edc136"}, - {file = "gevent-23.9.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:2bb65683df8a0a1427d42985158cee7dad8a3211247d5824302e276d9fd5bf65"}, - {file = "gevent-23.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bb645e9f68cb4b009d667507195e07314c594b2dac962ae797bd05bccd4076eb"}, - {file = "gevent-23.9.0-cp38-cp38-win32.whl", hash = "sha256:e9e3f81cc36eb623e8716515bd7e28f50ccca5fd930ba493275e60d5fbc69786"}, - {file = "gevent-23.9.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:951d95f1066fb84f661f2bb2c1d6423855a63922abf3e5bd1001711cfb1d8f38"}, - {file = "gevent-23.9.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:d55cf22d6b29593d256bad9fade978fbdc998d6f3bf3aa3399a81feaa91b5a86"}, - {file = "gevent-23.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5afcf6402981f33c84567e6a0a8dcf05e52e8c0b03d80b699644c5f251221e02"}, - {file = "gevent-23.9.0-cp39-cp39-win32.whl", hash = "sha256:91e1e96d50dc4a296dce77b9e28c29a7401fbaca6809989b2029266db6172967"}, + {file = "gevent-23.9.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae"}, + {file = "gevent-23.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6"}, + {file = "gevent-23.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7"}, + {file = "gevent-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e"}, + {file = "gevent-23.9.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7"}, + {file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71"}, + {file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e"}, + {file = "gevent-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a"}, + {file = "gevent-23.9.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599"}, + {file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303"}, + {file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d"}, + {file = "gevent-23.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1"}, + {file = "gevent-23.9.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe"}, + {file = "gevent-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5"}, + {file = "gevent-23.9.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397"}, + {file = "gevent-23.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507"}, + {file = "gevent-23.9.1-cp38-cp38-win32.whl", hash = "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a"}, + {file = "gevent-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f"}, + {file = "gevent-23.9.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a"}, + {file = "gevent-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653"}, + {file = "gevent-23.9.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd"}, + {file = "gevent-23.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543"}, + {file = "gevent-23.9.1-cp39-cp39-win32.whl", hash = "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2"}, + {file = "gevent-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b"}, + {file = "gevent-23.9.1.tar.gz", hash = "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34"}, ] [package.dependencies] cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} -greenlet = {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.12\""} +greenlet = {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} "zope.event" = "*" "zope.interface" = "*" @@ -1470,7 +1434,6 @@ test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idn name = "geventhttpclient" version = "2.0.11" description = "http client library for gevent" -category = "dev" optional = false python-versions = "*" files = [ @@ -1594,7 +1557,6 @@ six = "*" name = "greenlet" version = "2.0.2" description = "Lightweight in-process concurrent programming" -category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" files = [ @@ -1672,7 +1634,6 @@ test = ["objgraph", "psutil"] name = "gunicorn" version = "20.1.0" description = "WSGI HTTP Server for UNIX" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1693,7 +1654,6 @@ tornado = ["tornado (>=0.2)"] name = "idna" version = "2.10" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1705,7 +1665,6 @@ files = [ name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1717,7 +1676,6 @@ files = [ name = "iso8601" version = "2.0.0" description = "Simple module to parse ISO 8601 dates" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -1729,7 +1687,6 @@ files = [ name = "isodate" version = "0.6.1" description = "An ISO 8601 date/time/duration parser and formatter" -category = "main" optional = false python-versions = "*" files = [ @@ -1744,7 +1701,6 @@ six = "*" name = "isort" version = "5.12.0" description = "A Python utility / library to sort Python imports." -category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -1762,7 +1718,6 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"] name = "itsdangerous" version = "2.1.2" description = "Safely pass data to untrusted environments and back." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1774,7 +1729,6 @@ files = [ name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1792,7 +1746,6 @@ i18n = ["Babel (>=2.7)"] name = "jinja2-cli" version = "0.8.2" description = "A CLI interface to Jinja2" -category = "dev" optional = false python-versions = "*" files = [ @@ -1814,7 +1767,6 @@ yaml = ["jinja2", "pyyaml"] name = "jmespath" version = "1.0.1" description = "JSON Matching Expressions" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1826,7 +1778,6 @@ files = [ name = "jsonschema" version = "3.2.0" description = "An implementation of JSON Schema validation for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -1848,7 +1799,6 @@ format-nongpl = ["idna", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-va name = "kombu" version = "5.3.4" description = "Messaging library for Python." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1884,7 +1834,6 @@ zookeeper = ["kazoo (>=2.8.0)"] name = "locust" version = "2.16.1" description = "Developer friendly load testing framework" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1912,7 +1861,6 @@ Werkzeug = ">=2.0.0" name = "lxml" version = "4.9.3" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ @@ -2020,7 +1968,6 @@ source = ["Cython (>=0.29.35)"] name = "mako" version = "1.3.0" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2038,100 +1985,97 @@ testing = ["pytest"] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.4" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, + {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, ] [[package]] name = "marshmallow" -version = "3.20.1" +version = "3.20.2" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.20.1-py3-none-any.whl", hash = "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c"}, - {file = "marshmallow-3.20.1.tar.gz", hash = "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889"}, + {file = "marshmallow-3.20.2-py3-none-any.whl", hash = "sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9"}, + {file = "marshmallow-3.20.2.tar.gz", hash = "sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd"}, ] [package.dependencies] packaging = ">=17.0" [package.extras] -dev = ["flake8 (==6.0.0)", "flake8-bugbear (==23.7.10)", "mypy (==1.4.1)", "pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.13)", "autodocsumm (==0.2.11)", "sphinx (==7.0.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["flake8 (==6.0.0)", "flake8-bugbear (==23.7.10)", "mypy (==1.4.1)", "pre-commit (>=2.4,<4.0)"] +dev = ["pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.15)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["pre-commit (>=2.4,<4.0)"] tests = ["pytest", "pytz", "simplejson"] [[package]] name = "marshmallow-sqlalchemy" version = "0.29.0" description = "SQLAlchemy integration with the marshmallow (de)serialization library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2154,7 +2098,6 @@ tests = ["pytest", "pytest-lazy-fixture (>=0.6.2)"] name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2166,7 +2109,6 @@ files = [ name = "mistune" version = "0.8.4" description = "The fastest markdown parser in pure Python" -category = "main" optional = false python-versions = "*" files = [ @@ -2178,7 +2120,6 @@ files = [ name = "more-itertools" version = "8.14.0" description = "More routines for operating on iterables, beyond itertools" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -2190,7 +2131,6 @@ files = [ name = "moto" version = "4.1.11" description = "" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2237,7 +2177,6 @@ xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] name = "msgpack" version = "1.0.7" description = "MessagePack serializer" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2303,7 +2242,6 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2387,7 +2325,6 @@ files = [ name = "mypy" version = "1.5.0" description = "Optional static typing for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2429,7 +2366,6 @@ reports = ["lxml"] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -2441,7 +2377,6 @@ files = [ name = "nanoid" version = "2.0.0" description = "A tiny, secure, URL-friendly, unique string ID generator for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -2453,7 +2388,6 @@ files = [ name = "networkx" version = "2.8.8" description = "Python package for creating and manipulating graphs and networks" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2472,7 +2406,6 @@ test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] name = "newrelic" version = "6.10.0.165" description = "New Relic Python Agent" -category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" files = [ @@ -2498,7 +2431,6 @@ infinite-tracing = ["grpcio (<2)", "protobuf (<4)"] name = "notifications-python-client" version = "6.4.1" description = "Python API client for GOV.UK Notify." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2512,29 +2444,28 @@ requests = ">=2.0.0" [[package]] name = "notifications-utils" -version = "52.0.18" +version = "52.1.3" description = "Shared python code for Notification - Provides logging utils etc." -category = "main" optional = false python-versions = "~3.10" files = [] develop = false [package.dependencies] -awscli = "1.32.19" +awscli = "1.32.25" bleach = "6.0.0" -boto3 = "1.34.19" +boto3 = "1.34.25" cachetools = "4.2.4" certifi = "^2023.7.22" -cryptography = "^41.0.2" +cryptography = "^42.0.3" Flask = "2.3.3" Flask-Redis = "0.4.0" itsdangerous = "2.1.2" Jinja2 = "^3.0.0" -markupsafe = "2.1.3" +markupsafe = "2.1.4" mistune = "0.8.4" ordered-set = "4.1.0" -phonenumbers = "8.13.24" +phonenumbers = "8.13.28" py_w3c = "0.3.1" pypdf2 = "1.28.6" python-json-logger = "2.0.7" @@ -2548,14 +2479,13 @@ werkzeug = "2.3.7" [package.source] type = "git" url = "https://github.com/cds-snc/notifier-utils.git" -reference = "52.0.18" -resolved_reference = "69b6eee0c715fa4c3fe9ac48e4ccd6e3d4c1e239" +reference = "52.1.3" +resolved_reference = "06a40db6286f525fe3551e029418458d33342592" [[package]] name = "ordered-set" version = "4.1.0" description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2570,7 +2500,6 @@ dev = ["black", "mypy", "pytest"] name = "packaging" version = "23.2" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2582,7 +2511,6 @@ files = [ name = "pathspec" version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2594,7 +2522,6 @@ files = [ name = "pendulum" version = "2.1.2" description = "Python datetimes made easy" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -2627,21 +2554,19 @@ pytzdata = ">=2020.1" [[package]] name = "phonenumbers" -version = "8.13.24" +version = "8.13.28" description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." -category = "main" optional = false python-versions = "*" files = [ - {file = "phonenumbers-8.13.24-py2.py3-none-any.whl", hash = "sha256:7dd66c57da00c0f373de83074e78d66a0801381cface4d010cfe07be2fa77fe5"}, - {file = "phonenumbers-8.13.24.tar.gz", hash = "sha256:7abc66f38d92c3b9e827d597b5d590283ca3b05288d9fadea8bc0d6c8ad73c06"}, + {file = "phonenumbers-8.13.28-py2.py3-none-any.whl", hash = "sha256:ad7bc7d7fd6599a124423ffb840409630777c72d0ee58ba8070cc8e7efcb4c38"}, + {file = "phonenumbers-8.13.28.tar.gz", hash = "sha256:e22f276b0c4a70bd5b3f6d668d19cab2578f660b8df44d6418f81d64320151b9"}, ] [[package]] name = "platformdirs" version = "4.0.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2657,7 +2582,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2673,7 +2597,6 @@ testing = ["pytest", "pytest-benchmark"] name = "prompt-toolkit" version = "3.0.41" description = "Library for building powerful interactive command lines in Python" -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -2688,7 +2611,6 @@ wcwidth = "*" name = "psutil" version = "5.9.6" description = "Cross-platform lib for process and system monitoring in Python." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -2715,79 +2637,89 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] [[package]] name = "psycopg2-binary" -version = "2.9.7" +version = "2.9.9" description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "psycopg2-binary-2.9.7.tar.gz", hash = "sha256:1b918f64a51ffe19cd2e230b3240ba481330ce1d4b7875ae67305bd1d37b041c"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ea5f8ee87f1eddc818fc04649d952c526db4426d26bab16efbe5a0c52b27d6ab"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2993ccb2b7e80844d534e55e0f12534c2871952f78e0da33c35e648bf002bbff"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbbc3c5d15ed76b0d9db7753c0db40899136ecfe97d50cbde918f630c5eb857a"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:692df8763b71d42eb8343f54091368f6f6c9cfc56dc391858cdb3c3ef1e3e584"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dcfd5d37e027ec393a303cc0a216be564b96c80ba532f3d1e0d2b5e5e4b1e6e"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17cc17a70dfb295a240db7f65b6d8153c3d81efb145d76da1e4a096e9c5c0e63"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e5666632ba2b0d9757b38fc17337d84bdf932d38563c5234f5f8c54fd01349c9"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7db7b9b701974c96a88997d458b38ccb110eba8f805d4b4f74944aac48639b42"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c82986635a16fb1fa15cd5436035c88bc65c3d5ced1cfaac7f357ee9e9deddd4"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4fe13712357d802080cfccbf8c6266a3121dc0e27e2144819029095ccf708372"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-win32.whl", hash = "sha256:122641b7fab18ef76b18860dd0c772290566b6fb30cc08e923ad73d17461dc63"}, - {file = "psycopg2_binary-2.9.7-cp310-cp310-win_amd64.whl", hash = "sha256:f8651cf1f144f9ee0fa7d1a1df61a9184ab72962531ca99f077bbdcba3947c58"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4ecc15666f16f97709106d87284c136cdc82647e1c3f8392a672616aed3c7151"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fbb1184c7e9d28d67671992970718c05af5f77fc88e26fd7136613c4ece1f89"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a7968fd20bd550431837656872c19575b687f3f6f98120046228e451e4064df"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:094af2e77a1976efd4956a031028774b827029729725e136514aae3cdf49b87b"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26484e913d472ecb6b45937ea55ce29c57c662066d222fb0fbdc1fab457f18c5"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f309b77a7c716e6ed9891b9b42953c3ff7d533dc548c1e33fddc73d2f5e21f9"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6d92e139ca388ccfe8c04aacc163756e55ba4c623c6ba13d5d1595ed97523e4b"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2df562bb2e4e00ee064779902d721223cfa9f8f58e7e52318c97d139cf7f012d"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4eec5d36dbcfc076caab61a2114c12094c0b7027d57e9e4387b634e8ab36fd44"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1011eeb0c51e5b9ea1016f0f45fa23aca63966a4c0afcf0340ccabe85a9f65bd"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-win32.whl", hash = "sha256:ded8e15f7550db9e75c60b3d9fcbc7737fea258a0f10032cdb7edc26c2a671fd"}, - {file = "psycopg2_binary-2.9.7-cp311-cp311-win_amd64.whl", hash = "sha256:8a136c8aaf6615653450817a7abe0fc01e4ea720ae41dfb2823eccae4b9062a3"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2dec5a75a3a5d42b120e88e6ed3e3b37b46459202bb8e36cd67591b6e5feebc1"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc10da7e7df3380426521e8c1ed975d22df678639da2ed0ec3244c3dc2ab54c8"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee919b676da28f78f91b464fb3e12238bd7474483352a59c8a16c39dfc59f0c5"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb1c0e682138f9067a58fc3c9a9bf1c83d8e08cfbee380d858e63196466d5c86"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00d8db270afb76f48a499f7bb8fa70297e66da67288471ca873db88382850bf4"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9b0c2b466b2f4d89ccc33784c4ebb1627989bd84a39b79092e560e937a11d4ac"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:51d1b42d44f4ffb93188f9b39e6d1c82aa758fdb8d9de65e1ddfe7a7d250d7ad"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:11abdbfc6f7f7dea4a524b5f4117369b0d757725798f1593796be6ece20266cb"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f02f4a72cc3ab2565c6d9720f0343cb840fb2dc01a2e9ecb8bc58ccf95dc5c06"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-win32.whl", hash = "sha256:81d5dd2dd9ab78d31a451e357315f201d976c131ca7d43870a0e8063b6b7a1ec"}, - {file = "psycopg2_binary-2.9.7-cp37-cp37m-win_amd64.whl", hash = "sha256:62cb6de84d7767164a87ca97e22e5e0a134856ebcb08f21b621c6125baf61f16"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:59f7e9109a59dfa31efa022e94a244736ae401526682de504e87bd11ce870c22"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:95a7a747bdc3b010bb6a980f053233e7610276d55f3ca506afff4ad7749ab58a"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c721ee464e45ecf609ff8c0a555018764974114f671815a0a7152aedb9f3343"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4f37bbc6588d402980ffbd1f3338c871368fb4b1cfa091debe13c68bb3852b3"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac83ab05e25354dad798401babaa6daa9577462136ba215694865394840e31f8"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:024eaeb2a08c9a65cd5f94b31ace1ee3bb3f978cd4d079406aef85169ba01f08"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1c31c2606ac500dbd26381145684d87730a2fac9a62ebcfbaa2b119f8d6c19f4"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:42a62ef0e5abb55bf6ffb050eb2b0fcd767261fa3faf943a4267539168807522"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7952807f95c8eba6a8ccb14e00bf170bb700cafcec3924d565235dffc7dc4ae8"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e02bc4f2966475a7393bd0f098e1165d470d3fa816264054359ed4f10f6914ea"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-win32.whl", hash = "sha256:fdca0511458d26cf39b827a663d7d87db6f32b93efc22442a742035728603d5f"}, - {file = "psycopg2_binary-2.9.7-cp38-cp38-win_amd64.whl", hash = "sha256:d0b16e5bb0ab78583f0ed7ab16378a0f8a89a27256bb5560402749dbe8a164d7"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6822c9c63308d650db201ba22fe6648bd6786ca6d14fdaf273b17e15608d0852"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f94cb12150d57ea433e3e02aabd072205648e86f1d5a0a692d60242f7809b15"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5ee89587696d808c9a00876065d725d4ae606f5f7853b961cdbc348b0f7c9a1"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad5ec10b53cbb57e9a2e77b67e4e4368df56b54d6b00cc86398578f1c635f329"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:642df77484b2dcaf87d4237792246d8068653f9e0f5c025e2c692fc56b0dda70"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6a8b575ac45af1eaccbbcdcf710ab984fd50af048fe130672377f78aaff6fc1"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f955aa50d7d5220fcb6e38f69ea126eafecd812d96aeed5d5f3597f33fad43bb"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ad26d4eeaa0d722b25814cce97335ecf1b707630258f14ac4d2ed3d1d8415265"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ced63c054bdaf0298f62681d5dcae3afe60cbae332390bfb1acf0e23dcd25fc8"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2b04da24cbde33292ad34a40db9832a80ad12de26486ffeda883413c9e1b1d5e"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-win32.whl", hash = "sha256:18f12632ab516c47c1ac4841a78fddea6508a8284c7cf0f292cb1a523f2e2379"}, - {file = "psycopg2_binary-2.9.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb3b8d55924a6058a26db69fb1d3e7e32695ff8b491835ba9f479537e14dcf9f"}, + {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 = "pwnedpasswords" version = "2.0.0" description = "A Python wrapper for Troy Hunt's Pwned Passwords API." -category = "main" optional = false python-versions = "*" files = [ @@ -2801,7 +2733,6 @@ future = "*" name = "py" version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -2813,7 +2744,6 @@ files = [ name = "py-w3c" version = "0.3.1" description = "W3C services for python." -category = "main" optional = false python-versions = "*" files = [ @@ -2824,7 +2754,6 @@ files = [ name = "pyasn1" version = "0.5.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -2836,7 +2765,6 @@ files = [ name = "pycodestyle" version = "2.11.1" description = "Python style guide checker" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2848,7 +2776,6 @@ files = [ name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -2860,7 +2787,6 @@ files = [ name = "pycurl" version = "7.45.2" description = "PycURL -- A Python Interface To The cURL library" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -2871,7 +2797,6 @@ files = [ name = "pydantic" version = "2.5.2" description = "Data validation using Python type hints" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2891,7 +2816,6 @@ email = ["email-validator (>=2.0.0)"] name = "pydantic-core" version = "2.14.5" description = "" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3009,7 +2933,6 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" name = "pyflakes" version = "3.1.0" description = "passive checker of Python programs" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3021,7 +2944,6 @@ files = [ name = "pyjwt" version = "2.8.0" description = "JSON Web Token implementation in Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3039,7 +2961,6 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] name = "pypdf2" version = "1.28.6" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" -category = "main" optional = false python-versions = ">=2.7" files = [ @@ -3051,7 +2972,6 @@ files = [ name = "pyrsistent" version = "0.20.0" description = "Persistent/Functional/Immutable data structures" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3091,14 +3011,13 @@ files = [ [[package]] name = "pytest" -version = "7.4.0" +version = "7.4.4" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, - {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -3116,7 +3035,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-cov" version = "3.0.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3135,7 +3053,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "pytest-env" version = "0.8.2" description = "py.test plugin that allows you to add environment variables." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3153,7 +3070,6 @@ test = ["coverage (>=7.2.7)", "pytest-mock (>=3.10)"] name = "pytest-forked" version = "1.6.0" description = "run tests in isolated forked subprocesses" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3169,7 +3085,6 @@ pytest = ">=3.10" name = "pytest-mock" version = "3.11.1" description = "Thin-wrapper around the mock package for easier use with pytest" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3185,14 +3100,13 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "pytest-mock-resources" -version = "2.9.1" +version = "2.9.2" description = "A pytest plugin for easily instantiating reproducible mock resources." -category = "dev" optional = false python-versions = ">=3.7,<4" files = [ - {file = "pytest_mock_resources-2.9.1-py3-none-any.whl", hash = "sha256:707bbe5552a29ec01dc6811d8e6a5c741dda29dfd2cefb3904fa75f04a32ebb5"}, - {file = "pytest_mock_resources-2.9.1.tar.gz", hash = "sha256:dc3448dead0a87d02e13f85f1ac16ff50f7406196fdf95b8b1a61f68b0981ad6"}, + {file = "pytest_mock_resources-2.9.2-py3-none-any.whl", hash = "sha256:31a585e2fcf96303a98586f7024e4db8f668d8f5a9310cd79c554fd9f5687d49"}, + {file = "pytest_mock_resources-2.9.2.tar.gz", hash = "sha256:3b65808f1e8f01233038768c522fca3de65b01d7bc4b077ac3aa28ce41da04d3"}, ] [package.dependencies] @@ -3218,7 +3132,6 @@ redshift = ["boto3", "filelock", "moto", "python-on-whales (>=0.22.0)", "sqlpars name = "pytest-xdist" version = "2.5.0" description = "pytest xdist plugin for distributed testing and loop-on-failing modes" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3240,7 +3153,6 @@ testing = ["filelock"] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -3253,14 +3165,13 @@ six = ">=1.5" [[package]] name = "python-dotenv" -version = "1.0.0" +version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, + {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] @@ -3270,7 +3181,6 @@ cli = ["click (>=5.0)"] name = "python-json-logger" version = "2.0.7" description = "A python library adding a json log formatter" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3282,7 +3192,6 @@ files = [ name = "python-magic" version = "0.4.27" description = "File type identification using libmagic" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3294,7 +3203,6 @@ files = [ name = "python-on-whales" version = "0.67.0" description = "A Docker client for Python, designed to be fun and intuitive!" -category = "dev" optional = false python-versions = "<4,>=3.8" files = [ @@ -3303,7 +3211,7 @@ files = [ ] [package.dependencies] -pydantic = ">=1.9,<2.0.0 || >=2.1.0,<3" +pydantic = ">=1.9,<2.0.dev0 || >=2.1.dev0,<3" requests = "*" tqdm = "*" typer = ">=0.4.1" @@ -3316,7 +3224,6 @@ test = ["pytest"] name = "pytz" version = "2021.3" description = "World timezone definitions, modern and historical" -category = "main" optional = false python-versions = "*" files = [ @@ -3328,7 +3235,6 @@ files = [ name = "pytzdata" version = "2020.1" description = "The Olson timezone database for Python." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3340,7 +3246,6 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" -category = "dev" optional = false python-versions = "*" files = [ @@ -3364,7 +3269,6 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3386,6 +3290,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -3424,7 +3329,6 @@ files = [ name = "pyzmq" version = "25.1.1" description = "Python bindings for 0MQ" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3530,7 +3434,6 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} name = "redis" version = "5.0.1" description = "Python client for Redis database and key-value store" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3549,7 +3452,6 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)" name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3571,7 +3473,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-file" version = "1.5.1" description = "File transport adapter for Requests" -category = "main" optional = false python-versions = "*" files = [ @@ -3587,7 +3488,6 @@ six = "*" name = "requests-mock" version = "1.11.0" description = "Mock out responses from the requests package" -category = "dev" optional = false python-versions = "*" files = [ @@ -3607,7 +3507,6 @@ test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "tes name = "requests-toolbelt" version = "1.0.0" description = "A utility belt for advanced users of python-requests" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3622,7 +3521,6 @@ requests = ">=2.0.1,<3.0.0" name = "responses" version = "0.24.1" description = "A utility library for mocking out the `requests` Python library." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3642,7 +3540,6 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy name = "rfc3987" version = "1.3.8" description = "Parsing and validation of URIs (RFC 3986) and IRIs (RFC 3987)" -category = "dev" optional = false python-versions = "*" files = [ @@ -3654,7 +3551,6 @@ files = [ name = "roundrobin" version = "0.0.4" description = "Collection of roundrobin utilities" -category = "dev" optional = false python-versions = "*" files = [ @@ -3665,7 +3561,6 @@ files = [ name = "rsa" version = "4.7.2" description = "Pure-Python RSA implementation" -category = "main" optional = false python-versions = ">=3.5, <4" files = [ @@ -3680,7 +3575,6 @@ pyasn1 = ">=0.1.3" name = "s3transfer" version = "0.10.0" description = "An Amazon S3 Transfer Manager" -category = "main" optional = false python-versions = ">= 3.8" files = [ @@ -3698,7 +3592,6 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] name = "setuptools" version = "69.0.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3715,7 +3608,6 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar name = "simple-salesforce" version = "1.12.5" description = "A basic Salesforce.com REST API client." -category = "main" optional = false python-versions = "*" files = [ @@ -3735,7 +3627,6 @@ zeep = "*" name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -3747,7 +3638,6 @@ files = [ name = "smartypants" version = "2.0.1" description = "Python with the SmartyPants" -category = "main" optional = false python-versions = "*" files = [ @@ -3756,68 +3646,65 @@ files = [ [[package]] name = "sqlalchemy" -version = "1.4.49" +version = "1.4.51" description = "Database Abstraction Library" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "SQLAlchemy-1.4.49-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e126cf98b7fd38f1e33c64484406b78e937b1a280e078ef558b95bf5b6895f6"}, - {file = "SQLAlchemy-1.4.49-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:03db81b89fe7ef3857b4a00b63dedd632d6183d4ea5a31c5d8a92e000a41fc71"}, - {file = "SQLAlchemy-1.4.49-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:95b9df9afd680b7a3b13b38adf6e3a38995da5e162cc7524ef08e3be4e5ed3e1"}, - {file = "SQLAlchemy-1.4.49-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a63e43bf3f668c11bb0444ce6e809c1227b8f067ca1068898f3008a273f52b09"}, - {file = "SQLAlchemy-1.4.49-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca46de16650d143a928d10842939dab208e8d8c3a9a8757600cae9b7c579c5cd"}, - {file = "SQLAlchemy-1.4.49-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f835c050ebaa4e48b18403bed2c0fda986525896efd76c245bdd4db995e51a4c"}, - {file = "SQLAlchemy-1.4.49-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c21b172dfb22e0db303ff6419451f0cac891d2e911bb9fbf8003d717f1bcf91"}, - {file = "SQLAlchemy-1.4.49-cp310-cp310-win32.whl", hash = "sha256:5fb1ebdfc8373b5a291485757bd6431de8d7ed42c27439f543c81f6c8febd729"}, - {file = "SQLAlchemy-1.4.49-cp310-cp310-win_amd64.whl", hash = "sha256:f8a65990c9c490f4651b5c02abccc9f113a7f56fa482031ac8cb88b70bc8ccaa"}, - {file = "SQLAlchemy-1.4.49-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8923dfdf24d5aa8a3adb59723f54118dd4fe62cf59ed0d0d65d940579c1170a4"}, - {file = "SQLAlchemy-1.4.49-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9ab2c507a7a439f13ca4499db6d3f50423d1d65dc9b5ed897e70941d9e135b0"}, - {file = "SQLAlchemy-1.4.49-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5debe7d49b8acf1f3035317e63d9ec8d5e4d904c6e75a2a9246a119f5f2fdf3d"}, - {file = "SQLAlchemy-1.4.49-cp311-cp311-win32.whl", hash = "sha256:82b08e82da3756765c2e75f327b9bf6b0f043c9c3925fb95fb51e1567fa4ee87"}, - {file = "SQLAlchemy-1.4.49-cp311-cp311-win_amd64.whl", hash = "sha256:171e04eeb5d1c0d96a544caf982621a1711d078dbc5c96f11d6469169bd003f1"}, - {file = "SQLAlchemy-1.4.49-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f23755c384c2969ca2f7667a83f7c5648fcf8b62a3f2bbd883d805454964a800"}, - {file = "SQLAlchemy-1.4.49-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8396e896e08e37032e87e7fbf4a15f431aa878c286dc7f79e616c2feacdb366c"}, - {file = "SQLAlchemy-1.4.49-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66da9627cfcc43bbdebd47bfe0145bb662041472393c03b7802253993b6b7c90"}, - {file = "SQLAlchemy-1.4.49-cp312-cp312-win32.whl", hash = "sha256:9a06e046ffeb8a484279e54bda0a5abfd9675f594a2e38ef3133d7e4d75b6214"}, - {file = "SQLAlchemy-1.4.49-cp312-cp312-win_amd64.whl", hash = "sha256:7cf8b90ad84ad3a45098b1c9f56f2b161601e4670827d6b892ea0e884569bd1d"}, - {file = "SQLAlchemy-1.4.49-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:36e58f8c4fe43984384e3fbe6341ac99b6b4e083de2fe838f0fdb91cebe9e9cb"}, - {file = "SQLAlchemy-1.4.49-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b31e67ff419013f99ad6f8fc73ee19ea31585e1e9fe773744c0f3ce58c039c30"}, - {file = "SQLAlchemy-1.4.49-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc22807a7e161c0d8f3da34018ab7c97ef6223578fcdd99b1d3e7ed1100a5db"}, - {file = "SQLAlchemy-1.4.49-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c14b29d9e1529f99efd550cd04dbb6db6ba5d690abb96d52de2bff4ed518bc95"}, - {file = "SQLAlchemy-1.4.49-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c40f3470e084d31247aea228aa1c39bbc0904c2b9ccbf5d3cfa2ea2dac06f26d"}, - {file = "SQLAlchemy-1.4.49-cp36-cp36m-win32.whl", hash = "sha256:706bfa02157b97c136547c406f263e4c6274a7b061b3eb9742915dd774bbc264"}, - {file = "SQLAlchemy-1.4.49-cp36-cp36m-win_amd64.whl", hash = "sha256:a7f7b5c07ae5c0cfd24c2db86071fb2a3d947da7bd487e359cc91e67ac1c6d2e"}, - {file = "SQLAlchemy-1.4.49-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:4afbbf5ef41ac18e02c8dc1f86c04b22b7a2125f2a030e25bbb4aff31abb224b"}, - {file = "SQLAlchemy-1.4.49-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24e300c0c2147484a002b175f4e1361f102e82c345bf263242f0449672a4bccf"}, - {file = "SQLAlchemy-1.4.49-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:393cd06c3b00b57f5421e2133e088df9cabcececcea180327e43b937b5a7caa5"}, - {file = "SQLAlchemy-1.4.49-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:201de072b818f8ad55c80d18d1a788729cccf9be6d9dc3b9d8613b053cd4836d"}, - {file = "SQLAlchemy-1.4.49-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653ed6817c710d0c95558232aba799307d14ae084cc9b1f4c389157ec50df5c"}, - {file = "SQLAlchemy-1.4.49-cp37-cp37m-win32.whl", hash = "sha256:647e0b309cb4512b1f1b78471fdaf72921b6fa6e750b9f891e09c6e2f0e5326f"}, - {file = "SQLAlchemy-1.4.49-cp37-cp37m-win_amd64.whl", hash = "sha256:ab73ed1a05ff539afc4a7f8cf371764cdf79768ecb7d2ec691e3ff89abbc541e"}, - {file = "SQLAlchemy-1.4.49-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:37ce517c011560d68f1ffb28af65d7e06f873f191eb3a73af5671e9c3fada08a"}, - {file = "SQLAlchemy-1.4.49-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1878ce508edea4a879015ab5215546c444233881301e97ca16fe251e89f1c55"}, - {file = "SQLAlchemy-1.4.49-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95ab792ca493891d7a45a077e35b418f68435efb3e1706cb8155e20e86a9013c"}, - {file = "SQLAlchemy-1.4.49-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e8e608983e6f85d0852ca61f97e521b62e67969e6e640fe6c6b575d4db68557"}, - {file = "SQLAlchemy-1.4.49-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccf956da45290df6e809ea12c54c02ace7f8ff4d765d6d3dfb3655ee876ce58d"}, - {file = "SQLAlchemy-1.4.49-cp38-cp38-win32.whl", hash = "sha256:f167c8175ab908ce48bd6550679cc6ea20ae169379e73c7720a28f89e53aa532"}, - {file = "SQLAlchemy-1.4.49-cp38-cp38-win_amd64.whl", hash = "sha256:45806315aae81a0c202752558f0df52b42d11dd7ba0097bf71e253b4215f34f4"}, - {file = "SQLAlchemy-1.4.49-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:b6d0c4b15d65087738a6e22e0ff461b407533ff65a73b818089efc8eb2b3e1de"}, - {file = "SQLAlchemy-1.4.49-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a843e34abfd4c797018fd8d00ffffa99fd5184c421f190b6ca99def4087689bd"}, - {file = "SQLAlchemy-1.4.49-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:738d7321212941ab19ba2acf02a68b8ee64987b248ffa2101630e8fccb549e0d"}, - {file = "SQLAlchemy-1.4.49-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1c890421651b45a681181301b3497e4d57c0d01dc001e10438a40e9a9c25ee77"}, - {file = "SQLAlchemy-1.4.49-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d26f280b8f0a8f497bc10573849ad6dc62e671d2468826e5c748d04ed9e670d5"}, - {file = "SQLAlchemy-1.4.49-cp39-cp39-win32.whl", hash = "sha256:ec2268de67f73b43320383947e74700e95c6770d0c68c4e615e9897e46296294"}, - {file = "SQLAlchemy-1.4.49-cp39-cp39-win_amd64.whl", hash = "sha256:bbdf16372859b8ed3f4d05f925a984771cd2abd18bd187042f24be4886c2a15f"}, - {file = "SQLAlchemy-1.4.49.tar.gz", hash = "sha256:06ff25cbae30c396c4b7737464f2a7fc37a67b7da409993b182b024cec80aed9"}, + {file = "SQLAlchemy-1.4.51-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:1a09d5bd1a40d76ad90e5570530e082ddc000e1d92de495746f6257dc08f166b"}, + {file = "SQLAlchemy-1.4.51-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2be4e6294c53f2ec8ea36486b56390e3bcaa052bf3a9a47005687ccf376745d1"}, + {file = "SQLAlchemy-1.4.51-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca484ca11c65e05639ffe80f20d45e6be81fbec7683d6c9a15cd421e6e8b340"}, + {file = "SQLAlchemy-1.4.51-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0535d5b57d014d06ceeaeffd816bb3a6e2dddeb670222570b8c4953e2d2ea678"}, + {file = "SQLAlchemy-1.4.51-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af55cc207865d641a57f7044e98b08b09220da3d1b13a46f26487cc2f898a072"}, + {file = "SQLAlchemy-1.4.51-cp310-cp310-win32.whl", hash = "sha256:7af40425ac535cbda129d9915edcaa002afe35d84609fd3b9d6a8c46732e02ee"}, + {file = "SQLAlchemy-1.4.51-cp310-cp310-win_amd64.whl", hash = "sha256:8d1d7d63e5d2f4e92a39ae1e897a5d551720179bb8d1254883e7113d3826d43c"}, + {file = "SQLAlchemy-1.4.51-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eaeeb2464019765bc4340214fca1143081d49972864773f3f1e95dba5c7edc7d"}, + {file = "SQLAlchemy-1.4.51-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7deeae5071930abb3669b5185abb6c33ddfd2398f87660fafdb9e6a5fb0f3f2f"}, + {file = "SQLAlchemy-1.4.51-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0892e7ac8bc76da499ad3ee8de8da4d7905a3110b952e2a35a940dab1ffa550e"}, + {file = "SQLAlchemy-1.4.51-cp311-cp311-win32.whl", hash = "sha256:50e074aea505f4427151c286955ea025f51752fa42f9939749336672e0674c81"}, + {file = "SQLAlchemy-1.4.51-cp311-cp311-win_amd64.whl", hash = "sha256:3b0cd89a7bd03f57ae58263d0f828a072d1b440c8c2949f38f3b446148321171"}, + {file = "SQLAlchemy-1.4.51-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a33cb3f095e7d776ec76e79d92d83117438b6153510770fcd57b9c96f9ef623d"}, + {file = "SQLAlchemy-1.4.51-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cacc0b2dd7d22a918a9642fc89840a5d3cee18a0e1fe41080b1141b23b10916"}, + {file = "SQLAlchemy-1.4.51-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:245c67c88e63f1523e9216cad6ba3107dea2d3ee19adc359597a628afcabfbcb"}, + {file = "SQLAlchemy-1.4.51-cp312-cp312-win32.whl", hash = "sha256:8e702e7489f39375601c7ea5a0bef207256828a2bc5986c65cb15cd0cf097a87"}, + {file = "SQLAlchemy-1.4.51-cp312-cp312-win_amd64.whl", hash = "sha256:0525c4905b4b52d8ccc3c203c9d7ab2a80329ffa077d4bacf31aefda7604dc65"}, + {file = "SQLAlchemy-1.4.51-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:1980e6eb6c9be49ea8f89889989127daafc43f0b1b6843d71efab1514973cca0"}, + {file = "SQLAlchemy-1.4.51-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ec7a0ed9b32afdf337172678a4a0e6419775ba4e649b66f49415615fa47efbd"}, + {file = "SQLAlchemy-1.4.51-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352df882088a55293f621328ec33b6ffca936ad7f23013b22520542e1ab6ad1b"}, + {file = "SQLAlchemy-1.4.51-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:86a22143a4001f53bf58027b044da1fb10d67b62a785fc1390b5c7f089d9838c"}, + {file = "SQLAlchemy-1.4.51-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c37bc677690fd33932182b85d37433845de612962ed080c3e4d92f758d1bd894"}, + {file = "SQLAlchemy-1.4.51-cp36-cp36m-win32.whl", hash = "sha256:d0a83afab5e062abffcdcbcc74f9d3ba37b2385294dd0927ad65fc6ebe04e054"}, + {file = "SQLAlchemy-1.4.51-cp36-cp36m-win_amd64.whl", hash = "sha256:a61184c7289146c8cff06b6b41807c6994c6d437278e72cf00ff7fe1c7a263d1"}, + {file = "SQLAlchemy-1.4.51-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:3f0ef620ecbab46e81035cf3dedfb412a7da35340500ba470f9ce43a1e6c423b"}, + {file = "SQLAlchemy-1.4.51-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c55040d8ea65414de7c47f1a23823cd9f3fad0dc93e6b6b728fee81230f817b"}, + {file = "SQLAlchemy-1.4.51-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ef80328e3fee2be0a1abe3fe9445d3a2e52a1282ba342d0dab6edf1fef4707"}, + {file = "SQLAlchemy-1.4.51-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f8cafa6f885a0ff5e39efa9325195217bb47d5929ab0051636610d24aef45ade"}, + {file = "SQLAlchemy-1.4.51-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8f2df79a46e130235bc5e1bbef4de0583fb19d481eaa0bffa76e8347ea45ec6"}, + {file = "SQLAlchemy-1.4.51-cp37-cp37m-win32.whl", hash = "sha256:f2e5b6f5cf7c18df66d082604a1d9c7a2d18f7d1dbe9514a2afaccbb51cc4fc3"}, + {file = "SQLAlchemy-1.4.51-cp37-cp37m-win_amd64.whl", hash = "sha256:5e180fff133d21a800c4f050733d59340f40d42364fcb9d14f6a67764bdc48d2"}, + {file = "SQLAlchemy-1.4.51-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7d8139ca0b9f93890ab899da678816518af74312bb8cd71fb721436a93a93298"}, + {file = "SQLAlchemy-1.4.51-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb18549b770351b54e1ab5da37d22bc530b8bfe2ee31e22b9ebe650640d2ef12"}, + {file = "SQLAlchemy-1.4.51-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55e699466106d09f028ab78d3c2e1f621b5ef2c8694598242259e4515715da7c"}, + {file = "SQLAlchemy-1.4.51-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2ad16880ccd971ac8e570550fbdef1385e094b022d6fc85ef3ce7df400dddad3"}, + {file = "SQLAlchemy-1.4.51-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b97fd5bb6b7c1a64b7ac0632f7ce389b8ab362e7bd5f60654c2a418496be5d7f"}, + {file = "SQLAlchemy-1.4.51-cp38-cp38-win32.whl", hash = "sha256:cecb66492440ae8592797dd705a0cbaa6abe0555f4fa6c5f40b078bd2740fc6b"}, + {file = "SQLAlchemy-1.4.51-cp38-cp38-win_amd64.whl", hash = "sha256:39b02b645632c5fe46b8dd30755682f629ffbb62ff317ecc14c998c21b2896ff"}, + {file = "SQLAlchemy-1.4.51-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:b03850c290c765b87102959ea53299dc9addf76ca08a06ea98383348ae205c99"}, + {file = "SQLAlchemy-1.4.51-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e646b19f47d655261b22df9976e572f588185279970efba3d45c377127d35349"}, + {file = "SQLAlchemy-1.4.51-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3cf56cc36d42908495760b223ca9c2c0f9f0002b4eddc994b24db5fcb86a9e4"}, + {file = "SQLAlchemy-1.4.51-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0d661cff58c91726c601cc0ee626bf167b20cc4d7941c93c5f3ac28dc34ddbea"}, + {file = "SQLAlchemy-1.4.51-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3823dda635988e6744d4417e13f2e2b5fe76c4bf29dd67e95f98717e1b094cad"}, + {file = "SQLAlchemy-1.4.51-cp39-cp39-win32.whl", hash = "sha256:b00cf0471888823b7a9f722c6c41eb6985cf34f077edcf62695ac4bed6ec01ee"}, + {file = "SQLAlchemy-1.4.51-cp39-cp39-win_amd64.whl", hash = "sha256:a055ba17f4675aadcda3005df2e28a86feb731fdcc865e1f6b4f209ed1225cba"}, + {file = "SQLAlchemy-1.4.51.tar.gz", hash = "sha256:e7908c2025eb18394e32d65dd02d2e37e17d733cdbe7d78231c2b6d7eb20cdb9"}, ] [package.dependencies] greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (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\")"} [package.extras] -aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"] +aiomysql = ["aiomysql (>=0.2.0)", "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)", "greenlet (!=0.4.17)"] mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] @@ -3827,20 +3714,19 @@ mssql-pyodbc = ["pyodbc"] mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] mysql-connector = ["mysql-connector-python"] -oracle = ["cx-oracle (>=7)", "cx-oracle (>=7,<8)"] +oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] postgresql = ["psycopg2 (>=2.7)"] postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] postgresql-psycopg2binary = ["psycopg2-binary"] postgresql-psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql", "pymysql (<1)"] -sqlcipher = ["sqlcipher3-binary"] +sqlcipher = ["sqlcipher3_binary"] [[package]] name = "sqlalchemy-stubs" version = "0.4" description = "SQLAlchemy stubs and mypy plugin" -category = "dev" optional = false python-versions = "*" files = [ @@ -3854,14 +3740,13 @@ typing-extensions = ">=3.7.4" [[package]] name = "sqlalchemy2-stubs" -version = "0.0.2a35" +version = "0.0.2a38" description = "Typing Stubs for SQLAlchemy 1.4" -category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "sqlalchemy2-stubs-0.0.2a35.tar.gz", hash = "sha256:bd5d530697d7e8c8504c7fe792ef334538392a5fb7aa7e4f670bfacdd668a19d"}, - {file = "sqlalchemy2_stubs-0.0.2a35-py3-none-any.whl", hash = "sha256:593784ff9fc0dc2ded1895e3322591689db3be06f3ca006e3ef47640baf2d38a"}, + {file = "sqlalchemy2-stubs-0.0.2a38.tar.gz", hash = "sha256:861d722abeb12f13eacd775a9f09379b11a5a9076f469ccd4099961b95800f9e"}, + {file = "sqlalchemy2_stubs-0.0.2a38-py3-none-any.whl", hash = "sha256:b62aa46943807287550e2033dafe07564b33b6a815fbaa3c144e396f9cc53bcb"}, ] [package.dependencies] @@ -3871,7 +3756,6 @@ typing-extensions = ">=3.7.4" name = "statsd" version = "3.3.0" description = "A simple statsd client." -category = "main" optional = false python-versions = "*" files = [ @@ -3883,7 +3767,6 @@ files = [ name = "strict-rfc3339" version = "0.7" description = "Strict, simple, lightweight RFC3339 functions" -category = "dev" optional = false python-versions = "*" files = [ @@ -3894,7 +3777,6 @@ files = [ name = "tldextract" version = "3.4.4" description = "Accurately separates a URL's subdomain, domain, and public suffix, using the Public Suffix List (PSL). By default, this includes the public ICANN TLDs and their exceptions. You can optionally support the Public Suffix List's private domains as well." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3912,7 +3794,6 @@ requests-file = ">=1.4" name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -3924,7 +3805,6 @@ files = [ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3936,7 +3816,6 @@ files = [ name = "tqdm" version = "4.66.1" description = "Fast, Extensible Progress Meter" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3957,7 +3836,6 @@ telegram = ["requests"] name = "typer" version = "0.9.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3979,7 +3857,6 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. name = "types-boto" version = "2.49.18.9" description = "Typing stubs for boto" -category = "dev" optional = false python-versions = "*" files = [ @@ -3991,7 +3868,6 @@ files = [ name = "types-mock" version = "4.0.15.2" description = "Typing stubs for mock" -category = "dev" optional = false python-versions = "*" files = [ @@ -4003,7 +3879,6 @@ files = [ name = "types-pyopenssl" version = "23.3.0.0" description = "Typing stubs for pyOpenSSL" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4016,21 +3891,19 @@ cryptography = ">=35.0.0" [[package]] name = "types-python-dateutil" -version = "2.8.19.14" +version = "2.8.19.20240106" description = "Typing stubs for python-dateutil" -category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.8.19.14.tar.gz", hash = "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b"}, - {file = "types_python_dateutil-2.8.19.14-py3-none-any.whl", hash = "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9"}, + {file = "types-python-dateutil-2.8.19.20240106.tar.gz", hash = "sha256:1f8db221c3b98e6ca02ea83a58371b22c374f42ae5bbdf186db9c9a76581459f"}, + {file = "types_python_dateutil-2.8.19.20240106-py3-none-any.whl", hash = "sha256:efbbdc54590d0f16152fa103c9879c7d4a00e82078f6e2cf01769042165acaa2"}, ] [[package]] name = "types-pytz" version = "2022.7.1.2" description = "Typing stubs for pytz" -category = "dev" optional = false python-versions = "*" files = [ @@ -4040,14 +3913,13 @@ files = [ [[package]] name = "types-redis" -version = "4.6.0.5" +version = "4.6.0.20240106" description = "Typing stubs for redis" -category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "types-redis-4.6.0.5.tar.gz", hash = "sha256:5f179d10bd3ca995a8134aafcddfc3e12d52b208437c4529ef27e68acb301f38"}, - {file = "types_redis-4.6.0.5-py3-none-any.whl", hash = "sha256:4f662060247a2363c7a8f0b7e52915d68960870ff16a749a891eabcf87ed0be4"}, + {file = "types-redis-4.6.0.20240106.tar.gz", hash = "sha256:2b2fa3a78f84559616242d23f86de5f4130dfd6c3b83fb2d8ce3329e503f756e"}, + {file = "types_redis-4.6.0.20240106-py3-none-any.whl", hash = "sha256:912de6507b631934bd225cdac310b04a58def94391003ba83939e5a10e99568d"}, ] [package.dependencies] @@ -4056,36 +3928,22 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.31.0.2" +version = "2.31.0.20240106" description = "Typing stubs for requests" -category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "types-requests-2.31.0.2.tar.gz", hash = "sha256:6aa3f7faf0ea52d728bb18c0a0d1522d9bfd8c72d26ff6f61bfc3d06a411cf40"}, - {file = "types_requests-2.31.0.2-py3-none-any.whl", hash = "sha256:56d181c85b5925cbc59f4489a57e72a8b2166f18273fd8ba7b6fe0c0b986f12a"}, + {file = "types-requests-2.31.0.20240106.tar.gz", hash = "sha256:0e1c731c17f33618ec58e022b614a1a2ecc25f7dc86800b36ef341380402c612"}, + {file = "types_requests-2.31.0.20240106-py3-none-any.whl", hash = "sha256:da997b3b6a72cc08d09f4dba9802fdbabc89104b35fe24ee588e674037689354"}, ] [package.dependencies] -types-urllib3 = "*" - -[[package]] -name = "types-urllib3" -version = "1.26.25.14" -description = "Typing stubs for urllib3" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "types-urllib3-1.26.25.14.tar.gz", hash = "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f"}, - {file = "types_urllib3-1.26.25.14-py3-none-any.whl", hash = "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e"}, -] +urllib3 = ">=2" [[package]] name = "typing-extensions" version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4097,7 +3955,6 @@ files = [ name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" -category = "main" optional = false python-versions = ">=2" files = [ @@ -4107,38 +3964,36 @@ files = [ [[package]] name = "unidecode" -version = "1.3.6" +version = "1.3.8" description = "ASCII transliterations of Unicode text" -category = "main" optional = false python-versions = ">=3.5" files = [ - {file = "Unidecode-1.3.6-py3-none-any.whl", hash = "sha256:547d7c479e4f377b430dd91ac1275d593308dce0fc464fb2ab7d41f82ec653be"}, - {file = "Unidecode-1.3.6.tar.gz", hash = "sha256:fed09cf0be8cf415b391642c2a5addfc72194407caee4f98719e40ec2a72b830"}, + {file = "Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39"}, + {file = "Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4"}, ] [[package]] name = "urllib3" -version = "1.26.18" +version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7" files = [ - {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, - {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, ] [package.extras] -brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "vine" version = "5.1.0" description = "Python promises." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -4150,7 +4005,6 @@ files = [ name = "wcwidth" version = "0.2.12" description = "Measures the displayed width of unicode strings in a terminal" -category = "main" optional = false python-versions = "*" files = [ @@ -4162,7 +4016,6 @@ files = [ name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" -category = "main" optional = false python-versions = "*" files = [ @@ -4174,7 +4027,6 @@ files = [ name = "werkzeug" version = "2.3.7" description = "The comprehensive WSGI web application library." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4192,7 +4044,6 @@ watchdog = ["watchdog (>=2.3)"] name = "xmltodict" version = "0.13.0" description = "Makes working with XML feel like you are working with JSON" -category = "dev" optional = false python-versions = ">=3.4" files = [ @@ -4204,7 +4055,6 @@ files = [ name = "yarl" version = "1.9.3" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4308,7 +4158,6 @@ multidict = ">=4.0" name = "zeep" version = "4.2.1" description = "A Python SOAP client" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4336,7 +4185,6 @@ xmlsec = ["xmlsec (>=0.6.1)"] name = "zope-event" version = "5.0" description = "Very basic event publishing system" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4355,7 +4203,6 @@ test = ["zope.testrunner"] name = "zope-interface" version = "6.1" description = "Interfaces for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4408,4 +4255,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" python-versions = "~3.10.9" -content-hash = "98564f0ca361b4467614a26152b33a3d5c491dca89cbdc5ad20e5da80fed7744" +content-hash = "f2bf5c58fe6d2689072e7b9d4cf91976e07e76ade98dc3153977c4377b98c86e" diff --git a/pyproject.toml b/pyproject.toml index 0055065ab8..609ff16d41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ python = "~3.10.9" apig-wsgi = "2.18.0" boto = "2.49.0" cffi = "1.15.1" -celery = {extras = ["sqs"], version = "5.3.1"} +celery = {extras = ["sqs"], version = "5.3.6"} docopt = "0.6.2" environs = "9.5.0" # pyup: <9.3.3 # marshmallow v3 throws errors" fido2 = "0.9.3" @@ -35,36 +35,36 @@ Flask-SQLAlchemy = { git = "https://github.com/pallets-eco/flask-sqlalchemy.git" #git+https://github.com/mitsuhiko/flask-sqlalchemy.git@500e732dd1b975a56ab06a46bd1a20a21e682262#egg=Flask-SQLAlchemy==2.3.2.dev20190108 Flask = "2.3.3" click-datetime = "0.2" -gevent = "23.9.0" +gevent = "23.9.1" gunicorn = "20.1.0" iso8601 = "2.0.0" jsonschema = "3.2.0" marshmallow-sqlalchemy = "0.29.0" -marshmallow = "3.20.1" +marshmallow = "3.20.2" python-magic = "0.4.27" -psycopg2-binary = "2.9.7" +psycopg2-binary = "2.9.9" PyJWT = "2.8.0" pytz = "2021.3" PyYAML = "6.0.1" -SQLAlchemy = "1.4.49" +SQLAlchemy = "1.4.51" cachelib = "0.10.2" newrelic = "6.10.0.165" notifications-python-client = "6.4.1" -python-dotenv = "1.0.0" +python-dotenv = "1.0.1" pwnedpasswords = "2.0.0" tldextract = "3.4.4" nanoid = "2.0.0" -unidecode = "1.3.6" +unidecode = "1.3.8" more-itertools = "8.14.0" # PaaS awscli-cwlogs = "1.4.6" aws-embedded-metrics = "1.0.8" # Putting upgrade on hold due to new version introducing breaking changes Werkzeug = "2.3.7" -MarkupSafe = "2.1.3" +MarkupSafe = "2.1.4" # REVIEW: v2 is using sha512 instead of sha1 by default (in v1) itsdangerous = "2.1.2" -notifications-utils = { git = "https://github.com/cds-snc/notifier-utils.git", rev = "52.0.18" } +notifications-utils = { git = "https://github.com/cds-snc/notifier-utils.git", tag = "52.1.3" } # rsa = "4.9 # awscli 1.22.38 depends on rsa<4.8 typing-extensions = "4.7.1" greenlet = "2.0.2" @@ -72,7 +72,6 @@ simple-salesforce = "^1.12.3" # Pinned dependencies certifi = "^2023.7.22" # pinned for security reasons: https://github.com/cds-snc/notification-api/security/dependabot/119 -cryptography = "^41.0.2" # pinned for security reasons: https://github.com/cds-snc/notification-api/security/dependabot/118 idna = "2.10" # pinned to align with test moto dependency requirements (for <=2.9) [tool.poetry.group.test.dependencies] @@ -80,7 +79,7 @@ flake8 = "6.1.0" isort = "5.12.0" moto = "4.1.11" idna = "2.10" -pytest = "7.4.0" +pytest = "7.4.4" pytest-env = "0.8.2" pytest-mock = "3.11.1" pytest-cov = "3.0.0" @@ -97,12 +96,12 @@ black = "23.7.0" locust = "2.16.1" mypy = "1.5" sqlalchemy-stubs = "0.4" -sqlalchemy2-stubs = "0.0.2a35" +sqlalchemy2-stubs = "0.0.2a38" networkx = "2.8.8" # not directly required, pinned by Snyk to avoid a vulnerability -pytest-mock-resources = { extras = ["redis"], version = "2.9.1" } +pytest-mock-resources = { extras = ["redis"], version = "2.9.2" } types-boto = "2.49.18.9" types-mock = "4.0.15.2" -types-python-dateutil = "2.8.19.14" +types-python-dateutil = "2.8.19.20240106" types-pytz = "2022.7.1.2" -types-requests = "2.31.0.2" -types-redis = "4.6.0.5" +types-requests = "2.31.0.20240106" +types-redis = "4.6.0.20240106" diff --git a/scripts/enlarge_db/README.md b/scripts/enlarge_db/README.md new file mode 100644 index 0000000000..b36090390b --- /dev/null +++ b/scripts/enlarge_db/README.md @@ -0,0 +1,22 @@ +# Enlarge DB + +## Purpose + +The purpose of this script is add rows to the notification_history table. This is useful in estimating how long database-related infrastructure operations will take when performed on a database the same size as that in production. + +## How to use + +The script should be run in the same environment as api. Locally this can be in the api repo devcontainer, while in AWS the api kubernetes pod would be preferred. + +To add 2000 rows to the table with a client_reference of "test2000" run + +``` +cd scripts/enlarge_db +python enlarge_db.py -n 2000 -r test2000 +``` + +The new notifications are added in batches to improve performance, with a default batch size of 10000. You may use a different batch with the `-c` parameter, for example + +``` +python enlarge_db.py -n 2000 -c 101 -r test2000x101 +``` diff --git a/scripts/enlarge_db/enlarge_db.py b/scripts/enlarge_db/enlarge_db.py new file mode 100644 index 0000000000..649e6f3032 --- /dev/null +++ b/scripts/enlarge_db/enlarge_db.py @@ -0,0 +1,55 @@ + +import argparse +import sys +from datetime import datetime +from typing import List + +from flask import Flask + +sys.path.append("../..") +from app import create_app, create_uuid, db # noqa: E402 +from app.config import Config # noqa: E402 +from app.models import NotificationHistory # noqa: E402 + +DEFAULT_CHUNK_SIZE = 10000 + + +def create_notifications(n: int, ref: str) -> List[NotificationHistory]: + notifications = [ + NotificationHistory( + id=create_uuid(), + created_at=datetime.utcnow(), + template_id=Config.NEW_USER_EMAIL_VERIFICATION_TEMPLATE_ID, + template_version=1, + service_id=Config.NOTIFY_SERVICE_ID, + notification_type="email", + key_type='normal', + client_reference=ref, + ) + for _ in range(n) + ] + return notifications + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("-n", "--notifications", default=1, type=int, help="number of notifications to add to the notification_history table (default 1)") + parser.add_argument("-r", "--reference", default="manually created", type=str, help="client reference to use for the notifications (default 'manually created')") + parser.add_argument("-c", "--chunksize", default=DEFAULT_CHUNK_SIZE, type=int, help=f"chunk size for bulk_save_objects (default {DEFAULT_CHUNK_SIZE})") + args = parser.parse_args() + + app = Flask("enlarge_db") + create_app(app) + + for notifications_done in range(0, args.notifications, args.chunksize): + notifications = create_notifications(min(args.chunksize, args.notifications - notifications_done), args.reference) + print(f"Adding {len(notifications)} notifications to notification_history") + with app.app_context(): + try: + db.session.bulk_save_objects(notifications) + db.session.commit() + except Exception as e: + print(f"Error adding notifications: {e}") + db.session.rollback() + sys.exit(1) + print(f"Done {notifications_done+len(notifications)} / {args.notifications}") diff --git a/tests-perf/ops/Dockerfile b/tests-perf/ops/Dockerfile index fad9c6f3ee..ba36582848 100644 --- a/tests-perf/ops/Dockerfile +++ b/tests-perf/ops/Dockerfile @@ -1,10 +1,10 @@ FROM python:3.10-alpine3.16@sha256:afe68972cc00883d70b3760ee0ffbb7375cf09706c122dda7063ffe64c5be21b ENV PYTHONDONTWRITEBYTECODE 1 -ENV POETRY_VERSION "1.3.2" +ENV POETRY_VERSION "1.7.1" ENV APP_VENV="/app/.venv" ENV POETRY_HOME="/opt/poetry" -ENV POETRY_VERSION="1.3.2" +ENV POETRY_VERSION="1.7.1" ENV POETRY_VIRTUALENVS_CREATE="false" ENV PATH="${APP_VENV}/bin:${POETRY_HOME}/bin:$PATH" diff --git a/tests/app/api_key/test_rest.py b/tests/app/api_key/test_rest.py index 0def4b7884..a28985884a 100644 --- a/tests/app/api_key/test_rest.py +++ b/tests/app/api_key/test_rest.py @@ -1,9 +1,6 @@ -from datetime import datetime - import pytest from flask import url_for -from app import DATETIME_FORMAT from app.dao.api_key_dao import get_api_key_by_secret, get_unsigned_secret from app.models import KEY_TYPE_NORMAL from tests import create_sre_authorization_header @@ -33,12 +30,6 @@ def test_get_api_key_stats_with_sends(admin_request, notify_db, notify_db_sessio assert api_key_stats["sms_sends"] == 0 assert api_key_stats["total_sends"] == total_sends - # the following lines test that a send has occurred within the last second - last_send_dt = datetime.strptime(api_key_stats["last_send"], DATETIME_FORMAT) - now = datetime.utcnow() - time_delta = now - last_send_dt - assert abs(time_delta.total_seconds()) < 1 - def test_get_api_key_stats_no_sends(admin_request, notify_db, notify_db_session): service = create_service(service_name="Service 2") diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index 1b4f5b4e31..437d59e7ec 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -1328,12 +1328,11 @@ def test_save_sms_should_save_default_smm_sender_notification_reply_to_text_on(s def test_should_save_sms_template_to_and_persist_with_job_id(self, notify_api, sample_job, mocker): notification = _notification_json(sample_job.template, to="+1 650 253 2222", job_id=sample_job.id, row_number=2) mocker.patch("app.celery.provider_tasks.deliver_sms.apply_async") - mock_over_daily_limit = mocker.patch("app.celery.tasks.check_service_over_daily_message_limit") notification_id = uuid.uuid4() now = datetime.utcnow() - with set_config_values(notify_api, {"FF_EMAIL_DAILY_LIMIT": False}): - save_smss(sample_job.template.service_id, [signer_notification.sign(notification)], notification_id) + + save_smss(sample_job.template.service_id, [signer_notification.sign(notification)], notification_id) persisted_notification = Notification.query.one() assert persisted_notification.to == "+1 650 253 2222" assert persisted_notification.job_id == sample_job.id @@ -1350,7 +1349,6 @@ def test_should_save_sms_template_to_and_persist_with_job_id(self, notify_api, s provider_tasks.deliver_sms.apply_async.assert_called_once_with( [str(persisted_notification.id)], queue=QueueNames.SEND_SMS_MEDIUM ) - mock_over_daily_limit.assert_called_once_with("normal", sample_job.service) def test_save_sms_should_go_to_retry_queue_if_database_errors(self, sample_template, mocker): notification = _notification_json(sample_template, "+1 650 253 2222") @@ -1672,7 +1670,6 @@ def test_should_use_email_template_and_persist( self, notify_api, sample_email_template_with_placeholders, sample_api_key, mocker ): mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") - mock_over_daily_limit = mocker.patch("app.celery.tasks.check_service_over_daily_message_limit") now = datetime(2016, 1, 1, 11, 9, 0) notification_id = uuid.uuid4() @@ -1686,10 +1683,9 @@ def test_should_use_email_template_and_persist( ) with freeze_time("2016-01-01 11:10:00.00000"): - with set_config_values(notify_api, {"FF_EMAIL_DAILY_LIMIT": False}): - save_emails( - sample_email_template_with_placeholders.service_id, [signer_notification.sign(notification)], notification_id - ) + save_emails( + sample_email_template_with_placeholders.service_id, [signer_notification.sign(notification)], notification_id + ) persisted_notification = Notification.query.one() assert persisted_notification.to == "my_email@my_email.com" @@ -1709,7 +1705,6 @@ def test_should_use_email_template_and_persist( provider_tasks.deliver_email.apply_async.assert_called_once_with( [str(persisted_notification.id)], queue=QueueNames.SEND_EMAIL_MEDIUM ) - mock_over_daily_limit.assert_called_once_with("normal", sample_email_template_with_placeholders.service) def test_save_email_should_use_template_version_from_job_not_latest(self, sample_email_template, mocker): notification = _notification_json(sample_email_template, "my_email@my_email.com") diff --git a/tests/app/dao/test_api_key_dao.py b/tests/app/dao/test_api_key_dao.py index 9535ce0343..5e23002171 100644 --- a/tests/app/dao/test_api_key_dao.py +++ b/tests/app/dao/test_api_key_dao.py @@ -14,6 +14,7 @@ resign_api_keys, save_model_api_key, update_compromised_api_key_info, + update_last_used_api_key, ) from app.models import KEY_TYPE_NORMAL, ApiKey from tests.app.db import create_api_key @@ -35,6 +36,7 @@ def test_save_api_key_should_create_new_api_key_and_history(sample_service): assert len(all_api_keys) == 1 assert all_api_keys[0] == api_key assert api_key.version == 1 + assert api_key.last_used_timestamp is None all_history = api_key.get_history_model().query.all() assert len(all_history) == 1 @@ -60,6 +62,17 @@ def test_expire_api_key_should_update_the_api_key_and_create_history_record(noti sorted_all_history[1].version = 2 +def test_last_used_should_update_the_api_key_and_not_create_history_record(notify_api, sample_api_key): + last_used = datetime.utcnow() + update_last_used_api_key(api_key_id=sample_api_key.id, last_used=last_used) + all_api_keys = get_model_api_keys(service_id=sample_api_key.service_id) + assert len(all_api_keys) == 1 + assert all_api_keys[0].last_used_timestamp == last_used + + all_history = sample_api_key.get_history_model().query.all() + assert len(all_history) == 1 + + def test_update_compromised_api_key_info_and_create_history_record(notify_api, sample_api_key): update_compromised_api_key_info( service_id=sample_api_key.service_id, api_key_id=sample_api_key.id, compromised_info={"key": "value"} diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index 6e6087d2cc..ce10e24777 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -331,6 +331,39 @@ def test_fetch_notification_status_by_template_for_service_for_today_and_7_previ ] == sorted(results, key=lambda x: (x.notification_type, x.status, x.template_name, x.count)) +@freeze_time("2018-10-31T18:00:00") +def test_fetch_notification_status_gets_data_from_correct_timeframe( + notify_db_session, +): + service_1 = create_service(service_name="service_1") + sms_template = create_template(service=service_1, template_type=SMS_TYPE) + email_template = create_template(service=service_1, template_type=EMAIL_TYPE) + + # create notifications for every hour of the day + for i in range(24): + save_notification(create_notification(email_template, created_at=datetime(2018, 10, 30, i, 0, 0), status="delivered")) + save_notification(create_notification(email_template, created_at=datetime(2018, 10, 30, i, 0, 59), status="delivered")) + save_notification(create_notification(sms_template, created_at=datetime(2018, 10, 30, i, 0, 0), status="delivered")) + save_notification(create_notification(sms_template, created_at=datetime(2018, 10, 30, i, 0, 30), status="delivered")) + save_notification(create_notification(sms_template, created_at=datetime(2018, 10, 30, i, 0, 59), status="delivered")) + + # too early, shouldn't be included + save_notification( + create_notification( + service_1.templates[0], + created_at=datetime(2018, 10, 29, 23, 59, 59), + status="delivered", + ) + ) + data = fetch_notification_status_for_day(process_day=datetime.utcnow() - timedelta(days=1)) + + assert data[0].notification_type == "email" + assert data[0].notification_count == 48 + + assert data[1].notification_type == "sms" + assert data[1].notification_count == 72 + + def test_get_total_notifications_sent_for_api_key(notify_db_session): service = create_service(service_name="First Service") api_key = create_api_key(service) @@ -356,24 +389,20 @@ def test_get_total_notifications_sent_for_api_key(notify_db_session): assert dict(api_key_stats_3) == dict([(EMAIL_TYPE, total_sends), (SMS_TYPE, total_sends)]) +def test_get_last_send_for_api_key_check_last_used(notify_db_session): + service = create_service(service_name="First Service") + api_key = create_api_key(service, last_used=datetime.utcnow()) + + last_send = get_last_send_for_api_key(str(api_key.id))[0][0] + assert last_send == api_key.last_used_timestamp + + def test_get_last_send_for_api_key(notify_db_session): service = create_service(service_name="First Service") api_key = create_api_key(service) - template_email = create_template(service=service, template_type=EMAIL_TYPE) - total_sends = 10 - last_send = get_last_send_for_api_key(str(api_key.id)) assert last_send == [] - for x in range(total_sends): - save_notification(create_notification(template=template_email, api_key=api_key)) - - # the following lines test that a send has occurred within the last second - last_send = get_last_send_for_api_key(str(api_key.id))[0][0] - now = datetime.utcnow() - time_delta = now - last_send - assert abs(time_delta.total_seconds()) < 1 - def test_get_api_key_ranked_by_notifications_created(notify_db_session): service = create_service(service_name="Service 1") @@ -415,24 +444,24 @@ def test_get_api_key_ranked_by_notifications_created(notify_db_session): assert int(second_place[8]) == sms_sends -def test_last_used_for_api_key(notify_db_session): - service = create_service(service_name="Service 1") - api_key_1 = create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name="Key 1") - api_key_2 = create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name="Key 2") - api_key_3 = create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name="Key 3") - template_email = create_template(service=service, template_type="email") - create_notification_history(template=template_email, api_key=api_key_1, created_at="2022-03-04") - save_notification(create_notification(template=template_email, api_key=api_key_1, created_at="2022-03-05")) +# def test_last_used_for_api_key(notify_db_session): +# service = create_service(service_name="Service 1") +# api_key_1 = create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name="Key 1") +# api_key_2 = create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name="Key 2") +# api_key_3 = create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name="Key 3") +# template_email = create_template(service=service, template_type="email") +# create_notification_history(template=template_email, api_key=api_key_1, created_at="2022-03-04") +# save_notification(create_notification(template=template_email, api_key=api_key_1, created_at="2022-03-05")) - assert (get_last_send_for_api_key(str(api_key_1.id))[0][0]).strftime("%Y-%m-%d") == "2022-03-05" +# assert (get_last_send_for_api_key(str(api_key_1.id))[0][0]).strftime("%Y-%m-%d") == "2022-03-05" - save_notification(create_notification(template=template_email, api_key=api_key_2, created_at="2022-03-06")) +# save_notification(create_notification(template=template_email, api_key=api_key_2, created_at="2022-03-06")) - assert (get_last_send_for_api_key(str(api_key_2.id))[0][0]).strftime("%Y-%m-%d") == "2022-03-06" +# assert (get_last_send_for_api_key(str(api_key_2.id))[0][0]).strftime("%Y-%m-%d") == "2022-03-06" - create_notification_history(template=template_email, api_key=api_key_3, created_at="2022-03-07") +# create_notification_history(template=template_email, api_key=api_key_3, created_at="2022-03-07") - assert (get_last_send_for_api_key(str(api_key_3.id))[0][0]).strftime("%Y-%m-%d") == "2022-03-07" +# assert (get_last_send_for_api_key(str(api_key_3.id))[0][0]).strftime("%Y-%m-%d") == "2022-03-07" @pytest.mark.parametrize( @@ -1150,29 +1179,22 @@ def test_fetch_notification_status_for_service_for_today_handles_midnight_utc( email_template = create_template(service=service_1, template_type=EMAIL_TYPE) # create notifications that should not be included in today's count - create_ft_notification_status(date(2018, 10, 29), "email", service_1, count=30) + create_ft_notification_status(date(2018, 10, 24), "email", service_1, count=30) + create_ft_notification_status(date(2018, 10, 31), "email", service_1, count=20) + save_notification(create_notification(email_template, created_at=datetime(2018, 10, 31, 0, 0, 0), status="delivered")) save_notification(create_notification(email_template, created_at=datetime(2018, 10, 31, 11, 59, 59), status="delivered")) save_notification(create_notification(email_template, created_at=datetime(2018, 10, 31, 11, 59, 59), status="delivered")) save_notification(create_notification(email_template, created_at=datetime(2018, 10, 31, 23, 59, 59), status="delivered")) # create notifications that should be included in count - create_ft_notification_status(date(2018, 10, 31), "email", service_1, count=5) - create_ft_notification_status(date(2018, 10, 30), "email", service_1, count=5) - save_notification(create_notification(email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status="delivered")) - save_notification(create_notification(email_template, created_at=datetime(2018, 10, 31, 6, 0, 0), status="delivered")) - save_notification(create_notification(email_template, created_at=datetime(2018, 11, 1, 22, 59, 59), status="delivered")) + save_notification(create_notification(email_template, created_at=datetime(2018, 11, 1, 13, 0, 0), status="delivered")) + save_notification(create_notification(email_template, created_at=datetime(2018, 11, 1, 6, 0, 0), status="delivered")) + save_notification(create_notification(email_template, created_at=datetime(2018, 11, 1, 17, 59, 59), status="delivered")) - # checking the daily stats for this day should give us the 2 created after 12am UTC + # checking the daily stats for this day should give us the 3 created after 12am UTC results = sorted( fetch_notification_status_for_service_for_today_and_7_previous_days(service_1.id, limit_days=1), key=lambda x: (x.notification_type, x.status), ) - assert results[0][2] == 6 - - # checking the daily stats for the last 2 days should give us the 2 created after 12am UTC and the 1 from the day before - results = sorted( - fetch_notification_status_for_service_for_today_and_7_previous_days(service_1.id, limit_days=2), - key=lambda x: (x.notification_type, x.status), - ) - assert results[0][2] == 11 + assert results[0][2] == 3 diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index 4a81ea4f86..46941c9887 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -46,6 +46,7 @@ from app.models import ( EMAIL_TYPE, INTERNATIONAL_SMS_TYPE, + JOB_STATUS_SCHEDULED, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, @@ -68,6 +69,7 @@ user_folder_permissions, ) from app.schemas import service_schema +from tests.app.conftest import create_sample_job from tests.app.db import ( create_annual_billing, create_api_key, @@ -1396,7 +1398,7 @@ def create_email_sms_letter_template(): class TestServiceEmailLimits: - def test_get_email_count_for_service(self, notify_db_session): + def test_get_email_count_for_service(self): active_user_1 = create_user(email="active1@foo.com", state="active") service = Service( name="service_name", @@ -1432,7 +1434,98 @@ def test_dao_fetch_todays_total_message_count_returns_0_with_yesterday_messages( notification = save_notification( create_notification( created_at=yesterday, - template=create_template(service=create_service(service_name="tester"), template_type="email"), + template=create_template(service=create_service(service_name="tester123"), template_type="email"), ) ) assert fetch_todays_total_message_count(notification.service.id) == 0 + + def test_dao_fetch_todays_total_message_count_counts_notifications_in_jobs_scheduled_for_today( + self, notify_db, notify_db_session + ): + service = create_service(service_name="tester12") + template = create_template(service=service, template_type="email") + today = datetime.utcnow().date() + + create_sample_job( + notify_db, + notify_db_session, + service=service, + template=template, + scheduled_for=today, + job_status=JOB_STATUS_SCHEDULED, + notification_count=10, + ) + save_notification( + create_notification( + created_at=today, + template=template, + ) + ) + assert fetch_todays_total_message_count(service.id) == 11 + + def test_dao_fetch_todays_total_message_count_counts_notifications_in_jobs_scheduled_for_today_but_not_after_today( + self, notify_db, notify_db_session + ): + service = create_service() + template = create_template(service=service, template_type="email") + today = datetime.utcnow().date() + + create_sample_job( + notify_db, + notify_db_session, + service=service, + template=template, + scheduled_for=today, + job_status=JOB_STATUS_SCHEDULED, + notification_count=10, + ) + save_notification( + create_notification( + created_at=today, + template=template, + ) + ) + create_sample_job( + notify_db, + notify_db_session, + service=service, + template=template, + scheduled_for=today + timedelta(days=1), + job_status=JOB_STATUS_SCHEDULED, + notification_count=10, + ) + + assert fetch_todays_total_message_count(service.id) == 11 + + def test_dao_fetch_todays_total_message_count_counts_notifications_in_jobs_scheduled_for_today_but_not_before_today( + self, notify_db, notify_db_session + ): + service = create_service() + template = create_template(service=service, template_type="email") + today = datetime.utcnow().date() + + create_sample_job( + notify_db, + notify_db_session, + service=service, + template=template, + scheduled_for=today, + job_status=JOB_STATUS_SCHEDULED, + notification_count=10, + ) + create_sample_job( + notify_db, + notify_db_session, + service=service, + template=template, + scheduled_for=today - timedelta(days=1), + job_status=JOB_STATUS_SCHEDULED, + notification_count=10, + ) + save_notification( + create_notification( + created_at=today, + template=template, + ) + ) + assert fetch_todays_total_message_count(service.id) == 11 diff --git a/tests/app/db.py b/tests/app/db.py index 9e79f5042c..1dacec37bc 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -551,7 +551,7 @@ def create_letter_rate( return rate -def create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name=None): +def create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name=None, last_used=None): id_ = uuid.uuid4() name = key_name if key_name else "{} api key {}".format(key_type, id_) @@ -563,6 +563,7 @@ def create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name=None): key_type=key_type, id=id_, secret=uuid.uuid4(), + last_used_timestamp=last_used, ) db.session.add(api_key) db.session.commit() diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index 57cd6fba41..018df49123 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -884,6 +884,99 @@ def test_send_email_to_provider_should_format_email_address(sample_email_notific ) +def test_file_attachment_retry(mocker, notify_db, notify_db_session): + template = create_sample_email_template(notify_db, notify_db_session, content="Here is your ((file))") + + class mock_response: + status_code = 200 + + def json(): + return {"av-status": "clean"} + + mocker.patch("app.delivery.send_to_providers.document_download_client.check_scan_verdict", return_value=mock_response) + + personalisation = { + "file": document_download_response( + { + "direct_file_url": "http://foo.bar/direct_file_url", + "url": "http://foo.bar/url", + "mime_type": "application/pdf", + } + ) + } + personalisation["file"]["document"]["sending_method"] = "attach" + personalisation["file"]["document"]["filename"] = "file.txt" + personalisation["file"]["document"]["id"] = "1234" + + db_notification = save_notification(create_notification(template=template, personalisation=personalisation)) + + mocker.patch("app.delivery.send_to_providers.statsd_client") + mocker.patch("app.aws_ses_client.send_email", return_value="reference") + + # When a urllib3 request attempts retries and fails it will wrap the offending exception in a MaxRetryError + # thus we'll capture the logged exception and assert it's a MaxRetryError to verify that retries were attempted + mock_logger = mocker.patch("app.delivery.send_to_providers.current_app.logger.error") + logger_args = [] + + def mock_error(*args): + logger_args.append(args) + + mock_logger.side_effect = mock_error + + class MockHTTPResponse: + def __init__(self, status): + self.status = status + self.data = b"file content" if status == 200 else b"" + + mock_http = mocker.patch("urllib3.PoolManager") + mock_http.return_value.request.side_effect = [ + MockHTTPResponse(500), + MockHTTPResponse(500), + MockHTTPResponse(500), + MockHTTPResponse(500), + MockHTTPResponse(500), + ] + + send_to_providers.send_email_to_provider(db_notification) + exception = logger_args[0][0].split("Exception: ")[1] + assert mock_logger.call_count == 1 + assert "Max retries exceeded" in exception + + +def test_file_attachment_max_retries(mocker, notify_db, notify_db_session): + template = create_sample_email_template(notify_db, notify_db_session, content="Here is your ((file))") + + class mock_response: + status_code = 200 + + def json(): + return {"av-status": "clean"} + + mocker.patch("app.delivery.send_to_providers.document_download_client.check_scan_verdict", return_value=mock_response) + + personalisation = { + "file": document_download_response( + { + "direct_file_url": "http://foo.bar/direct_file_url", + "url": "http://foo.bar/url", + "mime_type": "application/pdf", + } + ) + } + personalisation["file"]["document"]["sending_method"] = "attach" + personalisation["file"]["document"]["filename"] = "file.txt" + + db_notification = save_notification(create_notification(template=template, personalisation=personalisation)) + + mocker.patch("app.delivery.send_to_providers.statsd_client") + mocker.patch("app.aws_ses_client.send_email", return_value="reference") + + mock_logger = mocker.patch("app.delivery.send_to_providers.current_app.logger.error") + send_to_providers.send_email_to_provider(db_notification) + assert mock_logger.call_count == 1 + assert "Max retries exceeded" in mock_logger.call_args[0][0] + + @pytest.mark.parametrize( "filename_attribute_present, filename, expected_filename", [ @@ -930,28 +1023,26 @@ def json(): statsd_mock = mocker.patch("app.delivery.send_to_providers.statsd_client") send_mock = mocker.patch("app.aws_ses_client.send_email", return_value="reference") - request_mock = mocker.patch( - "app.delivery.send_to_providers.urllib.request.Request", - return_value="request_mock", + mocker.patch("app.delivery.send_to_providers.Retry") + + response_return_mock = MagicMock() + response_return_mock.status = 200 + response_return_mock.data = "Hello there!" + + response_mock = mocker.patch( + "app.delivery.send_to_providers.PoolManager.request", + return_value=response_return_mock, ) - # See https://stackoverflow.com/a/34929900 - cm = MagicMock() - cm.read.return_value = "request_content" - cm.__enter__.return_value = cm - cm.getcode = lambda: 200 - urlopen_mock = mocker.patch("app.delivery.send_to_providers.urllib.request.urlopen") - urlopen_mock.return_value = cm send_to_providers.send_email_to_provider(db_notification) attachments = [] if filename_attribute_present: - request_mock.assert_called_once_with("http://foo.bar/direct_file_url") - urlopen_mock.assert_called_once_with("request_mock") + response_mock.assert_called_with("GET", url="http://foo.bar/direct_file_url") attachments = [ { - "data": "request_content", "name": expected_filename, + "data": "Hello there!", "mime_type": "application/pdf", } ] @@ -1001,12 +1092,7 @@ def test_notification_with_bad_file_attachment_url(mocker, notify_db, notify_db_ db_notification = save_notification(create_notification(template=template, personalisation=personalisation)) - # See https://stackoverflow.com/a/34929900 - cm = MagicMock() - cm.read.return_value = "request_content" - cm.__enter__.return_value = cm - urlopen_mock = mocker.patch("app.delivery.send_to_providers.urllib.request.urlopen") - urlopen_mock.return_value = cm + mocker.patch("app.delivery.send_to_providers.Retry") with pytest.raises(InvalidUrlException): send_to_providers.send_email_to_provider(db_notification) diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index e184da8199..76f89ebdf9 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -125,6 +125,71 @@ def test_cancel_letter_job_does_not_call_cancel_if_can_letter_job_be_cancelled_r assert response["message"] == "Sorry, it's too late, letters have already been sent." +def test_create_unscheduled_email_job_increments_daily_count(client, mocker, sample_email_job, fake_uuid): + mocker.patch("app.celery.tasks.process_job.apply_async") + mocker.patch("app.job.rest.increment_email_daily_count_send_warnings_if_needed") + mocker.patch( + "app.job.rest.get_job_metadata_from_s3", + return_value={ + "template_id": sample_email_job.template_id, + "original_file_name": sample_email_job.original_file_name, + "notification_count": "1", + "valid": "True", + }, + ) + mocker.patch( + "app.job.rest.get_job_from_s3", + return_value="email address\r\nsome@email.com", + ) + mocker.patch("app.dao.services_dao.dao_fetch_service_by_id", return_value=sample_email_job.service) + data = { + "id": fake_uuid, + "created_by": str(sample_email_job.created_by.id), + } + path = "/service/{}/job".format(sample_email_job.service_id) + auth_header = create_authorization_header() + headers = [("Content-Type", "application/json"), auth_header] + + response = client.post(path, data=json.dumps(data), headers=headers) + + assert response.status_code == 201 + + app.celery.tasks.process_job.apply_async.assert_called_once_with(([str(fake_uuid)]), queue="job-tasks") + app.job.rest.increment_email_daily_count_send_warnings_if_needed.assert_called_once_with(sample_email_job.service, 1) + + +def test_create_future_not_same_day_scheduled_email_job_does_not_increment_daily_count( + client, mocker, sample_email_job, fake_uuid +): + scheduled_date = (datetime.utcnow() + timedelta(hours=36, minutes=59)).isoformat() + mocker.patch("app.celery.tasks.process_job.apply_async") + mocker.patch("app.job.rest.increment_email_daily_count_send_warnings_if_needed") + mocker.patch( + "app.job.rest.get_job_metadata_from_s3", + return_value={ + "template_id": sample_email_job.template_id, + "original_file_name": sample_email_job.original_file_name, + "notification_count": "1", + "valid": "True", + }, + ) + mocker.patch( + "app.job.rest.get_job_from_s3", + return_value="email address\r\nsome@email.com", + ) + mocker.patch("app.dao.services_dao.dao_fetch_service_by_id", return_value=sample_email_job.service) + data = {"id": fake_uuid, "created_by": str(sample_email_job.created_by.id), "scheduled_for": scheduled_date} + path = "/service/{}/job".format(sample_email_job.service_id) + auth_header = create_authorization_header() + headers = [("Content-Type", "application/json"), auth_header] + + response = client.post(path, data=json.dumps(data), headers=headers) + + assert response.status_code == 201 + + app.job.rest.increment_email_daily_count_send_warnings_if_needed.assert_not_called() + + def test_create_unscheduled_job(client, sample_template, mocker, fake_uuid): mocker.patch("app.celery.tasks.process_job.apply_async") mocker.patch( @@ -451,6 +516,7 @@ def test_create_job_returns_404_if_template_does_not_exist(client, sample_servic "app.job.rest.get_job_metadata_from_s3", return_value={ "template_id": str(sample_service.id), + "valid": "True", }, ) data = { diff --git a/tests/app/notifications/rest/test_send_notification.py b/tests/app/notifications/rest/test_send_notification.py index b1f797f25a..a1cf1f0d52 100644 --- a/tests/app/notifications/rest/test_send_notification.py +++ b/tests/app/notifications/rest/test_send_notification.py @@ -28,7 +28,11 @@ Template, ) from app.utils import get_document_url -from app.v2.errors import RateLimitError, TooManyRequestsError +from app.v2.errors import ( + RateLimitError, + TooManyRequestsError, + TrialServiceTooManyEmailRequestsError, +) from tests import create_authorization_header from tests.app.conftest import ( create_sample_api_key, @@ -385,7 +389,7 @@ def test_should_block_api_call_if_over_day_limit_for_live_service(notify_db, not with notify_api.test_request_context(): with notify_api.test_client() as client: mocker.patch( - "app.notifications.validators.check_service_over_daily_message_limit", + "app.notifications.validators.check_service_over_api_rate_limit_and_update_rate", side_effect=TooManyRequestsError(1), ) mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") @@ -417,13 +421,14 @@ def test_should_block_api_call_if_over_day_limit_for_live_service(notify_db, not def test_should_block_api_call_if_over_day_limit_for_restricted_service(notify_db, notify_db_session, notify_api, mocker): with notify_api.test_request_context(): with notify_api.test_client() as client: - mocker.patch("app.celery.provider_tasks.deliver_sms.apply_async") + mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") mocker.patch( - "app.notifications.validators.check_service_over_daily_message_limit", - side_effect=TooManyRequestsError(1), + "app.notifications.validators.check_email_daily_limit", + side_effect=TrialServiceTooManyEmailRequestsError(1), ) service = create_sample_service(notify_db, notify_db_session, limit=1, restricted=True) + create_sample_service_safelist(notify_db, notify_db_session, service=service, email_address="ok@ok.com") email_template = create_sample_email_template(notify_db, notify_db_session, service=service) create_sample_notification( notify_db, @@ -461,7 +466,7 @@ def test_should_allow_api_call_if_under_day_limit_regardless_of_type( sms_template = create_sample_template(notify_db, notify_db_session, service=service) create_sample_notification(notify_db, notify_db_session, template=email_template, service=service) - data = {"to": sample_user.mobile_number, "template": str(sms_template.id)} + data = {"to": sample_user.mobile_number, "template": str(sms_template.id), "valid": "True"} auth_header = create_authorization_header(service_id=service.id) diff --git a/tests/app/notifications/test_process_notification.py b/tests/app/notifications/test_process_notification.py index bb194d3c28..eaa0c00da0 100644 --- a/tests/app/notifications/test_process_notification.py +++ b/tests/app/notifications/test_process_notification.py @@ -19,6 +19,7 @@ LETTER_TYPE, NORMAL, PRIORITY, + ApiKey, Notification, NotificationHistory, ScheduledNotification, @@ -433,6 +434,10 @@ def test_persist_notifications_list(self, sample_job, sample_api_key, notify_db_ assert persisted_notification[1].to == "foo2@bar.com" assert persisted_notification[0].service == sample_job.service + # Test that the api key last_used_timestamp got updated + api_key = ApiKey.query.get(sample_api_key.id) + assert api_key.last_used_timestamp is not None + def test_persist_notifications_reply_to_text_is_original_value_if_sender_is_changed_later( self, sample_template, sample_api_key, mocker ): @@ -913,6 +918,8 @@ def test_transform_email_notification_stores_normalised_email( assert persisted_notification.to == recipient assert persisted_notification.normalised_to == expected_recipient_normalised + api_key = ApiKey.query.get(sample_api_key.id) + assert api_key.last_used_timestamp is not None class TestDBSaveAndSendNotification: diff --git a/tests/app/notifications/test_validators.py b/tests/app/notifications/test_validators.py index 24175b431e..f49c4dc6e0 100644 --- a/tests/app/notifications/test_validators.py +++ b/tests/app/notifications/test_validators.py @@ -217,8 +217,7 @@ def test_check_service_message_limit_records_nearing_daily_limit( if limit_type == "sms": increment_sms_daily_count_send_warnings_if_needed(service) else: - with set_config(notify_api, "FF_EMAIL_DAILY_LIMIT", True): - increment_email_daily_count_send_warnings_if_needed(service) + increment_email_daily_count_send_warnings_if_needed(service) assert redis_get.call_args_list == [ call(count_key(limit_type, service.id)), @@ -290,8 +289,7 @@ def test_check_service_message_limit_in_cache_over_message_limit_fails( assert e.value.fields == [] with pytest.raises(TooManyEmailRequestsError) as e: - with set_config(notify_api, "FF_EMAIL_DAILY_LIMIT", True): - check_email_daily_limit(service) + check_email_daily_limit(service) assert e.value.status_code == 429 assert e.value.message == "Exceeded email daily sending limit of 4 messages" assert e.value.fields == [] @@ -350,8 +348,8 @@ def test_check_service_message_limit_skip_statsd_over_message_no_limit_fails_ema # When service = create_sample_service(notify_db, notify_db_session, restricted=True, limit=4, sms_limit=4) - with set_config(notify_api, "FF_EMAIL_DAILY_LIMIT", True): - check_email_daily_limit(service) + check_email_daily_limit(service) + # Then app_statsd.statsd_client.incr.assert_not_called() diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index d5f01b415d..27ded50b48 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1730,6 +1730,35 @@ def test_get_notifications_for_service_without_page_count( assert resp["notifications"][0]["id"] == str(without_job.id) +@freeze_time("2018-11-20T18:00:00") +@pytest.mark.parametrize("retention_period, expected_count_of_notifications", [(3, 72), (7, 168)]) +def test_get_notifications_for_service_gets_data_from_correct_timeframe( + admin_request, sample_service, retention_period, expected_count_of_notifications +): + email_template = create_template(service=sample_service, template_type=EMAIL_TYPE) + + # WEEK BEFORE + # Create 12 notifications for each hour of the day for 1 week + for i in range(retention_period): + for j in range(24): + save_notification( + create_notification(email_template, created_at=datetime(2018, 11, 5 + i, j, 0, 0), status="delivered") + ) + + # THIS WEEK + # Create 12 notifications for each hour of the day for 1 week + for i in range(retention_period): + for j in range(24): + save_notification( + create_notification(email_template, created_at=datetime(2018, 11, 13 + i, j, 0, 0), status="delivered") + ) + + resp = admin_request.get( + "service.get_all_notifications_for_service", service_id=email_template.service_id, limit_days=7, page_size=1 + ) + assert resp["total"] == expected_count_of_notifications + + @pytest.mark.parametrize( "should_prefix", [ @@ -2225,9 +2254,7 @@ def test_update_service_updating_daily_limit_sends_notification_to_users( if expected_call: send_notification_mock.assert_called_once_with( service_id=service.id, - template_id=current_app.config["DAILY_EMAIL_LIMIT_UPDATED_TEMPLATE_ID"] - if current_app.config["FF_EMAIL_DAILY_LIMIT"] - else current_app.config["DAILY_LIMIT_UPDATED_TEMPLATE_ID"], + template_id=current_app.config["DAILY_EMAIL_LIMIT_UPDATED_TEMPLATE_ID"], personalisation={ "service_name": service.name, "message_limit_en": "{:,}".format(new_limit), diff --git a/tests/app/service/test_send_one_off_notification.py b/tests/app/service/test_send_one_off_notification.py index 4b8de0890c..b9dc37cfd5 100644 --- a/tests/app/service/test_send_one_off_notification.py +++ b/tests/app/service/test_send_one_off_notification.py @@ -21,7 +21,6 @@ BadRequestError, LiveServiceTooManyEmailRequestsError, LiveServiceTooManySMSRequestsError, - TooManyRequestsError, ) from tests.app.db import ( create_letter_contact, @@ -31,7 +30,6 @@ create_template, create_user, ) -from tests.conftest import set_config_values @pytest.fixture @@ -282,12 +280,12 @@ def test_send_one_off_notification_raises_if_cant_send_to_recipient( assert "service is in trial mode" in e.value.message -def test_send_one_off_notification_raises_if_over_combined_limit(notify_db_session, mocker): +def test_send_one_off_notification_raises_if_over_combined_limit(notify_db_session, notify_api, mocker): service = create_service(message_limit=0) template = create_template(service=service) mocker.patch( - "app.service.send_notification.check_service_over_daily_message_limit", - side_effect=TooManyRequestsError(1), + "app.service.send_notification.check_sms_daily_limit", + side_effect=LiveServiceTooManySMSRequestsError(1), ) post_data = { @@ -296,7 +294,7 @@ def test_send_one_off_notification_raises_if_over_combined_limit(notify_db_sessi "created_by": str(service.created_by_id), } - with pytest.raises(TooManyRequestsError): + with pytest.raises(LiveServiceTooManySMSRequestsError): send_one_off_notification(service.id, post_data) @@ -314,9 +312,8 @@ def test_send_one_off_notification_raises_if_over_email_limit(notify_db_session, "created_by": str(service.created_by_id), } - with set_config_values(notify_api, {"FF_EMAIL_DAILY_LIMIT": True}): - with pytest.raises(LiveServiceTooManyEmailRequestsError): - send_one_off_notification(service.id, post_data) + with pytest.raises(LiveServiceTooManyEmailRequestsError): + send_one_off_notification(service.id, post_data) def test_send_one_off_notification_raises_if_over_sms_daily_limit(notify_db_session, mocker): diff --git a/tests_cypress/config.js b/tests_cypress/config.js index 5cd94f0e18..5ea2ae52d8 100644 --- a/tests_cypress/config.js +++ b/tests_cypress/config.js @@ -1,3 +1,33 @@ +let PRODUCTION = { + CONFIG_NAME: "PRODUCTION", + Hostnames: { + API: 'https://api.notification.canada.ca', + Admin: 'https://notification.canada.ca', + DDAPI: 'https://api.document.notification.canada.ca', + }, + Services: { + Notify: 'd6aa2c68-a2d9-4437-ab19-3ae8eb202553', + Cypress: '5c8a0501-2aa8-433a-ba51-cefb8063ab93' + }, + Templates: { + 'FILE_ATTACH_TEMPLATE_ID': 'ee6e4f6e-df3c-49b5-82de-eca5122ce965', + 'SIMPLE_EMAIL_TEMPLATE_ID': 'bcc5ff84-4f20-4714-ac8e-7c5bd91c49a6', + 'VARIABLES_EMAIL_TEMPLATE_ID': '0f10fca7-a5e8-4c03-bc96-610072f236eb', + 'SMOKE_TEST_EMAIL': 'be04f866-2302-4b76-8efd-2dec1e853c7d', + 'SMOKE_TEST_EMAIL_BULK': '6978ecf0-8049-47ca-b5af-f010796e8805', + 'SMOKE_TEST_EMAIL_ATTACH': 'ee6e4f6e-df3c-49b5-82de-eca5122ce965', + 'SMOKE_TEST_EMAIL_LINK': '8bac2ff9-32e6-4e19-bf80-4218ce4789fd', + 'SMOKE_TEST_SMS': 'f718f471-d940-44f6-9841-93f61da9b4f7' + }, + Users: { + Team: ['andrew.leith+2@cds-snc.ca'], + NonTeam: ['person@example.com'], + Simulated: ['simulate-delivered-2@notification.canada.ca', 'simulate-delivered-3@notification.canada.ca', 'success@simulator.amazonses.com'], + SimulatedPhone: ['+16132532222', '+16132532223', '+16132532224'] + }, + +} + let STAGING = { CONFIG_NAME: "STAGING", Hostnames: { @@ -69,9 +99,10 @@ let LOCAL = { const config = { STAGING, LOCAL, + PRODUCTION }; // choose which config to use here -const ConfigToUse = config.STAGING; +const ConfigToUse = config.LOCAL; module.exports = ConfigToUse; diff --git a/tests_cypress/cypress/Notify/NotifyAPI.js b/tests_cypress/cypress/Notify/NotifyAPI.js index 02ec5afbe5..400dd1b96b 100644 --- a/tests_cypress/cypress/Notify/NotifyAPI.js +++ b/tests_cypress/cypress/Notify/NotifyAPI.js @@ -26,8 +26,7 @@ const Utilities = { }; const Admin = { SendOneOff: ({to, template_id}) => { - - var token = Utilities.CreateJWT(Cypress.env('ADMIN_USERNAME'), Cypress.env('ADMIN_SECRET')); + var token = Utilities.CreateJWT(Cypress.env('ADMIN_USERNAME'), Cypress.env(config.CONFIG_NAME).ADMIN_SECRET); return cy.request({ url: `/service/${config.Services.Cypress}/send-notification`, method: 'POST', @@ -37,7 +36,7 @@ const Admin = { body: { 'to': to, 'template_id': template_id, - 'created_by': Cypress.env('NOTIFY_USER_ID'), + 'created_by': Cypress.env(config.CONFIG_NAME).NOTIFY_USER_ID, } }); } @@ -117,7 +116,7 @@ const API = { }); }, CreateAPIKey: ({ service_id, key_type, name }) => { - var token = Utilities.CreateJWT(Cypress.env('ADMIN_USERNAME'), Cypress.env('ADMIN_SECRET')); + var token = Utilities.CreateJWT(Cypress.env('ADMIN_USERNAME'), Cypress.env(config.CONFIG_NAME).ADMIN_SECRET); return cy.request({ url: `/service/${service_id}/api-key`, method: 'POST', @@ -127,12 +126,13 @@ const API = { body: { key_type: key_type, name: name, - created_by: Cypress.env('NOTIFY_USER_ID'), + created_by: Cypress.env(config.CONFIG_NAME).NOTIFY_USER_ID, } }); }, RevokeAPIKey: ({ token, type, url, source, failOnStatusCode = true }) => { - var jwt_token = Utilities.CreateJWT(Cypress.env('SRE_USERNAME'), Cypress.env('SRE_SECRET')); + + var jwt_token = Utilities.CreateJWT(Cypress.env('SRE_USERNAME'), Cypress.env(config.CONFIG_NAME).SRE_SECRET); cy.request({ url: `/sre-tools/api-key-revoke`, method: 'POST', @@ -148,7 +148,7 @@ const API = { }); }, RevokeAPIKeyWithAdminAuth: ({ token, type, url, source, failOnStatusCode = true }) => { - var jwt_token = Utilities.CreateJWT(Cypress.env('ADMIN_USERNAME'), Cypress.env('ADMIN_SECRET')); + var jwt_token = Utilities.CreateJWT(Cypress.env('ADMIN_USERNAME'),Cypress.env(config.CONFIG_NAME).ADMIN_SECRET); return cy.request({ url: `/sre-tools/api-key-revoke`, method: 'POST', diff --git a/tests_cypress/cypress/e2e/api/all.cy.js b/tests_cypress/cypress/e2e/api/all.cy.js index 3a52c1c0d7..48c595e245 100644 --- a/tests_cypress/cypress/e2e/api/all.cy.js +++ b/tests_cypress/cypress/e2e/api/all.cy.js @@ -1,2 +1,3 @@ import './email_notifications.cy'; import './file_attach.cy'; +import './sre_tools.cy'; \ No newline at end of file diff --git a/tests_cypress/package-lock.json b/tests_cypress/package-lock.json index d96f723791..d6f9b3c501 100644 --- a/tests_cypress/package-lock.json +++ b/tests_cypress/package-lock.json @@ -186,40 +186,6 @@ "strip-ansi": "^7.0.1" } }, - "string-width-cjs": { - "version": "npm:string-width@4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, "strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -229,23 +195,6 @@ "ansi-regex": "^6.0.1" } }, - "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - } - } - }, "wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -256,60 +205,6 @@ "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } - }, - "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } } } }, @@ -340,9 +235,9 @@ } }, "@types/node": { - "version": "16.18.40", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.40.tgz", - "integrity": "sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==", + "version": "16.18.76", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.76.tgz", + "integrity": "sha512-/GsO2uv1Z6R42lBr59dtem56gVF/yHKQaScggwU+gLU6DXE25sDmOar4c4IfWb3h+X/7OYZznPOFk7oGF3jQSA==", "dev": true }, "@types/sinonjs__fake-timers": { @@ -352,15 +247,15 @@ "dev": true }, "@types/sizzle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", + "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", "dev": true }, "@types/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, "optional": true, "requires": { @@ -465,9 +360,9 @@ "dev": true }, "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, "asynckit": { @@ -489,9 +384,9 @@ "dev": true }, "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, "axe-core": { @@ -565,19 +460,20 @@ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", + "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", "dev": true }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" } }, "caseless": { @@ -614,9 +510,9 @@ "dev": true }, "ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true }, "clean-stack": { @@ -670,9 +566,9 @@ "dev": true }, "colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, "combined-stream": { @@ -719,12 +615,12 @@ } }, "cypress": { - "version": "12.17.3", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.3.tgz", - "integrity": "sha512-/R4+xdIDjUSLYkiQfwJd630S81KIgicmQOLXotFxVXkl+eTeVO+3bHXxdi5KBh/OgC33HWN33kHX+0tQR/ZWpg==", + "version": "12.17.4", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.4.tgz", + "integrity": "sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ==", "dev": true, "requires": { - "@cypress/request": "^2.88.11", + "@cypress/request": "2.88.12", "@cypress/xvfb": "^1.2.4", "@types/node": "^16.18.39", "@types/sinonjs__fake-timers": "8.1.1", @@ -759,6 +655,7 @@ "minimist": "^1.2.8", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", + "process": "^0.11.10", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", "semver": "^7.5.3", @@ -796,9 +693,9 @@ } }, "dayjs": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", - "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==", + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", "dev": true }, "debug": { @@ -808,6 +705,14 @@ "dev": true, "requires": { "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "deepmerge": { @@ -815,6 +720,17 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" }, + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -899,12 +815,13 @@ } }, "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "requires": { - "ansi-colors": "^4.1.1" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" } }, "entities": { @@ -1060,6 +977,14 @@ "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } } }, "fs.realpath": { @@ -1069,21 +994,21 @@ "dev": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-stream": { @@ -1128,35 +1053,44 @@ } }, "global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", "dev": true, "requires": { "ini": "2.0.0" } }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "get-intrinsic": "^1.1.3" } }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.2" + } + }, "has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", @@ -1169,6 +1103,15 @@ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1484,17 +1427,31 @@ "requires": { "graceful-fs": "^4.1.6", "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } } }, "jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "requires": { "jws": "^3.2.2", - "lodash": "^4.17.21", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", "ms": "^2.1.1", - "semver": "^7.3.8" + "semver": "^7.5.4" } }, "jsprim": { @@ -1551,9 +1508,9 @@ "integrity": "sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==" }, "libmime": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.0.tgz", - "integrity": "sha512-X2U5Wx0YmK0rXFbk67ASMeqYIkZ6E5vY7pNWRKtnNzqjvdYYG8xtPDpCnuUEnPU9vlgNev+JoSrcaKSUaNvfsw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.1.tgz", + "integrity": "sha512-A0z9O4+5q+ZTj7QwNe/Juy1KARNb4WaviO4mYeFC4b8dBT2EEqK2pkM+GC8MVnkOjqhl5nYQxRgnPYRRTNmuSQ==", "requires": { "encoding-japanese": "2.0.0", "iconv-lite": "0.6.3", @@ -1567,11 +1524,11 @@ "integrity": "sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==" }, "linkify-it": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", - "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "requires": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "listr2": { @@ -1593,13 +1550,43 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, "log-symbols": { "version": "4.1.0", @@ -1656,32 +1643,19 @@ } }, "mailparser": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.6.5.tgz", - "integrity": "sha512-nteTpF0Khm5JLOnt4sigmzNdUH/6mO7PZ4KEnvxf4mckyXYFFhrtAWZzbq/V5aQMH+049gA7ZjfLdh+QiX2Uqg==", + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.6.6.tgz", + "integrity": "sha512-noCjBl3FToxmqTP2fp7z17hQsiCroWNntfTd8O+UejOAF59xeN5WGZK27ilexXV2e2X/cbUhG3L8sfEKaz0/sw==", "requires": { "encoding-japanese": "2.0.0", "he": "1.2.0", "html-to-text": "9.0.5", "iconv-lite": "0.6.3", "libmime": "5.2.1", - "linkify-it": "4.0.1", + "linkify-it": "5.0.0", "mailsplit": "5.4.0", - "nodemailer": "6.9.3", - "tlds": "1.240.0" - }, - "dependencies": { - "libmime": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.1.tgz", - "integrity": "sha512-A0z9O4+5q+ZTj7QwNe/Juy1KARNb4WaviO4mYeFC4b8dBT2EEqK2pkM+GC8MVnkOjqhl5nYQxRgnPYRRTNmuSQ==", - "requires": { - "encoding-japanese": "2.0.0", - "iconv-lite": "0.6.3", - "libbase64": "1.2.1", - "libqp": "2.0.1" - } - } + "nodemailer": "6.9.8", + "tlds": "1.248.0" } }, "mailsplit": { @@ -1692,6 +1666,19 @@ "libbase64": "1.2.1", "libmime": "5.2.0", "libqp": "2.0.1" + }, + "dependencies": { + "libmime": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.0.tgz", + "integrity": "sha512-X2U5Wx0YmK0rXFbk67ASMeqYIkZ6E5vY7pNWRKtnNzqjvdYYG8xtPDpCnuUEnPU9vlgNev+JoSrcaKSUaNvfsw==", + "requires": { + "encoding-japanese": "2.0.0", + "iconv-lite": "0.6.3", + "libbase64": "1.2.1", + "libqp": "2.0.1" + } + } } }, "merge-stream": { @@ -1743,9 +1730,9 @@ "dev": true }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "nodeify": { "version": "1.0.1", @@ -1757,9 +1744,9 @@ } }, "nodemailer": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.3.tgz", - "integrity": "sha512-fy9v3NgTzBngrMFkDsKEj0r02U7jm6XfC3b52eoNV+GCrGj+s8pt5OqhiJdWKuw51zCTdiNR/IUD1z33LIIGpg==" + "version": "6.9.8", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.8.tgz", + "integrity": "sha512-cfrYUk16e67Ks051i4CntM9kshRYei1/o/Gi8K1d+R34OIs21xdFnW7Pt7EucmVKA0LKtqUGNcjMZ7ehjl49mQ==" }, "npm-run-path": { "version": "4.0.1", @@ -1771,9 +1758,9 @@ } }, "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true }, "once": { @@ -1877,6 +1864,12 @@ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true + }, "promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/promise/-/promise-1.3.0.tgz", @@ -1986,9 +1979,9 @@ } }, "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", "dev": true }, "rimraf": { @@ -2001,9 +1994,9 @@ } }, "rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -2035,6 +2028,19 @@ "lru-cache": "^6.0.0" } }, + "set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "dev": true, + "requires": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2085,9 +2091,9 @@ } }, "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -2112,6 +2118,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -2126,6 +2143,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -2142,9 +2168,9 @@ } }, "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", + "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", "dev": true }, "through": { @@ -2154,9 +2180,9 @@ "dev": true }, "tlds": { - "version": "1.240.0", - "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.240.0.tgz", - "integrity": "sha512-1OYJQenswGZSOdRw7Bql5Qu7uf75b+F3HFBXbqnG/ifHa0fev1XcG+3pJf3pA/KC6RtHQzfKgIf1vkMlMG7mtQ==" + "version": "1.248.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.248.0.tgz", + "integrity": "sha512-noj0KdpWTBhwsKxMOXk0rN9otg4kTgLm4WohERRHbJ9IY+kSDKr3RmjitaQ3JFzny+DyvBOQKlFZhp0G0qNSfg==" }, "tmp": { "version": "0.2.1", @@ -2177,20 +2203,12 @@ "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" - }, - "dependencies": { - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true - } } }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, "tunnel-agent": { @@ -2215,14 +2233,14 @@ "dev": true }, "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.0.0.tgz", + "integrity": "sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==" }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true }, "untildify": { @@ -2312,6 +2330,17 @@ "strip-ansi": "^6.0.0" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/tests_smoke/smoke/common.py b/tests_smoke/smoke/common.py index 37293fc77c..8c189baf19 100644 --- a/tests_smoke/smoke/common.py +++ b/tests_smoke/smoke/common.py @@ -79,7 +79,7 @@ def single_succeeded(uri: str, use_jwt: bool) -> bool: token = create_jwt_token(Config.ADMIN_CLIENT_SECRET, client_id=Config.ADMIN_CLIENT_USER_NAME) headers = {"Authorization": f"Bearer {token}"} else: - headers = {"Authorization": f"ApiKey-v1 {Config.API_KEY[-36:]}"} + headers = {"Authorization": f"ApiKey-v1 {Config.API_KEY}"} response = requests.get( uri, diff --git a/tests_smoke/smoke/test_api_bulk.py b/tests_smoke/smoke/test_api_bulk.py index 1817a2b552..e31455589d 100644 --- a/tests_smoke/smoke/test_api_bulk.py +++ b/tests_smoke/smoke/test_api_bulk.py @@ -25,7 +25,7 @@ def test_api_bulk(notification_type: Notification_type): "template_id": template_id, "csv": rows_to_csv([[header, "var"], *job_line(to, 2)]), }, - headers={"Authorization": f"ApiKey-v1 {Config.API_KEY[-36:]}"}, + headers={"Authorization": f"ApiKey-v1 {Config.API_KEY}"}, ) if response.status_code != 201: pretty_print(response.json()) diff --git a/tests_smoke/smoke/test_api_one_off.py b/tests_smoke/smoke/test_api_one_off.py index 1c19ec1018..643192bb64 100644 --- a/tests_smoke/smoke/test_api_one_off.py +++ b/tests_smoke/smoke/test_api_one_off.py @@ -57,7 +57,7 @@ def test_api_one_off(notification_type: Notification_type, attachment_type: Atta response = requests.post( f"{Config.API_HOST_NAME}/v2/notifications/{notification_type.value}", json=data, - headers={"Authorization": f"ApiKey-v1 {Config.API_KEY[-36:]}"}, + headers={"Authorization": f"ApiKey-v1 {Config.API_KEY}"}, ) if response.status_code != 201: pretty_print(response.json())