From 1bd655271ab43a8c446460907da7ea7a4e66a45d Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sat, 25 Nov 2023 20:43:23 +0100 Subject: [PATCH 1/9] Cleanup docker-compose --- docker-compose.yml | 177 +-------------------------------------------- 1 file changed, 1 insertion(+), 176 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ef43f16296..1dae52a1fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ x-defaults: PRETIX_API_TOKEN: None # ask for a token SECRET_KEY: secret-key API_URL: /graphql - API_URL_SERVER: http://gateway:4000/graphql + API_URL_SERVER: http://localhost:8000/graphql # Stripe STRIPE_SUBSCRIPTION_PRICE_ID: price_1IkVzxD5MZ3GejSORRBZCvK6 # URLs @@ -117,172 +117,6 @@ services: interval: 5s timeout: 5s - users-backend: - build: - context: ./users-backend - dockerfile: ../Dockerfile.python.local - networks: [pycon_net] - entrypoint: "" - command: sh -c "poetry run task migrate && - touch /.ready && - python -m uvicorn main:wrapped_app --host 0.0.0.0 --port 8050 --reload" - depends_on: - users-backend-db: - condition: service_healthy - environment: - <<: *enviroment_defaults - DATABASE_URL: postgresql+asyncpg://users:users@users-backend-db/users - volumes: - - ./users-backend:/home/app/ - tty: true - stdin_open: true - ports: - - 8050:8050 - healthcheck: - test: ["CMD-SHELL", "test -f /.ready"] - interval: 10s - timeout: 10s - retries: 10 - - users-backend-db: - image: postgres:14.5 - networks: [pycon_net] - ports: - - "15500:5432" - volumes: - - users-backend-db-data:/var/lib/postgresql/data - - ./db-superuser.sql:/docker-entrypoint-initdb.d/db-superuser.sql - environment: - POSTGRES_USER: users - POSTGRES_PASSWORD: users - POSTGRES_DB: users - healthcheck: - test: ["CMD-SHELL", "pg_isready -U users"] - interval: 5s - timeout: 5s - - association-backend: - build: - context: ./association-backend - dockerfile: ../Dockerfile.python.local - networks: [pycon_net] - entrypoint: "" - command: sh -c "poetry run task migrate && - touch /.ready && - python -m uvicorn main:wrapped_app --host 0.0.0.0 --port 8060 --reload" - depends_on: - association-backend-db: - condition: service_healthy - environment: - <<: *enviroment_defaults - DATABASE_URL: postgresql://association:association@association-backend-db/association - volumes: - - ./association-backend:/home/app/ - tty: true - stdin_open: true - ports: - - 8060:8060 - healthcheck: - test: ["CMD-SHELL", "test -f /.ready"] - interval: 10s - timeout: 10s - retries: 10 - - association-backend-db: - image: postgres:14.5 - networks: [pycon_net] - ports: - - "15503:5432" - volumes: - - association-backend-db-data:/var/lib/postgresql/data - - ./db-superuser.sql:/docker-entrypoint-initdb.d/db-superuser.sql - environment: - POSTGRES_USER: association - POSTGRES_PASSWORD: association - POSTGRES_DB: association - healthcheck: - test: ["CMD-SHELL", "pg_isready -U association"] - interval: 5s - timeout: 5s - - cms: - build: - context: ./cms - dockerfile: ../Dockerfile.python311.local - networks: [pycon_net] - command: sh -c "pdm run migrate && - touch /.ready && - pdm run python manage.py runserver 0.0.0.0:8070" - depends_on: - cms-db: - condition: service_healthy - environment: - <<: *enviroment_defaults - DATABASE_URL: postgresql://cms:cms@cms-db/cms - DJANGO_SETTINGS_MODULE: cms.settings.dev - MAPBOX_PUBLIC_API_KEY: ${MAPBOX_PUBLIC_API_KEY} - volumes: - - ./cms:/home/app/ - - /home/app/.venv - tty: true - stdin_open: true - ports: - - 8070:8070 - healthcheck: - test: ["CMD-SHELL", "test -f /.ready"] - interval: 10s - timeout: 10s - retries: 10 - - cms-db: - image: postgres:14.5 - networks: [pycon_net] - ports: - - "15504:5432" - volumes: - - cms-db-data:/var/lib/postgresql/data - - ./db-superuser.sql:/docker-entrypoint-initdb.d/db-superuser.sql - environment: - POSTGRES_USER: cms - POSTGRES_PASSWORD: cms - POSTGRES_DB: cms - healthcheck: - test: ["CMD-SHELL", "pg_isready -U cms"] - interval: 5s - timeout: 5s - - gateway: - build: - context: ./gateway - dockerfile: ../Dockerfile.node.local - networks: [pycon_net] - entrypoint: "" - command: sh -c "pnpm install && - touch /.ready && - pnpm dev" - volumes: - - ./gateway/:/home/node/app - - /home/node/app/.pnpm-store/ - - /home/node/app/node_modules/ - environment: - <<: *enviroment_defaults - tty: true - stdin_open: true - ports: - - 4000:4000 - depends_on: - pycon-backend: - condition: service_healthy - users-backend: - condition: service_healthy - association-backend: - condition: service_healthy - healthcheck: - test: ["CMD-SHELL", "test -f /.ready"] - interval: 10s - timeout: 10s - retries: 10 - pycon-frontend: build: context: ./frontend @@ -302,9 +136,6 @@ services: stdin_open: true ports: - 3000:3000 - depends_on: - gateway: - condition: service_healthy association-frontend: build: @@ -326,9 +157,6 @@ services: stdin_open: true ports: - 3020:3020 - depends_on: - gateway: - condition: service_healthy email-templates: build: @@ -405,7 +233,4 @@ networks: volumes: pycon-backend-db-data: - users-backend-db-data: - association-backend-db-data: temporal-db-data: - cms-db-data: From f1f48e607f939b6fec47c39c38c8bdf647f5c92b Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sat, 25 Nov 2023 20:44:59 +0100 Subject: [PATCH 2/9] compose --- docker-compose.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 1dae52a1fe..ace61ce8d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -136,6 +136,9 @@ services: stdin_open: true ports: - 3000:3000 + depends_on: + pycon-backend: + condition: service_healthy association-frontend: build: @@ -157,6 +160,9 @@ services: stdin_open: true ports: - 3020:3020 + depends_on: + pycon-backend: + condition: service_healthy email-templates: build: From 29e661590eb8102e09199b1aee9f6a606a2fddf9 Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sat, 25 Nov 2023 21:02:50 +0100 Subject: [PATCH 3/9] fix --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ace61ce8d5..103b39dc95 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ x-defaults: PRETIX_API_TOKEN: None # ask for a token SECRET_KEY: secret-key API_URL: /graphql - API_URL_SERVER: http://localhost:8000/graphql + API_URL_SERVER: http://pycon-backend:8000/graphql # Stripe STRIPE_SUBSCRIPTION_PRICE_ID: price_1IkVzxD5MZ3GejSORRBZCvK6 # URLs From d9c31165750170ada65aa2c36450ccaab5a4e726 Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sat, 25 Nov 2023 21:14:06 +0100 Subject: [PATCH 4/9] Remove old merged codebases --- association-backend/.dockerignore | 9 - association-backend/.editorconfig | 1 - association-backend/.flake8 | 1 - association-backend/.isort.cfg | 1 - association-backend/Dockerfile | 35 - association-backend/README.md | 1 - association-backend/alembic.ini | 83 - association-backend/alembic/README | 1 - association-backend/alembic/env.py | 85 - association-backend/alembic/script.py.mako | 24 - .../versions/94d0021c6380_new_structure.py | 79 - association-backend/main.py | 108 - association-backend/poetry.lock | 1722 ----- association-backend/pyproject.toml | 59 - association-backend/scripts/__init__.py | 0 association-backend/scripts/db_utils.py | 77 - association-backend/scripts/prepare_db.py | 31 - association-backend/src/__init__.py | 0 association-backend/src/api/__init__.py | 0 association-backend/src/api/context.py | 16 - association-backend/src/api/mutation.py | 8 - .../src/api/mutations/__init__.py | 0 .../api/mutations/manage_user_subscription.py | 46 - .../subscribe_user_to_association.py | 52 - association-backend/src/api/query.py | 13 - association-backend/src/api/schema.py | 6 - association-backend/src/api/tests/__init__.py | 0 .../src/api/tests/mutations/__init__.py | 0 ...st_manage_user_association_subscription.py | 128 - .../test_subscribe_user_to_association.py | 93 - association-backend/src/api/views.py | 22 - .../src/association/__init__.py | 0 association-backend/src/association/auth.py | 18 - .../src/association/settings.py | 61 - .../src/association/tests/__init__.py | 0 .../src/association/tests/api.py | 19 - .../src/association/tests/factories.py | 82 - .../src/association/tests/session.py | 16 - .../src/association_membership/__init__.py | 0 .../association_membership/domain/__init__.py | 0 .../association_membership/domain/entities.py | 189 - .../domain/exceptions.py | 17 - .../domain/repository.py | 102 - .../domain/services/__init__.py | 0 .../manage_user_association_subscription.py | 39 - .../services/subscribe_user_to_association.py | 26 - .../association_membership/tests/__init__.py | 0 ...st_manage_user_association_subscription.py | 139 - .../test_subscribe_user_to_association.py | 88 - .../tests/domain/test_entities.py | 60 - .../tests/domain/test_repository.py | 28 - .../association_membership/tests/factories.py | 19 - .../tests/fake_repository.py | 46 - association-backend/src/database/__init__.py | 0 association-backend/src/database/db.py | 13 - .../src/internal_api/__init__.py | 0 .../src/internal_api/context.py | 21 - .../src/internal_api/permissions.py | 9 - association-backend/src/internal_api/query.py | 26 - .../src/internal_api/schema.py | 5 - .../src/internal_api/tests/__init__.py | 0 .../src/internal_api/tests/fixtures.py | 29 - .../internal_api/tests/queries/__init__.py | 0 .../tests/queries/test_user_id_is_member.py | 139 - association-backend/src/internal_api/views.py | 40 - association-backend/src/webhooks/__init__.py | 0 .../src/webhooks/auth_backend.py | 38 - .../src/webhooks/exceptions.py | 33 - .../src/webhooks/handlers/__init__.py | 46 - .../handlers/crons/membership_check_status.py | 79 - .../src/webhooks/handlers/pretix/__init__.py | 0 .../src/webhooks/handlers/pretix/api.py | 43 - .../pretix/pretix_event_order_paid.py | 239 - .../src/webhooks/handlers/stripe/__init__.py | 0 .../handlers/stripe/handle_invoice_paid.py | 74 - .../crons/test_membership_check_status.py | 476 -- .../tests/handlers/pretix/payloads.py | 746 -- .../pretix/test_pretix_event_order_paid.py | 336 - .../tests/handlers/stripe/payloads.py | 177 - .../stripe/test_handle_invoice_paid.py | 124 - .../src/webhooks/tests/test_views.py | 52 - association-backend/src/webhooks/views.py | 40 - cms/.coveragerc | 34 - cms/.dockerignore | 42 - cms/.tool-versions | 1 - cms/Dockerfile | 53 - cms/api/__init__.py | 0 cms/api/base/__init__.py | 0 cms/api/base/blocks/__init__.py | 0 cms/api/base/blocks/accordion.py | 17 - cms/api/base/blocks/cta.py | 12 - cms/api/base/blocks/map.py | 52 - cms/api/base/types.py | 8 - cms/api/home/__init__.py | 0 cms/api/home/blocks/__init__.py | 0 cms/api/home/blocks/home_intro_section.py | 17 - cms/api/news/__init__.py | 0 cms/api/news/blocks/__init__.py | 0 cms/api/news/blocks/news_grid_section.py | 11 - cms/api/news/queries/__init__.py | 0 cms/api/news/queries/news_article.py | 28 - cms/api/news/queries/news_articles.py | 21 - cms/api/news/types.py | 26 - cms/api/page/__init__.py | 0 cms/api/page/blocks/__init__.py | 0 cms/api/page/blocks/checkout_section.py | 45 - cms/api/page/blocks/information_section.py | 31 - cms/api/page/blocks/keynoters_section.py | 20 - cms/api/page/blocks/live_streaming_section.py | 13 - .../page/blocks/schedule_preview_section.py | 26 - cms/api/page/blocks/slider_cards_section.py | 71 - cms/api/page/blocks/socials_section.py | 17 - cms/api/page/blocks/special_guest_section.py | 34 - cms/api/page/blocks/sponsors_section.py | 30 - cms/api/page/blocks/text_section.py | 43 - cms/api/page/queries/__init__.py | 0 cms/api/page/queries/cms_page.py | 32 - cms/api/page/queries/cms_pages.py | 19 - cms/api/page/types.py | 66 - cms/api/schema.py | 12 - cms/api/tests/__init__.py | 0 cms/api/tests/factories.py | 84 - cms/api/tests/news/__init__.py | 0 cms/api/tests/news/test_queries.py | 277 - cms/api/tests/pages/__init__.py | 0 cms/api/tests/pages/test_queries.py | 236 - cms/api/views.py | 9 - cms/base/__init__.py | 0 cms/base/blocks/__init__.py | 0 cms/base/blocks/accordion.py | 10 - cms/base/blocks/cta.py | 10 - cms/base/blocks/map.py | 11 - cms/cms/__init__.py | 0 cms/cms/settings/__init__.py | 0 cms/cms/settings/base.py | 170 - cms/cms/settings/dev.py | 12 - cms/cms/settings/prod.py | 22 - cms/cms/urls.py | 33 - cms/cms/wsgi.py | 16 - cms/conftest.py | 42 - cms/custom_auth/backend.py | 69 - cms/custom_auth/tests/__init__.py | 0 cms/custom_auth/tests/test_backend.py | 117 - cms/gunicorn.conf.py | 4 - cms/home/__init__.py | 0 cms/home/blocks/__init__.py | 0 cms/home/blocks/home_intro_section.py | 10 - cms/home/migrations/0001_initial.py | 52 - cms/home/migrations/__init__.py | 0 cms/home/models.py | 21 - cms/manage.py | 10 - cms/news/__init__.py | 0 cms/news/admin.py | 1 - cms/news/apps.py | 6 - cms/news/blocks/__init__.py | 0 cms/news/blocks/news_grid_section.py | 7 - cms/news/migrations/0001_news_articles.py | 29 - .../0002_alter_newsarticle_excerpt.py | 18 - cms/news/migrations/__init__.py | 0 cms/news/models.py | 15 - cms/news/tests/__init__.py | 0 cms/news/tests/factories.py | 20 - cms/page/__init__.py | 0 cms/page/apps.py | 11 - cms/page/blocks/__init__.py | 0 cms/page/blocks/checkout_section.py | 16 - cms/page/blocks/information_section.py | 22 - cms/page/blocks/keynoters_section.py | 11 - cms/page/blocks/live_streaming_section.py | 7 - cms/page/blocks/schedule_preview_section.py | 12 - cms/page/blocks/slider_cards_section.py | 48 - cms/page/blocks/socials_section.py | 10 - cms/page/blocks/special_guest_section.py | 17 - cms/page/blocks/sponsors_section.py | 17 - cms/page/blocks/text_section.py | 24 - cms/page/fields.py | 59 - cms/page/migrations/0001_initial.py | 52 - .../migrations/0002_alter_genericpage_body.py | 74 - cms/page/migrations/0003_update_blocks.py | 22 - cms/page/migrations/0004_update_blocks.py | 24 - .../0005_add_illustration_choices.py | 153 - .../migrations/0006_refactor_structure.py | 22 - .../0007_add_body_size_in_text_section.py | 22 - cms/page/migrations/0008_homepage_blocks.py | 22 - .../0009_vercel_frontend_settings.py | 22 - cms/page/migrations/0010_news_articles.py | 22 - .../migrations/0011_alter_genericpage_body.py | 22 - .../migrations/0012_live_streaming_section.py | 22 - cms/page/migrations/__init__.py | 0 cms/page/models.py | 47 - cms/page/signals.py | 66 - cms/page/tests/__init__.py | 0 cms/page/tests/test_signals.py | 124 - cms/pdm.lock | 1242 ---- cms/pyproject.toml | 41 - cms/sites/__init__.py | 0 cms/sites/apps.py | 6 - .../0001_vercel_frontend_settings.py | 28 - cms/sites/migrations/__init__.py | 0 cms/sites/models.py | 11 - cms/sites/tests/__init__.py | 0 cms/sites/tests/factories.py | 10 - gateway/.dockerignore | 9 - gateway/.editorconfig | 1 - gateway/.eslintrc | 3 - gateway/.npmrc | 1 - gateway/.prettierignore | 1 - gateway/.prettierrc | 9 - gateway/Dockerfile | 32 - gateway/__mocks__/config.ts | 3 - gateway/config.ts | 21 - gateway/context.ts | 54 - gateway/dev-types.d.ts | 1 - gateway/dev/dev-server.ts | 35 - gateway/dev/utils.ts | 3 - gateway/gateway.ts | 70 - gateway/handler.ts | 55 - gateway/init.ts | 6 - gateway/jest.config.js | 5 - gateway/nodemon.json | 5 - gateway/package.json | 50 - gateway/pastaporto/create-pastaporto.ts | 38 - gateway/plugins/apollo-headers.ts | 47 - gateway/plugins/format-cookies-express.ts | 20 - gateway/plugins/sentry.ts | 123 - gateway/pnpm-lock.yaml | 6599 ----------------- gateway/services.ts | 12 - gateway/tsconfig.json | 26 - gateway/types/global.ts | 2 - users-backend/.dockerignore | 8 - users-backend/.editorconfig | 1 - users-backend/.flake8 | 1 - users-backend/.isort.cfg | 1 - users-backend/Dockerfile | 36 - users-backend/alembic.ini | 83 - users-backend/alembic/README | 1 - users-backend/alembic/env.py | 85 - users-backend/alembic/script.py.mako | 24 - .../1d5fd146e8a5_import_user_model.py | 46 - .../2e5e687d84d9_store_jwt_auth_id.py | 28 - .../c998e741da04_make_email_unique.py | 27 - users-backend/gunicorn.conf.py | 5 - users-backend/main.py | 101 - users-backend/poetry.lock | 2123 ------ users-backend/pyproject.toml | 71 - users-backend/scripts/__init__.py | 0 users-backend/scripts/db_utils.py | 77 - users-backend/scripts/prepare_db.py | 36 - users-backend/users/__init__.py | 0 users-backend/users/api/__init__.py | 0 users-backend/users/api/context.py | 34 - users-backend/users/api/dataloader.py | 19 - users-backend/users/api/schema.py | 16 - users-backend/users/api/tests/__init__.py | 0 .../users/api/tests/mutation/__init__.py | 0 .../api/tests/mutation/test_login_mutation.py | 187 - .../tests/mutation/test_register_mutation.py | 152 - .../mutation/test_reset_password_mutation.py | 185 - .../mutation/test_update_profile_mutation.py | 58 - .../users/api/tests/query/__init__.py | 0 .../users/api/tests/query/test_me.py | 60 - users-backend/users/api/types.py | 9 - users-backend/users/api/views.py | 71 - users-backend/users/db.py | 13 - users-backend/users/domain/__init__.py | 0 users-backend/users/domain/entities.py | 149 - users-backend/users/domain/paginable.py | 39 - users-backend/users/domain/repository.py | 102 - .../users/domain/services/__init__.py | 18 - .../domain/services/create_pastaporto.py | 62 - .../users/domain/services/exceptions.py | 31 - users-backend/users/domain/services/login.py | 42 - .../users/domain/services/register.py | 41 - .../domain/services/request_reset_password.py | 31 - .../users/domain/services/reset_password.py | 63 - .../users/domain/services/social_login.py | 54 - .../users/domain/services/update_profile.py | 47 - users-backend/users/domain/tests/__init__.py | 0 .../users/domain/tests/fake_repository.py | 58 - .../users/domain/tests/services/__init__.py | 0 .../tests/services/test_create_pastaporto.py | 173 - .../users/domain/tests/services/test_login.py | 171 - .../domain/tests/services/test_register.py | 86 - .../services/test_request_reset_password.py | 25 - .../tests/services/test_reset_password.py | 195 - .../tests/services/test_social_login.py | 104 - .../tests/services/test_update_profile.py | 101 - .../users/domain/tests/test_entities.py | 31 - .../users/domain/tests/test_paginable.py | 65 - .../users/domain/tests/test_repository.py | 287 - users-backend/users/emails.py | 20 - users-backend/users/internal_api/__init__.py | 0 users-backend/users/internal_api/context.py | 23 - .../users/internal_api/input_types.py | 8 - users-backend/users/internal_api/mutation.py | 38 - .../users/internal_api/permissions.py | 9 - users-backend/users/internal_api/query.py | 75 - users-backend/users/internal_api/schema.py | 6 - .../users/internal_api/tests/__init__.py | 0 .../tests/mutations/test_login.py | 116 - .../internal_api/tests/queries/__init__.py | 0 .../queries/test_create_pastaporto_query.py | 169 - .../tests/queries/test_search_users.py | 84 - .../tests/queries/test_user_by_email.py | 105 - .../tests/queries/test_user_query.py | 76 - .../tests/queries/test_users_by_emails.py | 138 - .../tests/queries/test_users_by_ids.py | 140 - users-backend/users/internal_api/types.py | 49 - users-backend/users/internal_api/views.py | 40 - users-backend/users/settings.py | 83 - users-backend/users/social_auth/__init__.py | 0 .../users/social_auth/tests/__init__.py | 0 .../social_auth/tests/test_google_login.py | 105 - users-backend/users/social_auth/views.py | 53 - .../users/starlette_password/__init__.py | 1 - .../users/starlette_password/crypto.py | 71 - .../users/starlette_password/hashers.py | 737 -- .../users/starlette_password/imports.py | 22 - .../users/starlette_password/plain_hasher.py | 37 - users-backend/users/tests/__init__.py | 0 users-backend/users/tests/api.py | 33 - users-backend/users/tests/factories.py | 64 - users-backend/users/tests/session.py | 30 - 323 files changed, 25931 deletions(-) delete mode 100644 association-backend/.dockerignore delete mode 120000 association-backend/.editorconfig delete mode 120000 association-backend/.flake8 delete mode 120000 association-backend/.isort.cfg delete mode 100644 association-backend/Dockerfile delete mode 100644 association-backend/README.md delete mode 100644 association-backend/alembic.ini delete mode 100644 association-backend/alembic/README delete mode 100644 association-backend/alembic/env.py delete mode 100644 association-backend/alembic/script.py.mako delete mode 100644 association-backend/alembic/versions/94d0021c6380_new_structure.py delete mode 100644 association-backend/main.py delete mode 100644 association-backend/poetry.lock delete mode 100644 association-backend/pyproject.toml delete mode 100644 association-backend/scripts/__init__.py delete mode 100644 association-backend/scripts/db_utils.py delete mode 100644 association-backend/scripts/prepare_db.py delete mode 100644 association-backend/src/__init__.py delete mode 100644 association-backend/src/api/__init__.py delete mode 100644 association-backend/src/api/context.py delete mode 100644 association-backend/src/api/mutation.py delete mode 100644 association-backend/src/api/mutations/__init__.py delete mode 100644 association-backend/src/api/mutations/manage_user_subscription.py delete mode 100644 association-backend/src/api/mutations/subscribe_user_to_association.py delete mode 100644 association-backend/src/api/query.py delete mode 100644 association-backend/src/api/schema.py delete mode 100644 association-backend/src/api/tests/__init__.py delete mode 100644 association-backend/src/api/tests/mutations/__init__.py delete mode 100644 association-backend/src/api/tests/mutations/test_manage_user_association_subscription.py delete mode 100644 association-backend/src/api/tests/mutations/test_subscribe_user_to_association.py delete mode 100644 association-backend/src/api/views.py delete mode 100644 association-backend/src/association/__init__.py delete mode 100644 association-backend/src/association/auth.py delete mode 100644 association-backend/src/association/settings.py delete mode 100644 association-backend/src/association/tests/__init__.py delete mode 100644 association-backend/src/association/tests/api.py delete mode 100644 association-backend/src/association/tests/factories.py delete mode 100644 association-backend/src/association/tests/session.py delete mode 100644 association-backend/src/association_membership/__init__.py delete mode 100644 association-backend/src/association_membership/domain/__init__.py delete mode 100644 association-backend/src/association_membership/domain/entities.py delete mode 100644 association-backend/src/association_membership/domain/exceptions.py delete mode 100644 association-backend/src/association_membership/domain/repository.py delete mode 100644 association-backend/src/association_membership/domain/services/__init__.py delete mode 100644 association-backend/src/association_membership/domain/services/manage_user_association_subscription.py delete mode 100644 association-backend/src/association_membership/domain/services/subscribe_user_to_association.py delete mode 100644 association-backend/src/association_membership/tests/__init__.py delete mode 100644 association-backend/src/association_membership/tests/domain/services/test_manage_user_association_subscription.py delete mode 100644 association-backend/src/association_membership/tests/domain/services/test_subscribe_user_to_association.py delete mode 100644 association-backend/src/association_membership/tests/domain/test_entities.py delete mode 100644 association-backend/src/association_membership/tests/domain/test_repository.py delete mode 100644 association-backend/src/association_membership/tests/factories.py delete mode 100644 association-backend/src/association_membership/tests/fake_repository.py delete mode 100644 association-backend/src/database/__init__.py delete mode 100644 association-backend/src/database/db.py delete mode 100644 association-backend/src/internal_api/__init__.py delete mode 100644 association-backend/src/internal_api/context.py delete mode 100644 association-backend/src/internal_api/permissions.py delete mode 100644 association-backend/src/internal_api/query.py delete mode 100644 association-backend/src/internal_api/schema.py delete mode 100644 association-backend/src/internal_api/tests/__init__.py delete mode 100644 association-backend/src/internal_api/tests/fixtures.py delete mode 100644 association-backend/src/internal_api/tests/queries/__init__.py delete mode 100644 association-backend/src/internal_api/tests/queries/test_user_id_is_member.py delete mode 100644 association-backend/src/internal_api/views.py delete mode 100644 association-backend/src/webhooks/__init__.py delete mode 100644 association-backend/src/webhooks/auth_backend.py delete mode 100644 association-backend/src/webhooks/exceptions.py delete mode 100644 association-backend/src/webhooks/handlers/__init__.py delete mode 100644 association-backend/src/webhooks/handlers/crons/membership_check_status.py delete mode 100644 association-backend/src/webhooks/handlers/pretix/__init__.py delete mode 100644 association-backend/src/webhooks/handlers/pretix/api.py delete mode 100644 association-backend/src/webhooks/handlers/pretix/pretix_event_order_paid.py delete mode 100644 association-backend/src/webhooks/handlers/stripe/__init__.py delete mode 100644 association-backend/src/webhooks/handlers/stripe/handle_invoice_paid.py delete mode 100644 association-backend/src/webhooks/tests/handlers/crons/test_membership_check_status.py delete mode 100644 association-backend/src/webhooks/tests/handlers/pretix/payloads.py delete mode 100644 association-backend/src/webhooks/tests/handlers/pretix/test_pretix_event_order_paid.py delete mode 100644 association-backend/src/webhooks/tests/handlers/stripe/payloads.py delete mode 100644 association-backend/src/webhooks/tests/handlers/stripe/test_handle_invoice_paid.py delete mode 100644 association-backend/src/webhooks/tests/test_views.py delete mode 100644 association-backend/src/webhooks/views.py delete mode 100644 cms/.coveragerc delete mode 100644 cms/.dockerignore delete mode 100644 cms/.tool-versions delete mode 100644 cms/Dockerfile delete mode 100644 cms/api/__init__.py delete mode 100644 cms/api/base/__init__.py delete mode 100644 cms/api/base/blocks/__init__.py delete mode 100644 cms/api/base/blocks/accordion.py delete mode 100644 cms/api/base/blocks/cta.py delete mode 100644 cms/api/base/blocks/map.py delete mode 100644 cms/api/base/types.py delete mode 100644 cms/api/home/__init__.py delete mode 100644 cms/api/home/blocks/__init__.py delete mode 100644 cms/api/home/blocks/home_intro_section.py delete mode 100644 cms/api/news/__init__.py delete mode 100644 cms/api/news/blocks/__init__.py delete mode 100644 cms/api/news/blocks/news_grid_section.py delete mode 100644 cms/api/news/queries/__init__.py delete mode 100644 cms/api/news/queries/news_article.py delete mode 100644 cms/api/news/queries/news_articles.py delete mode 100644 cms/api/news/types.py delete mode 100644 cms/api/page/__init__.py delete mode 100644 cms/api/page/blocks/__init__.py delete mode 100644 cms/api/page/blocks/checkout_section.py delete mode 100644 cms/api/page/blocks/information_section.py delete mode 100644 cms/api/page/blocks/keynoters_section.py delete mode 100644 cms/api/page/blocks/live_streaming_section.py delete mode 100644 cms/api/page/blocks/schedule_preview_section.py delete mode 100644 cms/api/page/blocks/slider_cards_section.py delete mode 100644 cms/api/page/blocks/socials_section.py delete mode 100644 cms/api/page/blocks/special_guest_section.py delete mode 100644 cms/api/page/blocks/sponsors_section.py delete mode 100644 cms/api/page/blocks/text_section.py delete mode 100644 cms/api/page/queries/__init__.py delete mode 100644 cms/api/page/queries/cms_page.py delete mode 100644 cms/api/page/queries/cms_pages.py delete mode 100644 cms/api/page/types.py delete mode 100644 cms/api/schema.py delete mode 100644 cms/api/tests/__init__.py delete mode 100644 cms/api/tests/factories.py delete mode 100644 cms/api/tests/news/__init__.py delete mode 100644 cms/api/tests/news/test_queries.py delete mode 100644 cms/api/tests/pages/__init__.py delete mode 100644 cms/api/tests/pages/test_queries.py delete mode 100644 cms/api/views.py delete mode 100644 cms/base/__init__.py delete mode 100644 cms/base/blocks/__init__.py delete mode 100644 cms/base/blocks/accordion.py delete mode 100644 cms/base/blocks/cta.py delete mode 100644 cms/base/blocks/map.py delete mode 100644 cms/cms/__init__.py delete mode 100644 cms/cms/settings/__init__.py delete mode 100644 cms/cms/settings/base.py delete mode 100644 cms/cms/settings/dev.py delete mode 100644 cms/cms/settings/prod.py delete mode 100644 cms/cms/urls.py delete mode 100644 cms/cms/wsgi.py delete mode 100644 cms/conftest.py delete mode 100644 cms/custom_auth/backend.py delete mode 100644 cms/custom_auth/tests/__init__.py delete mode 100644 cms/custom_auth/tests/test_backend.py delete mode 100644 cms/gunicorn.conf.py delete mode 100644 cms/home/__init__.py delete mode 100644 cms/home/blocks/__init__.py delete mode 100644 cms/home/blocks/home_intro_section.py delete mode 100644 cms/home/migrations/0001_initial.py delete mode 100644 cms/home/migrations/__init__.py delete mode 100644 cms/home/models.py delete mode 100755 cms/manage.py delete mode 100644 cms/news/__init__.py delete mode 100644 cms/news/admin.py delete mode 100644 cms/news/apps.py delete mode 100644 cms/news/blocks/__init__.py delete mode 100644 cms/news/blocks/news_grid_section.py delete mode 100644 cms/news/migrations/0001_news_articles.py delete mode 100644 cms/news/migrations/0002_alter_newsarticle_excerpt.py delete mode 100644 cms/news/migrations/__init__.py delete mode 100644 cms/news/models.py delete mode 100644 cms/news/tests/__init__.py delete mode 100644 cms/news/tests/factories.py delete mode 100644 cms/page/__init__.py delete mode 100644 cms/page/apps.py delete mode 100644 cms/page/blocks/__init__.py delete mode 100644 cms/page/blocks/checkout_section.py delete mode 100644 cms/page/blocks/information_section.py delete mode 100644 cms/page/blocks/keynoters_section.py delete mode 100644 cms/page/blocks/live_streaming_section.py delete mode 100644 cms/page/blocks/schedule_preview_section.py delete mode 100644 cms/page/blocks/slider_cards_section.py delete mode 100644 cms/page/blocks/socials_section.py delete mode 100644 cms/page/blocks/special_guest_section.py delete mode 100644 cms/page/blocks/sponsors_section.py delete mode 100644 cms/page/blocks/text_section.py delete mode 100644 cms/page/fields.py delete mode 100644 cms/page/migrations/0001_initial.py delete mode 100644 cms/page/migrations/0002_alter_genericpage_body.py delete mode 100644 cms/page/migrations/0003_update_blocks.py delete mode 100644 cms/page/migrations/0004_update_blocks.py delete mode 100644 cms/page/migrations/0005_add_illustration_choices.py delete mode 100644 cms/page/migrations/0006_refactor_structure.py delete mode 100644 cms/page/migrations/0007_add_body_size_in_text_section.py delete mode 100644 cms/page/migrations/0008_homepage_blocks.py delete mode 100644 cms/page/migrations/0009_vercel_frontend_settings.py delete mode 100644 cms/page/migrations/0010_news_articles.py delete mode 100644 cms/page/migrations/0011_alter_genericpage_body.py delete mode 100644 cms/page/migrations/0012_live_streaming_section.py delete mode 100644 cms/page/migrations/__init__.py delete mode 100644 cms/page/models.py delete mode 100644 cms/page/signals.py delete mode 100644 cms/page/tests/__init__.py delete mode 100644 cms/page/tests/test_signals.py delete mode 100644 cms/pdm.lock delete mode 100644 cms/pyproject.toml delete mode 100644 cms/sites/__init__.py delete mode 100644 cms/sites/apps.py delete mode 100644 cms/sites/migrations/0001_vercel_frontend_settings.py delete mode 100644 cms/sites/migrations/__init__.py delete mode 100644 cms/sites/models.py delete mode 100644 cms/sites/tests/__init__.py delete mode 100644 cms/sites/tests/factories.py delete mode 100644 gateway/.dockerignore delete mode 120000 gateway/.editorconfig delete mode 100644 gateway/.eslintrc delete mode 100644 gateway/.npmrc delete mode 100644 gateway/.prettierignore delete mode 100755 gateway/.prettierrc delete mode 100644 gateway/Dockerfile delete mode 100644 gateway/__mocks__/config.ts delete mode 100644 gateway/config.ts delete mode 100644 gateway/context.ts delete mode 100644 gateway/dev-types.d.ts delete mode 100644 gateway/dev/dev-server.ts delete mode 100644 gateway/dev/utils.ts delete mode 100644 gateway/gateway.ts delete mode 100644 gateway/handler.ts delete mode 100644 gateway/init.ts delete mode 100644 gateway/jest.config.js delete mode 100644 gateway/nodemon.json delete mode 100644 gateway/package.json delete mode 100644 gateway/pastaporto/create-pastaporto.ts delete mode 100644 gateway/plugins/apollo-headers.ts delete mode 100644 gateway/plugins/format-cookies-express.ts delete mode 100644 gateway/plugins/sentry.ts delete mode 100644 gateway/pnpm-lock.yaml delete mode 100644 gateway/services.ts delete mode 100644 gateway/tsconfig.json delete mode 100644 gateway/types/global.ts delete mode 100644 users-backend/.dockerignore delete mode 120000 users-backend/.editorconfig delete mode 120000 users-backend/.flake8 delete mode 120000 users-backend/.isort.cfg delete mode 100644 users-backend/Dockerfile delete mode 100644 users-backend/alembic.ini delete mode 100644 users-backend/alembic/README delete mode 100644 users-backend/alembic/env.py delete mode 100644 users-backend/alembic/script.py.mako delete mode 100644 users-backend/alembic/versions/1d5fd146e8a5_import_user_model.py delete mode 100644 users-backend/alembic/versions/2e5e687d84d9_store_jwt_auth_id.py delete mode 100644 users-backend/alembic/versions/c998e741da04_make_email_unique.py delete mode 100644 users-backend/gunicorn.conf.py delete mode 100644 users-backend/main.py delete mode 100644 users-backend/poetry.lock delete mode 100644 users-backend/pyproject.toml delete mode 100644 users-backend/scripts/__init__.py delete mode 100644 users-backend/scripts/db_utils.py delete mode 100644 users-backend/scripts/prepare_db.py delete mode 100644 users-backend/users/__init__.py delete mode 100644 users-backend/users/api/__init__.py delete mode 100644 users-backend/users/api/context.py delete mode 100644 users-backend/users/api/dataloader.py delete mode 100644 users-backend/users/api/schema.py delete mode 100644 users-backend/users/api/tests/__init__.py delete mode 100644 users-backend/users/api/tests/mutation/__init__.py delete mode 100644 users-backend/users/api/tests/mutation/test_login_mutation.py delete mode 100644 users-backend/users/api/tests/mutation/test_register_mutation.py delete mode 100644 users-backend/users/api/tests/mutation/test_reset_password_mutation.py delete mode 100644 users-backend/users/api/tests/mutation/test_update_profile_mutation.py delete mode 100644 users-backend/users/api/tests/query/__init__.py delete mode 100644 users-backend/users/api/tests/query/test_me.py delete mode 100644 users-backend/users/api/types.py delete mode 100644 users-backend/users/api/views.py delete mode 100644 users-backend/users/db.py delete mode 100644 users-backend/users/domain/__init__.py delete mode 100644 users-backend/users/domain/entities.py delete mode 100644 users-backend/users/domain/paginable.py delete mode 100644 users-backend/users/domain/repository.py delete mode 100644 users-backend/users/domain/services/__init__.py delete mode 100644 users-backend/users/domain/services/create_pastaporto.py delete mode 100644 users-backend/users/domain/services/exceptions.py delete mode 100644 users-backend/users/domain/services/login.py delete mode 100644 users-backend/users/domain/services/register.py delete mode 100644 users-backend/users/domain/services/request_reset_password.py delete mode 100644 users-backend/users/domain/services/reset_password.py delete mode 100644 users-backend/users/domain/services/social_login.py delete mode 100644 users-backend/users/domain/services/update_profile.py delete mode 100644 users-backend/users/domain/tests/__init__.py delete mode 100644 users-backend/users/domain/tests/fake_repository.py delete mode 100644 users-backend/users/domain/tests/services/__init__.py delete mode 100644 users-backend/users/domain/tests/services/test_create_pastaporto.py delete mode 100644 users-backend/users/domain/tests/services/test_login.py delete mode 100644 users-backend/users/domain/tests/services/test_register.py delete mode 100644 users-backend/users/domain/tests/services/test_request_reset_password.py delete mode 100644 users-backend/users/domain/tests/services/test_reset_password.py delete mode 100644 users-backend/users/domain/tests/services/test_social_login.py delete mode 100644 users-backend/users/domain/tests/services/test_update_profile.py delete mode 100644 users-backend/users/domain/tests/test_entities.py delete mode 100644 users-backend/users/domain/tests/test_paginable.py delete mode 100644 users-backend/users/domain/tests/test_repository.py delete mode 100644 users-backend/users/emails.py delete mode 100644 users-backend/users/internal_api/__init__.py delete mode 100644 users-backend/users/internal_api/context.py delete mode 100644 users-backend/users/internal_api/input_types.py delete mode 100644 users-backend/users/internal_api/mutation.py delete mode 100644 users-backend/users/internal_api/permissions.py delete mode 100644 users-backend/users/internal_api/query.py delete mode 100644 users-backend/users/internal_api/schema.py delete mode 100644 users-backend/users/internal_api/tests/__init__.py delete mode 100644 users-backend/users/internal_api/tests/mutations/test_login.py delete mode 100644 users-backend/users/internal_api/tests/queries/__init__.py delete mode 100644 users-backend/users/internal_api/tests/queries/test_create_pastaporto_query.py delete mode 100644 users-backend/users/internal_api/tests/queries/test_search_users.py delete mode 100644 users-backend/users/internal_api/tests/queries/test_user_by_email.py delete mode 100644 users-backend/users/internal_api/tests/queries/test_user_query.py delete mode 100644 users-backend/users/internal_api/tests/queries/test_users_by_emails.py delete mode 100644 users-backend/users/internal_api/tests/queries/test_users_by_ids.py delete mode 100644 users-backend/users/internal_api/types.py delete mode 100644 users-backend/users/internal_api/views.py delete mode 100644 users-backend/users/settings.py delete mode 100644 users-backend/users/social_auth/__init__.py delete mode 100644 users-backend/users/social_auth/tests/__init__.py delete mode 100644 users-backend/users/social_auth/tests/test_google_login.py delete mode 100644 users-backend/users/social_auth/views.py delete mode 100644 users-backend/users/starlette_password/__init__.py delete mode 100644 users-backend/users/starlette_password/crypto.py delete mode 100644 users-backend/users/starlette_password/hashers.py delete mode 100644 users-backend/users/starlette_password/imports.py delete mode 100644 users-backend/users/starlette_password/plain_hasher.py delete mode 100644 users-backend/users/tests/__init__.py delete mode 100644 users-backend/users/tests/api.py delete mode 100644 users-backend/users/tests/factories.py delete mode 100644 users-backend/users/tests/session.py diff --git a/association-backend/.dockerignore b/association-backend/.dockerignore deleted file mode 100644 index 97309e4008..0000000000 --- a/association-backend/.dockerignore +++ /dev/null @@ -1,9 +0,0 @@ -.vscode/ -tests/ -.coverage -.env -.flake8 -.isort.cfg -coverage.xml -Dockerfile - diff --git a/association-backend/.editorconfig b/association-backend/.editorconfig deleted file mode 120000 index 38d9a0ce1c..0000000000 --- a/association-backend/.editorconfig +++ /dev/null @@ -1 +0,0 @@ -../.editorconfig \ No newline at end of file diff --git a/association-backend/.flake8 b/association-backend/.flake8 deleted file mode 120000 index cb0568d647..0000000000 --- a/association-backend/.flake8 +++ /dev/null @@ -1 +0,0 @@ -../.flake8 \ No newline at end of file diff --git a/association-backend/.isort.cfg b/association-backend/.isort.cfg deleted file mode 120000 index 62a72913ba..0000000000 --- a/association-backend/.isort.cfg +++ /dev/null @@ -1 +0,0 @@ -../.isort.cfg \ No newline at end of file diff --git a/association-backend/Dockerfile b/association-backend/Dockerfile deleted file mode 100644 index 16423b0d58..0000000000 --- a/association-backend/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG FUNCTION_DIR="/home/app/" - -FROM python:3.9-slim as build-stage - -ARG FUNCTION_DIR - -RUN mkdir -p ${FUNCTION_DIR} -WORKDIR ${FUNCTION_DIR} - -RUN apt-get update -y && apt-get install -y gcc libpq-dev - -ENV LIBRARY_PATH=/lib:/usr/lib - -COPY poetry.lock ${FUNCTION_DIR} -COPY pyproject.toml ${FUNCTION_DIR} - -RUN pip3 install poetry - -RUN poetry config virtualenvs.in-project true -RUN poetry install --no-dev -E lambda - -FROM python:3.9-slim - -ARG FUNCTION_DIR - -WORKDIR ${FUNCTION_DIR} - -COPY --from=build-stage ${FUNCTION_DIR}/.venv ${FUNCTION_DIR}/.venv - -RUN mkdir -p ${FUNCTION_DIR}/assets - -COPY . ${FUNCTION_DIR} - -ENTRYPOINT ["/home/app/.venv/bin/python", "-m", "awslambdaric"] -CMD [ "main.handler" ] diff --git a/association-backend/README.md b/association-backend/README.md deleted file mode 100644 index d4f9420698..0000000000 --- a/association-backend/README.md +++ /dev/null @@ -1 +0,0 @@ -# PyCon Italia website - Association Service diff --git a/association-backend/alembic.ini b/association-backend/alembic.ini deleted file mode 100644 index 0d78e89942..0000000000 --- a/association-backend/alembic.ini +++ /dev/null @@ -1,83 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# path to migration scripts -script_location = alembic - -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# timezone to use when rendering the date -# within the migration file as well as the filename. -# string value is passed to dateutil.tz.gettz() -# leave blank for localtime -# timezone = - -# max length of characters to apply to the -# "slug" field -# truncate_slug_length = 40 - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - -# set to 'true' to allow .pyc and .pyo files without -# a source .py file to be detected as revisions in the -# versions/ directory -# sourceless = false - -# version location specification; this defaults -# to alembic/versions. When using multiple version -# directories, initial revisions must be specified with --version-path -# version_locations = %(here)s/bar %(here)s/bat alembic/versions - -# the output encoding used when revision files -# are written from script.py.mako -# output_encoding = utf-8 - - -[post_write_hooks] -# post_write_hooks defines scripts or Python functions that are run -# on newly generated revision scripts. See the documentation for further -# detail and examples - -# format using "black" - use the console_scripts runner, against the "black" entrypoint -# hooks=black -# black.type=console_scripts -# black.entrypoint=black -# black.options=-l 79 - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/association-backend/alembic/README b/association-backend/alembic/README deleted file mode 100644 index 98e4f9c44e..0000000000 --- a/association-backend/alembic/README +++ /dev/null @@ -1 +0,0 @@ -Generic single-database configuration. \ No newline at end of file diff --git a/association-backend/alembic/env.py b/association-backend/alembic/env.py deleted file mode 100644 index 9252c90fa2..0000000000 --- a/association-backend/alembic/env.py +++ /dev/null @@ -1,85 +0,0 @@ -import os -import sys -from logging.config import fileConfig - -sys.path.insert(0, os.getcwd()) - -from alembic import context - -# Import models here -# Maybe improve this? -from src.association_membership.domain.entities import * - -from src.database.db import metadata -from src.association.settings import DATABASE_URL -from sqlalchemy import engine_from_config, pool - - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config - -# Interpret the config file for Python logging. -# This line sets up loggers basically. -fileConfig(config.config_file_name) - - -config.set_main_option("sqlalchemy.url", DATABASE_URL) - - -target_metadata = metadata - -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - - -def run_migrations_offline(): - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ - url = config.get_main_option("sqlalchemy.url") - context.configure( - url=url, - target_metadata=target_metadata, - literal_binds=True, - dialect_opts={"paramstyle": "named"}, - ) - - with context.begin_transaction(): - context.run_migrations() - - -def run_migrations_online(): - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - connectable = engine_from_config( - config.get_section(config.config_ini_section), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) - - with connectable.connect() as connection: - context.configure(connection=connection, target_metadata=target_metadata) - - with context.begin_transaction(): - context.run_migrations() - - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() diff --git a/association-backend/alembic/script.py.mako b/association-backend/alembic/script.py.mako deleted file mode 100644 index 2c0156303a..0000000000 --- a/association-backend/alembic/script.py.mako +++ /dev/null @@ -1,24 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} - -""" -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} -branch_labels = ${repr(branch_labels)} -depends_on = ${repr(depends_on)} - - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} diff --git a/association-backend/alembic/versions/94d0021c6380_new_structure.py b/association-backend/alembic/versions/94d0021c6380_new_structure.py deleted file mode 100644 index 4322692444..0000000000 --- a/association-backend/alembic/versions/94d0021c6380_new_structure.py +++ /dev/null @@ -1,79 +0,0 @@ -"""new_structure - -Revision ID: 94d0021c6380 -Revises: -Create Date: 2021-12-14 21:49:11.108263 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '94d0021c6380' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('stripe_customers', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('user_id', sa.Integer(), nullable=False), - sa.Column('stripe_customer_id', sa.String(length=256), nullable=False), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('stripe_customer_id'), - sa.UniqueConstraint('user_id') - ) - op.create_table('subscriptions', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('user_id', sa.Integer(), nullable=False), - sa.Column('status', sa.String(length=20), nullable=False), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('user_id') - ) - op.create_table('payments', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('subscription', sa.Integer(), nullable=False), - sa.Column('idempotency_key', sa.String(length=256), nullable=False), - sa.Column('total', sa.Integer(), nullable=False), - sa.Column('payment_date', sa.DateTime(timezone=True), nullable=False), - sa.Column('period_start', sa.DateTime(timezone=True), nullable=False), - sa.Column('period_end', sa.DateTime(timezone=True), nullable=False), - sa.Column('status', sa.String(length=20), nullable=False), - sa.ForeignKeyConstraint(['subscription'], ['subscriptions.id'], name='fk_payments_subscriptions_id_subscription'), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('idempotency_key') - ) - op.create_table('pretix_payments', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('payment', sa.Integer(), nullable=False), - sa.Column('order_code', sa.String(length=256), nullable=False), - sa.Column('event_organizer', sa.String(length=512), nullable=False), - sa.Column('event_id', sa.String(length=512), nullable=False), - sa.ForeignKeyConstraint(['payment'], ['payments.id'], name='fk_pretix_payments_payments_id_payment'), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('order_code') - ) - op.create_table('stripe_subscription_payments', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('payment', sa.Integer(), nullable=False), - sa.Column('stripe_subscription_id', sa.String(length=256), nullable=False), - sa.Column('stripe_invoice_id', sa.String(length=256), nullable=False), - sa.Column('invoice_pdf', sa.Text(), nullable=False), - sa.ForeignKeyConstraint(['payment'], ['payments.id'], name='fk_stripe_subscription_payments_payments_id_payment'), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('stripe_invoice_id') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('stripe_subscription_payments') - op.drop_table('pretix_payments') - op.drop_table('payments') - op.drop_table('subscriptions') - op.drop_table('stripe_customers') - # ### end Alembic commands ### diff --git a/association-backend/main.py b/association-backend/main.py deleted file mode 100644 index fe7cb19742..0000000000 --- a/association-backend/main.py +++ /dev/null @@ -1,108 +0,0 @@ -import asyncio -import logging -import subprocess -import sys -from io import StringIO - -from mangum import Mangum -from pythonit_toolkit.sentry.sentry import configure_sentry -from pythonit_toolkit.starlette_backend.pastaporto_backend import on_auth_error -from sentry_sdk.integrations.asgi import SentryAsgiMiddleware -from starlette.applications import Starlette -from starlette.middleware import Middleware -from starlette.middleware.authentication import AuthenticationMiddleware -from starlette.routing import Route - -from src.api.views import GraphQL -from src.association.auth import RouterAuthBackend -from src.association.settings import DEBUG, ENV, SENTRY_DSN -from src.database.db import database -from src.internal_api.views import GraphQL as InternalGraphQL -from src.webhooks.handlers import run_handler -from src.webhooks.views import pretix_webhook, stripe_webhook - -if SENTRY_DSN: - configure_sentry(dsn=str(SENTRY_DSN), env=ENV) - -logging.getLogger().setLevel(logging.INFO) -logging.basicConfig(level=logging.INFO) -logging.getLogger("sqlalchemy.engine.Engine").disabled = True - - -app = Starlette( - debug=DEBUG, - routes=[ - Route("/graphql", GraphQL()), - Route("/internal-api", InternalGraphQL()), - Route("/stripe-webhook", stripe_webhook, methods=["POST"]), - Route("/pretix-webhook", pretix_webhook, methods=["POST"]), - ], - middleware=[ - Middleware( - AuthenticationMiddleware, - backend=RouterAuthBackend(), - on_error=on_auth_error, - ), - ], -) - - -@app.on_event("startup") -async def startup(): - if not database.is_connected: - await database.connect() - - -@app.on_event("shutdown") -async def shutdown(): - if database.is_connected: - await database.disconnect() - - -wrapped_app = SentryAsgiMiddleware(app) - - -async def event_handler(event): - try: - await startup() - await run_handler("crons", event["name"], event["payload"]) - finally: - await shutdown() - - -def handler(event, context): - if command := event.get("_cli_command"): # noqa - native_stdout = sys.stdout - native_stderr = sys.stderr - output_buffer = StringIO() - - try: - sys.stdout = output_buffer - sys.stderr = output_buffer - - if command.get("action") == "migrate": - result = subprocess.check_output( - "/home/app/.venv/bin/alembic upgrade head", - shell=True, - stderr=subprocess.STDOUT, - ) - output_buffer.write(_to_native(result)) - finally: - sys.stdout = native_stdout - sys.stderr = native_stderr - - return {"output": output_buffer.getvalue()} - - if received_event := event.get("cronEvent"): - asyncio.run(event_handler(received_event)) - return - - asgi_handler = Mangum(wrapped_app) - response = asgi_handler(event, context) - return response - - -def _to_native(x, charset=sys.getdefaultencoding(), errors="strict"): # noqa - if x is None or isinstance(x, str): - return x - return x.decode(charset, errors) diff --git a/association-backend/poetry.lock b/association-backend/poetry.lock deleted file mode 100644 index 83c51984a1..0000000000 --- a/association-backend/poetry.lock +++ /dev/null @@ -1,1722 +0,0 @@ -[[package]] -name = "aiosqlite" -version = "0.17.0" -description = "asyncio bridge to the standard sqlite3 module" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -typing_extensions = ">=3.7.2" - -[[package]] -name = "alembic" -version = "1.8.0" -description = "A database migration tool for SQLAlchemy." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -Mako = "*" -SQLAlchemy = ">=1.3.0" - -[package.extras] -tz = ["python-dateutil"] - -[[package]] -name = "anyio" -version = "3.6.1" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16)"] - -[[package]] -name = "asgi-lifespan" -version = "1.0.1" -description = "Programmatic startup/shutdown of ASGI apps." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -sniffio = "*" - -[[package]] -name = "asyncpg" -version = "0.21.0" -description = "An asyncio PostgreSQL driver" -category = "main" -optional = false -python-versions = ">=3.5.0" - -[package.extras] -dev = ["Cython (==0.29.20)", "Sphinx (>=1.7.3,<1.8.0)", "flake8 (>=3.7.9,<3.8.0)", "pycodestyle (>=2.5.0,<2.6.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "uvloop (>=0.14.0,<0.15.0)"] -docs = ["Sphinx (>=1.7.3,<1.8.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)"] -test = ["flake8 (>=3.7.9,<3.8.0)", "pycodestyle (>=2.5.0,<2.6.0)", "uvloop (>=0.14.0,<0.15.0)"] - -[[package]] -name = "awslambdaric" -version = "2.0.4" -description = "AWS Lambda Runtime Interface Client for Python" -category = "main" -optional = true -python-versions = ">=3.6" - -[package.dependencies] -simplejson = "3.17.2" - -[[package]] -name = "boto3" -version = "1.24.26" -description = "The AWS SDK for Python" -category = "main" -optional = false -python-versions = ">= 3.7" - -[package.dependencies] -botocore = ">=1.27.26,<1.28.0" -jmespath = ">=0.7.1,<2.0.0" -s3transfer = ">=0.6.0,<0.7.0" - -[package.extras] -crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] - -[[package]] -name = "botocore" -version = "1.27.26" -description = "Low-level, data-driven core of boto 3." -category = "main" -optional = false -python-versions = ">= 3.7" - -[package.dependencies] -jmespath = ">=0.7.1,<2.0.0" -python-dateutil = ">=2.1,<3.0.0" -urllib3 = ">=1.25.4,<1.27" - -[package.extras] -crt = ["awscrt (==0.13.8)"] - -[[package]] -name = "cached-property" -version = "1.5.2" -description = "A decorator for caching properties in classes." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "certifi" -version = "2022.6.15" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "charset-normalizer" -version = "2.1.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.6.0" - -[package.extras] -unicode_backport = ["unicodedata2"] - -[[package]] -name = "click" -version = "7.1.2" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "click-completion" -version = "0.5.2" -description = "Fish, Bash, Zsh and PowerShell completion for Click" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -click = "*" -jinja2 = "*" -shellingham = "*" -six = "*" - -[[package]] -name = "click-default-group" -version = "1.2.2" -description = "Extends click.Group to invoke a command without explicit subcommand name" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -click = "*" - -[[package]] -name = "colorama" -version = "0.4.5" -description = "Cross-platform colored terminal text." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "commonmark" -version = "0.9.1" -description = "Python parser for the CommonMark Markdown spec" -category = "dev" -optional = false -python-versions = "*" - -[package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] - -[[package]] -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" - -[package.extras] -toml = ["toml"] - -[[package]] -name = "cucumber-tag-expressions" -version = "4.1.0" -description = "Provides tag-expression parser for cucumber/behave" -category = "dev" -optional = false -python-versions = ">=2.7" - -[package.extras] -develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest-html (>=1.19.0)", "tox (>=2.9)"] - -[[package]] -name = "databases" -version = "0.6.0" -description = "Async database support for Python." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -sqlalchemy = ">=1.4,<1.5" - -[package.extras] -aiomysql = ["aiomysql"] -aiopg = ["aiopg"] -aiosqlite = ["aiosqlite"] -asyncmy = ["asyncmy"] -asyncpg = ["asyncpg"] -mysql = ["aiomysql"] -postgresql = ["asyncpg"] -sqlite = ["aiosqlite"] - -[[package]] -name = "dnspython" -version = "2.2.1" -description = "DNS toolkit" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" - -[package.extras] -curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] -dnssec = ["cryptography (>=2.6,<37.0)"] -doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.10.0)"] -idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.20)"] -wmi = ["wmi (>=1.5.1,<2.0.0)"] - -[[package]] -name = "email-validator" -version = "1.2.1" -description = "A robust email syntax and deliverability validation library." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -dnspython = ">=1.15.0" -idna = ">=2.0.0" - -[[package]] -name = "factory-boy" -version = "3.2.1" -description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -Faker = ">=0.7.0" - -[package.extras] -dev = ["Django", "Pillow", "SQLAlchemy", "coverage", "flake8", "isort", "mongoengine", "tox", "wheel (>=0.32.0)", "zest.releaser[recommended]"] -doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"] - -[[package]] -name = "faker" -version = "8.16.0" -description = "Faker is a Python package that generates fake data for you." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -python-dateutil = ">=2.4" -text-unidecode = "1.3" - -[[package]] -name = "fancycompleter" -version = "0.9.1" -description = "colorful TAB completion for Python prompt" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -pyreadline = {version = "*", markers = "platform_system == \"Windows\""} -pyrepl = ">=0.8.2" - -[[package]] -name = "flake8" -version = "3.9.2" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.7.0,<2.8.0" -pyflakes = ">=2.3.0,<2.4.0" - -[[package]] -name = "graphql-core" -version = "3.1.7" -description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." -category = "main" -optional = false -python-versions = ">=3.6,<4" - -[[package]] -name = "greenlet" -version = "1.1.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.*" - -[package.extras] -docs = ["Sphinx"] - -[[package]] -name = "h11" -version = "0.12.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "httpcore" -version = "0.13.7" -description = "A minimal low-level HTTP client." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -anyio = ">=3.0.0,<4.0.0" -h11 = ">=0.11,<0.13" -sniffio = ">=1.0.0,<2.0.0" - -[package.extras] -http2 = ["h2 (>=3,<5)"] - -[[package]] -name = "httpx" -version = "0.20.0" -description = "The next generation HTTP client." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -certifi = "*" -charset-normalizer = "*" -httpcore = ">=0.13.3,<0.14.0" -rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10.0.0,<11.0.0)"] -http2 = ["h2 (>=3,<5)"] - -[[package]] -name = "idna" -version = "3.3" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "invoke" -version = "1.5.0" -description = "Pythonic task execution" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "isort" -version = "5.10.1" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.6.1,<4.0" - -[package.extras] -colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements_deprecated_finder = ["pip-api", "pipreqs"] - -[[package]] -name = "jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jmespath" -version = "1.0.1" -description = "JSON Matching Expressions" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "mako" -version = "1.2.1" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -MarkupSafe = ">=0.9.2" - -[package.extras] -babel = ["Babel"] -lingua = ["lingua"] -testing = ["pytest"] - -[[package]] -name = "mangum" -version = "0.11.0" -description = "AWS Lambda & API Gateway support for ASGI" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -typing-extensions = "*" - -[[package]] -name = "markupsafe" -version = "2.1.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "mslex" -version = "0.3.0" -description = "shlex for windows" -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "ormar" -version = "0.11.1" -description = "A simple async ORM with fastapi in mind and pydantic validation." -category = "main" -optional = false -python-versions = ">=3.7.0,<4.0.0" - -[package.dependencies] -aiosqlite = ">=0.17.0,<0.18.0" -databases = ">=0.3.2,<0.5.0 || >0.5.0,<0.5.1 || >0.5.1,<0.5.2 || >0.5.2,<0.5.3 || >0.5.3,<0.6.1" -pydantic = ">=1.6.1,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<=1.9.1" -SQLAlchemy = ">=1.3.18,<1.4.38" - -[package.extras] -crypto = ["cryptography (>=35,<38)"] -dev = ["aiomysql (>=0.0.21,<0.0.23)", "asyncpg (>=0.24,<0.26)", "cryptography (>=35,<38)", "orjson (>=3.6.4)", "psycopg2-binary (>=2.9.1,<3.0.0)"] -mysql = ["aiomysql (>=0.0.21,<0.0.23)"] -orjson = ["orjson (>=3.6.4)"] -postgres = ["asyncpg (>=0.24,<0.26)", "psycopg2-binary (>=2.9.1,<3.0.0)"] -postgresql = ["asyncpg (>=0.24,<0.26)", "psycopg2-binary (>=2.9.1,<3.0.0)"] - -[[package]] -name = "pdbpp" -version = "0.10.3" -description = "pdb++, a drop-in replacement for pdb" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -fancycompleter = ">=0.8" -pygments = "*" -wmctrl = "*" - -[package.extras] -funcsigs = ["funcsigs"] -testing = ["funcsigs", "pytest"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pprintpp" -version = "0.4.0" -description = "A drop-in replacement for pprint that's actually pretty" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "psutil" -version = "5.9.1" -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.*" - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - -[[package]] -name = "psycopg2" -version = "2.9.5" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pycodestyle" -version = "2.7.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pydantic" -version = "1.9.1" -description = "Data validation and settings management using python type hints" -category = "main" -optional = false -python-versions = ">=3.6.1" - -[package.dependencies] -typing-extensions = ">=3.7.4.3" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pyflakes" -version = "2.3.1" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pygments" -version = "2.12.0" -description = "Pygments is a syntax highlighting package written in Python." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pyjwt" -version = "2.0.1" -description = "JSON Web Token implementation in Python" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -crypto = ["cryptography (>=3.3.1,<4.0.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.3.1,<4.0.0)", "mypy", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - -[[package]] -name = "pyreadline" -version = "2.1" -description = "A python implmementation of GNU readline." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "pyrepl" -version = "0.9.0" -description = "A library for building flexible command line interfaces" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -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" - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-multipart" -version = "0.0.5" -description = "A streaming multipart parser for Python" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -six = ">=1.4.0" - -[[package]] -name = "pythonit-toolkit" -version = "0.1.81" -description = "" -category = "main" -optional = false -python-versions = ">=3.9,<4.0" - -[package.dependencies] -boto3 = ">=1.17.39,<2.0.0" -httpx = ">=0.20.0,<0.21.0" -pydantic = "*" -PyJWT = "2.0.1" -sentry-sdk = ">=1.1.0,<2.0.0" -starlette = ">=0.14.2,<0.15.0" -strawberry-graphql = "*" - -[[package]] -name = "requests" -version = "2.28.1" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7, <4" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "respx" -version = "0.18.2" -description = "A utility for mocking out the Python HTTPX and HTTP Core libraries." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -httpx = ">=0.20.0" - -[[package]] -name = "rfc3986" -version = "1.5.0" -description = "Validating URI References per RFC 3986" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} - -[package.extras] -idna2008 = ["idna"] - -[[package]] -name = "rich" -version = "10.16.2" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "dev" -optional = false -python-versions = ">=3.6.2,<4.0.0" - -[package.dependencies] -colorama = ">=0.4.0,<0.5.0" -commonmark = ">=0.9.0,<0.10.0" -pygments = ">=2.6.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] - -[[package]] -name = "s3transfer" -version = "0.6.0" -description = "An Amazon S3 Transfer Manager" -category = "main" -optional = false -python-versions = ">= 3.7" - -[package.dependencies] -botocore = ">=1.12.36,<2.0a.0" - -[package.extras] -crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"] - -[[package]] -name = "sentinel" -version = "0.3.0" -description = "Create sentinel objects, akin to None, NotImplemented, Ellipsis" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" - -[package.extras] -varname = ["varname (>=0.1)"] - -[[package]] -name = "sentry-sdk" -version = "1.6.0" -description = "Python client for Sentry (https://sentry.io)" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -certifi = "*" -urllib3 = ">=1.10.0" - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)"] -httpx = ["httpx (>=0.16.0)"] -pure_eval = ["asttokens", "executing", "pure-eval"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "shellingham" -version = "1.4.0" -description = "Tool to Detect Surrounding Shell" -category = "dev" -optional = false -python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,>=2.6" - -[[package]] -name = "simplejson" -version = "3.17.2" -description = "Simple, fast, extensible JSON encoder/decoder for Python" -category = "main" -optional = true -python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -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.*" - -[[package]] -name = "sniffio" -version = "1.2.0" -description = "Sniff out which async library your code is running under" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "sqlalchemy" -version = "1.4.37" -description = "Database Abstraction Library" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" - -[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)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] -mariadb_connector = ["mariadb (>=1.0.1)"] -mssql = ["pyodbc"] -mssql_pymssql = ["pymssql"] -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)"] -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"] - -[[package]] -name = "starlette" -version = "0.14.2" -description = "The little ASGI library that shines." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] - -[[package]] -name = "strawberry-graphql" -version = "0.90.3" -description = "A library for creating GraphQL APIs" -category = "main" -optional = false -python-versions = ">=3.7,<4.0" - -[package.dependencies] -cached-property = ">=1.5.2,<2.0.0" -click = ">=7.0,<9.0" -graphql-core = ">=3.1.0,<3.2.0" -pygments = ">=2.3,<3.0" -python-dateutil = ">=2.7.0,<3.0.0" -python-multipart = ">=0.0.5,<0.0.6" -sentinel = ">=0.3.0,<0.4.0" -typing_extensions = ">=3.7.4,<5.0.0" - -[package.extras] -aiohttp = ["aiohttp (>=3.7.4.post0,<4.0.0)"] -asgi = ["starlette (>=0.13.6,<0.17.0)"] -chalice = ["chalice (>=1.22,<2.0)"] -debug-server = ["starlette (>=0.13.6,<0.17.0)", "uvicorn (>=0.11.6,<0.16.0)"] -django = ["asgiref (>=3.2,<4.0)", "django (>=2,<4)"] -fastapi = ["fastapi (>=0.65.2)"] -flask = ["flask (>=1.1,<2.0)"] -opentelemetry = ["opentelemetry-api (<2)", "opentelemetry-sdk (<2)"] -pydantic = ["pydantic (<2)"] -sanic = ["sanic (>=20.12.2,<22.0.0)"] - -[[package]] -name = "stripe" -version = "2.76.0" -description = "Python bindings for the Stripe API" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -requests = {version = ">=2.20", markers = "python_version >= \"3.0\""} - -[[package]] -name = "taskipy" -version = "1.10.1" -description = "tasks runner for python projects" -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -colorama = ">=0.4.4,<0.5.0" -mslex = {version = ">=0.3.0,<0.4.0", markers = "sys_platform == \"win32\""} -psutil = ">=5.7.2,<6.0.0" -tomli = ">=1.2.3,<2.0.0" - -[[package]] -name = "text-unidecode" -version = "1.3" -description = "The most basic Text::Unidecode port" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "time-machine" -version = "2.7.1" -description = "Travel through time in your tests." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -python-dateutil = "*" - -[[package]] -name = "tomli" -version = "1.2.3" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "typing-extensions" -version = "4.3.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "urllib3" -version = "1.26.10" -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.*, <4" - -[package.extras] -brotli = ["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)"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "uvicorn" -version = "0.13.4" -description = "The lightning-fast ASGI server." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -click = ">=7.0.0,<8.0.0" -h11 = ">=0.8" - -[package.extras] -standard = ["PyYAML (>=5.1)", "colorama (>=0.4)", "httptools (>=0.1.0,<0.2.0)", "python-dotenv (>=0.13)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchgod (>=0.6)", "websockets (>=8.0.0,<9.0.0)"] - -[[package]] -name = "ward" -version = "0.65.0b0" -description = "A modern Python testing framework" -category = "dev" -optional = false -python-versions = ">=3.6.1,<4.0.0" - -[package.dependencies] -click = ">=7,<9" -click-completion = ">=0.5.2,<0.6.0" -click-default-group = ">=1.2.2,<2.0.0" -cucumber-tag-expressions = ">=2.0.0,<5.0.0" -pluggy = ">=0.13.1,<2.0.0" -pprintpp = ">=0.4.0,<0.5.0" -rich = ">=10.0.0,<11.0.0" -tomli = ">=1.0.0,<2.0.0" - -[[package]] -name = "websockets" -version = "8.1" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" -optional = false -python-versions = ">=3.6.1" - -[[package]] -name = "wmctrl" -version = "0.4" -description = "A tool to programmatically control windows inside X" -category = "dev" -optional = false -python-versions = "*" - -[extras] -lambda = ["awslambdaric"] - -[metadata] -lock-version = "1.1" -python-versions = "^3.9" -content-hash = "2d55e80b0fec85a03a40e9d894211ca7539bc10d7991797edcee45c8835fb857" - -[metadata.files] -aiosqlite = [ - {file = "aiosqlite-0.17.0-py3-none-any.whl", hash = "sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231"}, - {file = "aiosqlite-0.17.0.tar.gz", hash = "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51"}, -] -alembic = [ - {file = "alembic-1.8.0-py3-none-any.whl", hash = "sha256:b5ae4bbfc7d1302ed413989d39474d102e7cfa158f6d5969d2497955ffe85a30"}, - {file = "alembic-1.8.0.tar.gz", hash = "sha256:a2d4d90da70b30e70352cd9455e35873a255a31402a438fe24815758d7a0e5e1"}, -] -anyio = [ - {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"}, - {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"}, -] -asgi-lifespan = [ - {file = "asgi-lifespan-1.0.1.tar.gz", hash = "sha256:9a33e7da2073c4764bc79bd6136501d6c42f60e3d2168ba71235e84122eadb7f"}, - {file = "asgi_lifespan-1.0.1-py3-none-any.whl", hash = "sha256:9ea969dc5eb5cf08e52c08dce6f61afcadd28112e72d81c972b1d8eb8691ab53"}, -] -asyncpg = [ - {file = "asyncpg-0.21.0-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:09badce47a4645cfe523cc8a182bd047d5d62af0caaea77935e6a3c9e77dc364"}, - {file = "asyncpg-0.21.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b7807bfedd24dd15cfb2c17c60977ce01410615ecc285268b5144a944ec97ff"}, - {file = "asyncpg-0.21.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dfd491e9865e64a3e91f1587b1d88d71dde1cfb850429253a73d4d44b98c3a0f"}, - {file = "asyncpg-0.21.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:8587e206d78e739ca83a40c9982e03b28f8904c95a54dc782da99e86cf768f73"}, - {file = "asyncpg-0.21.0-cp35-cp35m-win32.whl", hash = "sha256:b1b10916c006e5c2c0dcd5dadeb38cbf61ecd20d66c50164e82f31c22c7e329d"}, - {file = "asyncpg-0.21.0-cp35-cp35m-win_amd64.whl", hash = "sha256:22d161618b59e4b56fb2a5cc956aa9eeb336d07cae924a5b90c9aa1c2d137f15"}, - {file = "asyncpg-0.21.0-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:f2d1aa890ffd1ad062a38b7ff7488764b3da4b0a24e0c83d7bbb1d1a6609df15"}, - {file = "asyncpg-0.21.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e7bfb9269aeb11d78d50accf1be46823683ced99209b7199e307cdf7da849522"}, - {file = "asyncpg-0.21.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:68f7981f65317a5d5f497ec76919b488dbe0e838f8b924e7517a680bdca0f308"}, - {file = "asyncpg-0.21.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:a4c1feb285ec3807ecd5b54ab718a3d065bb55c93ebaf800670eadde31484be8"}, - {file = "asyncpg-0.21.0-cp36-cp36m-win32.whl", hash = "sha256:dddf4d4c5e781310a36529c3c87c1746837c2d2c7ec0f2ec4e4f06450d83c50a"}, - {file = "asyncpg-0.21.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7ee29c4707eb8fb3d3a0348ac4495e06f4afaca3ee38c3bebedc9c8b239125ff"}, - {file = "asyncpg-0.21.0-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:4421407b07b4e22291a226d9de0bf6f3ea8158aa1c12d83bfedbf5c22e13cd55"}, - {file = "asyncpg-0.21.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:aa2e0cb14c01a2f58caeeca7196681b30aa22dd22c82845560b401df5e98e171"}, - {file = "asyncpg-0.21.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:28584783dd0d21b2a0db3bfe54fb12f21425a4cc015e4419083ea99e6de0de9b"}, - {file = "asyncpg-0.21.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:915cebc8a7693c8a5e89804fa106678dbedcc50d0270ebab0b75f16e668bd59b"}, - {file = "asyncpg-0.21.0-cp37-cp37m-win32.whl", hash = "sha256:308b8ba32c42ea1ed84c034320678ec307296bb4faf3fbbeb9f9e20b46db99a5"}, - {file = "asyncpg-0.21.0-cp37-cp37m-win_amd64.whl", hash = "sha256:888593b6688faa7ec1c97ff7f2ca3b5a5b8abb15478fe2a13c5012b607a28737"}, - {file = "asyncpg-0.21.0-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:ecd5232cf64f58caac3b85103f1223fdf20e9eb43bfa053c56ef9e5dd76ab099"}, - {file = "asyncpg-0.21.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3ade59cef35bffae6dbc6f5f3ef56e1d53c67f0a7adc3cc4c714f07568d2d717"}, - {file = "asyncpg-0.21.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ea26604932719b3612541e606508d9d604211f56a65806ccf8c92c64104f4f8a"}, - {file = "asyncpg-0.21.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e51d1a012b779e0ebf0195f80d004f65d3c60cc06f0fa1cef9d3e536262abbd"}, - {file = "asyncpg-0.21.0-cp38-cp38-win32.whl", hash = "sha256:615c7e3adb46e1f2e3aff45e4ee9401b4f24f9f7153e5530a0753369be72a5c6"}, - {file = "asyncpg-0.21.0-cp38-cp38-win_amd64.whl", hash = "sha256:823eca36108bd64a8600efe7bbf1230aa00f2defa3be42852f3b61ab40cf1226"}, - {file = "asyncpg-0.21.0.tar.gz", hash = "sha256:53cb2a0eb326f61e34ef4da2db01d87ce9c0ebe396f65a295829df334e31863f"}, -] -awslambdaric = [ - {file = "awslambdaric-2.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e90053614f0e5e5d6d6ae6d164412ce95b5d549c6fb0f6ff4290d77c5e9d3e5"}, - {file = "awslambdaric-2.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bad98f2f94cecc90b89ac4e1d4feed96eb664e13c29b7ce232444cc9358e0d36"}, - {file = "awslambdaric-2.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11a365164efec105aa670259dfe473d9609da8f6f2e468790b2dfc24969bfff1"}, - {file = "awslambdaric-2.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe76893a1b42bcee4c91c6456092d2a42455818756e8f62d50e8c5adb22fa9e7"}, - {file = "awslambdaric-2.0.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2eb2fdb1ae0f84669d37f193f247fa115a282a7777e051ced3a33620d6280646"}, - {file = "awslambdaric-2.0.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d64dcba8da9dbea62644133a48c75376a37bfe0f84096ad73bf7fc5b2eb31fc7"}, - {file = "awslambdaric-2.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc7072f642fdd215387d4921bbd5ac91b96a4a705bce5e7853622d09fe59f57d"}, - {file = "awslambdaric-2.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:676a741ad8f3aa27d651bcf3a2b83d5cee815f99c8b2b9abef3cb22ca7b29698"}, - {file = "awslambdaric-2.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8f280b25d8a7ae6b6ff92a9bbc6567b984264be8ef3e0fcb0402a1247f6c75d"}, - {file = "awslambdaric-2.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38f8ae67ecb5b4e9f7fc42746ee39765dd7ddab359cb7e8ebfda1de0f0c0b059"}, - {file = "awslambdaric-2.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:387b94cb0358662ae2b203f0aa2af25e80c6a2019a6b569f733ecd993a4f53d2"}, - {file = "awslambdaric-2.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63a82d21d66146b3fde7eb6086abd058b75bdcab4a02b02afe0e8e4a45edfb5b"}, - {file = "awslambdaric-2.0.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:059c7a66d4470169e01620d93f07424b80d302e3736cd11e68373f293a41e396"}, - {file = "awslambdaric-2.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fd0e1b3891987fa7ebb0c08d24c76af5fc17466f6efdfa9a59848dfb23930ec"}, - {file = "awslambdaric-2.0.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b0781bd41c20a2f2a0b018464a1daa376f663bd5eb7b0b6ba78f483681b1519"}, - {file = "awslambdaric-2.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbbd24446ce2f876335b178f04aa4ec7ec480afc0f9621ebfdd5f55ad4b7c06e"}, - {file = "awslambdaric-2.0.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19da28e8c892b1c52a9db4d2b986af303932e3a4c4632eb0c5d5eb6a673c6022"}, - {file = "awslambdaric-2.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efff2292fc8f8484eb094ffd77808a67815353be898a7f0b33ce51b841af691"}, - {file = "awslambdaric-2.0.4.tar.gz", hash = "sha256:dad646f566aa7ec9b7179f16ca6741a2bea148abec6ed5947f86d00607e0a9a2"}, -] -boto3 = [ - {file = "boto3-1.24.26-py3-none-any.whl", hash = "sha256:b8e79df81b57c40b3ddf7bfe657bf239c0ae70f6c6fab9fa3784d4f30253d29c"}, - {file = "boto3-1.24.26.tar.gz", hash = "sha256:46b0909379955d70ae995ecd26a03e4842104da81ecdc38b83b5701d70e87c2e"}, -] -botocore = [ - {file = "botocore-1.27.26-py3-none-any.whl", hash = "sha256:0436b1e3fae36ea53760380e87cd80f94c8cdd3085c84a8a431d1182cf5b99e8"}, - {file = "botocore-1.27.26.tar.gz", hash = "sha256:4a46d0508470883dc97ab8e9769617f21229f4ff8c146a47379f4042d917e520"}, -] -cached-property = [ - {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, - {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, -] -certifi = [ - {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, - {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"}, - {file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"}, -] -click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, -] -click-completion = [ - {file = "click-completion-0.5.2.tar.gz", hash = "sha256:5bf816b81367e638a190b6e91b50779007d14301b3f9f3145d68e3cade7bce86"}, -] -click-default-group = [ - {file = "click-default-group-1.2.2.tar.gz", hash = "sha256:d9560e8e8dfa44b3562fbc9425042a0fd6d21956fcc2db0077f63f34253ab904"}, -] -colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] -commonmark = [ - {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, - {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, -] -coverage = [ - {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, - {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, - {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, - {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, - {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, - {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, - {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, - {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, - {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, - {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, - {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, - {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, - {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, - {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, - {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, - {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, - {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, - {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, - {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, - {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, - {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, - {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, - {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, - {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, -] -cucumber-tag-expressions = [ - {file = "cucumber-tag-expressions-4.1.0.tar.gz", hash = "sha256:e314d5fed6eebb2f90380271f562248fb15e18636764faf40f4dde4b28b1f960"}, -] -databases = [ - {file = "databases-0.6.0-py3-none-any.whl", hash = "sha256:c36468d9e00f47a825669a73158e6745e0401a169186fdefbb2fb7d43276320a"}, - {file = "databases-0.6.0.tar.gz", hash = "sha256:abf088900e6665952fede331cb126a1810a097fb3aad54e02a4f58521419dabf"}, -] -dnspython = [ - {file = "dnspython-2.2.1-py3-none-any.whl", hash = "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"}, - {file = "dnspython-2.2.1.tar.gz", hash = "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e"}, -] -email-validator = [ - {file = "email_validator-1.2.1-py2.py3-none-any.whl", hash = "sha256:c8589e691cf73eb99eed8d10ce0e9cbb05a0886ba920c8bcb7c82873f4c5789c"}, - {file = "email_validator-1.2.1.tar.gz", hash = "sha256:6757aea012d40516357c0ac2b1a4c31219ab2f899d26831334c5d069e8b6c3d8"}, -] -factory-boy = [ - {file = "factory_boy-3.2.1-py2.py3-none-any.whl", hash = "sha256:eb02a7dd1b577ef606b75a253b9818e6f9eaf996d94449c9d5ebb124f90dc795"}, - {file = "factory_boy-3.2.1.tar.gz", hash = "sha256:a98d277b0c047c75eb6e4ab8508a7f81fb03d2cb21986f627913546ef7a2a55e"}, -] -faker = [ - {file = "Faker-8.16.0-py3-none-any.whl", hash = "sha256:bb10913b9d3ac2aa37180f816c82040e81f9e0c32cb08445533f293cec8930bf"}, - {file = "Faker-8.16.0.tar.gz", hash = "sha256:d70b375d0af0e4c3abd594003691a1055a96281a414884e623d27bccc7d781da"}, -] -fancycompleter = [ - {file = "fancycompleter-0.9.1-py3-none-any.whl", hash = "sha256:dd076bca7d9d524cc7f25ec8f35ef95388ffef9ef46def4d3d25e9b044ad7080"}, - {file = "fancycompleter-0.9.1.tar.gz", hash = "sha256:09e0feb8ae242abdfd7ef2ba55069a46f011814a80fe5476be48f51b00247272"}, -] -flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, -] -graphql-core = [ - {file = "graphql-core-3.1.7.tar.gz", hash = "sha256:62ec192150ccecd9a18cfb79e3e72eb7d1fd68fb594ef19c40099b6deec8ef0c"}, - {file = "graphql_core-3.1.7-py3-none-any.whl", hash = "sha256:9b460f60320be01c7f3b1766cf3e406933003008055079b9d983b8f3988f4400"}, -] -greenlet = [ - {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d"}, - {file = "greenlet-1.1.2-cp27-cp27m-win32.whl", hash = "sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713"}, - {file = "greenlet-1.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8"}, - {file = "greenlet-1.1.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"}, - {file = "greenlet-1.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965"}, - {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"}, - {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c"}, - {file = "greenlet-1.1.2-cp35-cp35m-win32.whl", hash = "sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963"}, - {file = "greenlet-1.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e"}, - {file = "greenlet-1.1.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"}, - {file = "greenlet-1.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f"}, - {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"}, - {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"}, - {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"}, - {file = "greenlet-1.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe"}, - {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"}, - {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"}, - {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"}, - {file = "greenlet-1.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2"}, - {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"}, - {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"}, - {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"}, - {file = "greenlet-1.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3"}, - {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"}, - {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"}, - {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, -] -h11 = [ - {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, - {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, -] -httpcore = [ - {file = "httpcore-0.13.7-py3-none-any.whl", hash = "sha256:369aa481b014cf046f7067fddd67d00560f2f00426e79569d99cb11245134af0"}, - {file = "httpcore-0.13.7.tar.gz", hash = "sha256:036f960468759e633574d7c121afba48af6419615d36ab8ede979f1ad6276fa3"}, -] -httpx = [ - {file = "httpx-0.20.0-py3-none-any.whl", hash = "sha256:33af5aad9bdc82ef1fc89219c1e36f5693bf9cd0ebe330884df563445682c0f8"}, - {file = "httpx-0.20.0.tar.gz", hash = "sha256:09606d630f070d07f9ff28104fbcea429ea0014c1e89ac90b4d8de8286c40e7b"}, -] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] -invoke = [ - {file = "invoke-1.5.0-py2-none-any.whl", hash = "sha256:da7c2d0be71be83ffd6337e078ef9643f41240024d6b2659e7b46e0b251e339f"}, - {file = "invoke-1.5.0-py3-none-any.whl", hash = "sha256:7e44d98a7dc00c91c79bac9e3007276965d2c96884b3c22077a9f04042bd6d90"}, - {file = "invoke-1.5.0.tar.gz", hash = "sha256:f0c560075b5fb29ba14dad44a7185514e94970d1b9d57dcd3723bec5fed92650"}, -] -isort = [ - {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, -] -jinja2 = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] -jmespath = [ - {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, - {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, -] -mako = [ - {file = "Mako-1.2.1-py3-none-any.whl", hash = "sha256:df3921c3081b013c8a2d5ff03c18375651684921ae83fd12e64800b7da923257"}, - {file = "Mako-1.2.1.tar.gz", hash = "sha256:f054a5ff4743492f1aa9ecc47172cb33b42b9d993cffcc146c9de17e717b0307"}, -] -mangum = [ - {file = "mangum-0.11.0-py3-none-any.whl", hash = "sha256:516309ba6d3d62c8fa8371d2779fc41e4f3dc1df91967e83e97db027e56dba1d"}, - {file = "mangum-0.11.0.tar.gz", hash = "sha256:4b8e69030ba2182d4ce089620ee5c5b6075ae3cc5cedb00ab1d3db264231980e"}, -] -markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mslex = [ - {file = "mslex-0.3.0-py2.py3-none-any.whl", hash = "sha256:380cb14abf8fabf40e56df5c8b21a6d533dc5cbdcfe42406bbf08dda8f42e42a"}, - {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"}, -] -ormar = [ - {file = "ormar-0.11.1-py3-none-any.whl", hash = "sha256:a12f83165502b515310a1c20fbccbbcb9a1257716040d74c24503adbb890b86a"}, - {file = "ormar-0.11.1.tar.gz", hash = "sha256:f72733b3727794a1b3e03effa95377f632e4019bda5e06889632c89393ddc69f"}, -] -pdbpp = [ - {file = "pdbpp-0.10.3-py2.py3-none-any.whl", hash = "sha256:79580568e33eb3d6f6b462b1187f53e10cd8e4538f7d31495c9181e2cf9665d1"}, - {file = "pdbpp-0.10.3.tar.gz", hash = "sha256:d9e43f4fda388eeb365f2887f4e7b66ac09dce9b6236b76f63616530e2f669f5"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -pprintpp = [ - {file = "pprintpp-0.4.0-py2.py3-none-any.whl", hash = "sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d"}, - {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, -] -psutil = [ - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"}, - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"}, - {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"}, - {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"}, - {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"}, - {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"}, - {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"}, - {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"}, - {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"}, - {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"}, - {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"}, - {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"}, - {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"}, - {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"}, - {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"}, - {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"}, - {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"}, - {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"}, - {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"}, - {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"}, -] -psycopg2 = [ - {file = "psycopg2-2.9.5-cp310-cp310-win32.whl", hash = "sha256:d3ef67e630b0de0779c42912fe2cbae3805ebaba30cda27fea2a3de650a9414f"}, - {file = "psycopg2-2.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:4cb9936316d88bfab614666eb9e32995e794ed0f8f6b3b718666c22819c1d7ee"}, - {file = "psycopg2-2.9.5-cp311-cp311-win32.whl", hash = "sha256:093e3894d2d3c592ab0945d9eba9d139c139664dcf83a1c440b8a7aa9bb21955"}, - {file = "psycopg2-2.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:920bf418000dd17669d2904472efeab2b20546efd0548139618f8fa305d1d7ad"}, - {file = "psycopg2-2.9.5-cp36-cp36m-win32.whl", hash = "sha256:b9ac1b0d8ecc49e05e4e182694f418d27f3aedcfca854ebd6c05bb1cffa10d6d"}, - {file = "psycopg2-2.9.5-cp36-cp36m-win_amd64.whl", hash = "sha256:fc04dd5189b90d825509caa510f20d1d504761e78b8dfb95a0ede180f71d50e5"}, - {file = "psycopg2-2.9.5-cp37-cp37m-win32.whl", hash = "sha256:922cc5f0b98a5f2b1ff481f5551b95cd04580fd6f0c72d9b22e6c0145a4840e0"}, - {file = "psycopg2-2.9.5-cp37-cp37m-win_amd64.whl", hash = "sha256:1e5a38aa85bd660c53947bd28aeaafb6a97d70423606f1ccb044a03a1203fe4a"}, - {file = "psycopg2-2.9.5-cp38-cp38-win32.whl", hash = "sha256:f5b6320dbc3cf6cfb9f25308286f9f7ab464e65cfb105b64cc9c52831748ced2"}, - {file = "psycopg2-2.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:1a5c7d7d577e0eabfcf15eb87d1e19314c8c4f0e722a301f98e0e3a65e238b4e"}, - {file = "psycopg2-2.9.5-cp39-cp39-win32.whl", hash = "sha256:322fd5fca0b1113677089d4ebd5222c964b1760e361f151cbb2706c4912112c5"}, - {file = "psycopg2-2.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:190d51e8c1b25a47484e52a79638a8182451d6f6dff99f26ad9bd81e5359a0fa"}, - {file = "psycopg2-2.9.5.tar.gz", hash = "sha256:a5246d2e683a972e2187a8714b5c2cf8156c064629f9a9b1a873c1730d9e245a"}, -] -pycodestyle = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, -] -pydantic = [ - {file = "pydantic-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193"}, - {file = "pydantic-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11"}, - {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310"}, - {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131"}, - {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580"}, - {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd"}, - {file = "pydantic-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd"}, - {file = "pydantic-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761"}, - {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918"}, - {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74"}, - {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a"}, - {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166"}, - {file = "pydantic-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b"}, - {file = "pydantic-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892"}, - {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e"}, - {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608"}, - {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537"}, - {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380"}, - {file = "pydantic-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728"}, - {file = "pydantic-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a"}, - {file = "pydantic-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1"}, - {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195"}, - {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b"}, - {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49"}, - {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"}, - {file = "pydantic-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0"}, - {file = "pydantic-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6"}, - {file = "pydantic-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810"}, - {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f"}, - {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee"}, - {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761"}, - {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd"}, - {file = "pydantic-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1"}, - {file = "pydantic-1.9.1-py3-none-any.whl", hash = "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58"}, - {file = "pydantic-1.9.1.tar.gz", hash = "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a"}, -] -pyflakes = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, -] -pygments = [ - {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, - {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, -] -pyjwt = [ - {file = "PyJWT-2.0.1-py3-none-any.whl", hash = "sha256:b70b15f89dc69b993d8a8d32c299032d5355c82f9b5b7e851d1a6d706dffe847"}, - {file = "PyJWT-2.0.1.tar.gz", hash = "sha256:a5c70a06e1f33d81ef25eecd50d50bd30e34de1ca8b2b9fa3fe0daaabcf69bf7"}, -] -pyreadline = [ - {file = "pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1"}, -] -pyrepl = [ - {file = "pyrepl-0.9.0.tar.gz", hash = "sha256:292570f34b5502e871bbb966d639474f2b57fbfcd3373c2d6a2f3d56e681a775"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -python-multipart = [ - {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"}, -] -pythonit-toolkit = [ - {file = "pythonit-toolkit-0.1.81.tar.gz", hash = "sha256:ec92af55989f40cacc664aae8fbe05c31a5f295ac13abdff3503cd8ad83fc22a"}, - {file = "pythonit_toolkit-0.1.81-py3-none-any.whl", hash = "sha256:8b5b5aba6bb63dcd7eba523bc31de8b2e4a04dfadfa0a6e753d2f616d640f0b3"}, -] -requests = [ - {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, - {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, -] -respx = [ - {file = "respx-0.18.2-py2.py3-none-any.whl", hash = "sha256:09fb4fc49f3900fb124d13b188c90355a45efde29fd18c5e2f513804cf7f84c6"}, - {file = "respx-0.18.2.tar.gz", hash = "sha256:d39ff874f514dc4293b2e1f7c5e6c8c2ad871ee02ef49b3f57535257008495d8"}, -] -rfc3986 = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, -] -rich = [ - {file = "rich-10.16.2-py3-none-any.whl", hash = "sha256:c59d73bd804c90f747c8d7b1d023b88f2a9ac2454224a4aeaf959b21eeb42d03"}, - {file = "rich-10.16.2.tar.gz", hash = "sha256:720974689960e06c2efdb54327f8bf0cdbdf4eae4ad73b6c94213cad405c371b"}, -] -s3transfer = [ - {file = "s3transfer-0.6.0-py3-none-any.whl", hash = "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd"}, - {file = "s3transfer-0.6.0.tar.gz", hash = "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"}, -] -sentinel = [ - {file = "sentinel-0.3.0-py3-none-any.whl", hash = "sha256:bd8710dd26752039c668604f6be2aaf741b56f7811c5924a4dcdfd74359244f3"}, - {file = "sentinel-0.3.0.tar.gz", hash = "sha256:f28143aa4716dbc8f6193f5682176a3c33cd26aaae05d9ecf66c186a9887cc2d"}, -] -sentry-sdk = [ - {file = "sentry-sdk-1.6.0.tar.gz", hash = "sha256:b82ad57306d5546713f15d5d70daea0408cf7f998c7566db16e0e6257e51e561"}, - {file = "sentry_sdk-1.6.0-py2.py3-none-any.whl", hash = "sha256:ddbd191b6f4e696b7845b4d87389898ae1207981faf114f968a57363aa6be03c"}, -] -shellingham = [ - {file = "shellingham-1.4.0-py2.py3-none-any.whl", hash = "sha256:536b67a0697f2e4af32ab176c00a50ac2899c5a05e0d8e2dadac8e58888283f9"}, - {file = "shellingham-1.4.0.tar.gz", hash = "sha256:4855c2458d6904829bd34c299f11fdeed7cfefbf8a2c522e4caea6cd76b3171e"}, -] -simplejson = [ - {file = "simplejson-3.17.2-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8"}, - {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6"}, - {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:292c2e3f53be314cc59853bd20a35bf1f965f3bc121e007ab6fd526ed412a85d"}, - {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0dd9d9c738cb008bfc0862c9b8fa6743495c03a0ed543884bf92fb7d30f8d043"}, - {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:42b8b8dd0799f78e067e2aaae97e60d58a8f63582939af60abce4c48631a0aa4"}, - {file = "simplejson-3.17.2-cp27-cp27m-win32.whl", hash = "sha256:8042040af86a494a23c189b5aa0ea9433769cc029707833f261a79c98e3375f9"}, - {file = "simplejson-3.17.2-cp27-cp27m-win_amd64.whl", hash = "sha256:034550078a11664d77bc1a8364c90bb7eef0e44c2dbb1fd0a4d92e3997088667"}, - {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:fed0f22bf1313ff79c7fc318f7199d6c2f96d4de3234b2f12a1eab350e597c06"}, - {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:2e7b57c2c146f8e4dadf84977a83f7ee50da17c8861fd7faf694d55e3274784f"}, - {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:da3c55cdc66cfc3fffb607db49a42448785ea2732f055ac1549b69dcb392663b"}, - {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c1cb29b1fced01f97e6d5631c3edc2dadb424d1f4421dad079cb13fc97acb42f"}, - {file = "simplejson-3.17.2-cp33-cp33m-win32.whl", hash = "sha256:8f713ea65958ef40049b6c45c40c206ab363db9591ff5a49d89b448933fa5746"}, - {file = "simplejson-3.17.2-cp33-cp33m-win_amd64.whl", hash = "sha256:344e2d920a7f27b4023c087ab539877a1e39ce8e3e90b867e0bfa97829824748"}, - {file = "simplejson-3.17.2-cp34-cp34m-win32.whl", hash = "sha256:05b43d568300c1cd43f95ff4bfcff984bc658aa001be91efb3bb21df9d6288d3"}, - {file = "simplejson-3.17.2-cp34-cp34m-win_amd64.whl", hash = "sha256:cff6453e25204d3369c47b97dd34783ca820611bd334779d22192da23784194b"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a"}, - {file = "simplejson-3.17.2-cp35-cp35m-win32.whl", hash = "sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396"}, - {file = "simplejson-3.17.2-cp35-cp35m-win_amd64.whl", hash = "sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a"}, - {file = "simplejson-3.17.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:ffd4e4877a78c84d693e491b223385e0271278f5f4e1476a4962dca6824ecfeb"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:10fc250c3edea4abc15d930d77274ddb8df4803453dde7ad50c2f5565a18a4bb"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:76ac9605bf2f6d9b56abf6f9da9047a8782574ad3531c82eae774947ae99cc3f"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:7f10f8ba9c1b1430addc7dd385fc322e221559d3ae49b812aebf57470ce8de45"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bc00d1210567a4cdd215ac6e17dc00cb9893ee521cee701adfd0fa43f7c73139"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:af4868da7dd53296cd7630687161d53a7ebe2e63814234631445697bd7c29f46"}, - {file = "simplejson-3.17.2-cp36-cp36m-win32.whl", hash = "sha256:7d276f69bfc8c7ba6c717ba8deaf28f9d3c8450ff0aa8713f5a3280e232be16b"}, - {file = "simplejson-3.17.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a55c76254d7cf8d4494bc508e7abb993a82a192d0db4552421e5139235604625"}, - {file = "simplejson-3.17.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:9a2b7543559f8a1c9ed72724b549d8cc3515da7daf3e79813a15bdc4a769de25"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:311f5dc2af07361725033b13cc3d0351de3da8bede3397d45650784c3f21fbcf"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2862beabfb9097a745a961426fe7daf66e1714151da8bb9a0c430dde3d59c7c0"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:afebfc3dd3520d37056f641969ce320b071bc7a0800639c71877b90d053e087f"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d4813b30cb62d3b63ccc60dd12f2121780c7a3068db692daeb90f989877aaf04"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fabde09af43e0cbdee407555383063f8b45bfb52c361bc5da83fcffdb4fd278"}, - {file = "simplejson-3.17.2-cp37-cp37m-win32.whl", hash = "sha256:ceaa28a5bce8a46a130cd223e895080e258a88d51bf6e8de2fc54a6ef7e38c34"}, - {file = "simplejson-3.17.2-cp37-cp37m-win_amd64.whl", hash = "sha256:9551f23e09300a9a528f7af20e35c9f79686d46d646152a0c8fc41d2d074d9b0"}, - {file = "simplejson-3.17.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c94dc64b1a389a416fc4218cd4799aa3756f25940cae33530a4f7f2f54f166da"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b59aa298137ca74a744c1e6e22cfc0bf9dca3a2f41f51bc92eb05695155d905a"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ad8f41c2357b73bc9e8606d2fa226233bf4d55d85a8982ecdfd55823a6959995"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:845a14f6deb124a3bcb98a62def067a67462a000e0508f256f9c18eff5847efc"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d0b64409df09edb4c365d95004775c988259efe9be39697d7315c42b7a5e7e94"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:55d65f9cc1b733d85ef95ab11f559cce55c7649a2160da2ac7a078534da676c8"}, - {file = "simplejson-3.17.2.tar.gz", hash = "sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -sniffio = [ - {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, - {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, -] -sqlalchemy = [ - {file = "SQLAlchemy-1.4.37-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:d9050b0c4a7f5538650c74aaba5c80cd64450e41c206f43ea6d194ae6d060ff9"}, - {file = "SQLAlchemy-1.4.37-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b4c92823889cf9846b972ee6db30c0e3a92c0ddfc76c6060a6cda467aa5fb694"}, - {file = "SQLAlchemy-1.4.37-cp27-cp27m-win32.whl", hash = "sha256:b55932fd0e81b43f4aff397c8ad0b3c038f540af37930423ab8f47a20b117e4c"}, - {file = "SQLAlchemy-1.4.37-cp27-cp27m-win_amd64.whl", hash = "sha256:4a17c1a1152ca4c29d992714aa9df3054da3af1598e02134f2e7314a32ef69d8"}, - {file = "SQLAlchemy-1.4.37-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ffe487570f47536b96eff5ef2b84034a8ba4e19aab5ab7647e677d94a119ea55"}, - {file = "SQLAlchemy-1.4.37-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:78363f400fbda80f866e8e91d37d36fe6313ff847ded08674e272873c1377ea5"}, - {file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee34c85cbda7779d66abac392c306ec78c13f5c73a1f01b8b767916d4895d23"}, - {file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b38e088659b30c2ca0af63e5d139fad1779a7925d75075a08717a21c406c0f6"}, - {file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6629c79967a6c92e33fad811599adf9bc5cee6e504a1027bbf9cc1b6fb2d276d"}, - {file = "SQLAlchemy-1.4.37-cp310-cp310-win32.whl", hash = "sha256:2aac2a685feb9882d09f457f4e5586c885d578af4e97a2b759e91e8c457cbce5"}, - {file = "SQLAlchemy-1.4.37-cp310-cp310-win_amd64.whl", hash = "sha256:7a44683cf97744a405103ef8fdd31199e9d7fc41b4a67e9044523b29541662b0"}, - {file = "SQLAlchemy-1.4.37-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:cffc67cdd07f0e109a1fc83e333972ae423ea5ad414585b63275b66b870ea62b"}, - {file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17417327b87a0f703c9a20180f75e953315207d048159aff51822052f3e33e69"}, - {file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aaa0e90e527066409c2ea5676282cf4afb4a40bb9dce0f56c8ec2768bff22a6e"}, - {file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1d9fb3931e27d59166bb5c4dcc911400fee51082cfba66ceb19ac954ade068"}, - {file = "SQLAlchemy-1.4.37-cp36-cp36m-win32.whl", hash = "sha256:0e7fd52e48e933771f177c2a1a484b06ea03774fc7741651ebdf19985a34037c"}, - {file = "SQLAlchemy-1.4.37-cp36-cp36m-win_amd64.whl", hash = "sha256:eec39a17bab3f69c44c9df4e0ed87c7306f2d2bf1eca3070af644927ec4199fa"}, - {file = "SQLAlchemy-1.4.37-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:caca6acf3f90893d7712ae2c6616ecfeac3581b4cc677c928a330ce6fbad4319"}, - {file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50c8eaf44c3fed5ba6758d375de25f163e46137c39fda3a72b9ee1d1bb327dfc"}, - {file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:139c50b9384e6d32a74fc4dcd0e9717f343ed38f95dbacf832c782c68e3862f3"}, - {file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4c3b009c9220ae6e33f17b45f43fb46b9a1d281d76118405af13e26376f2e11"}, - {file = "SQLAlchemy-1.4.37-cp37-cp37m-win32.whl", hash = "sha256:9785d6f962d2c925aeb06a7539ac9d16608877da6aeaaf341984b3693ae80a02"}, - {file = "SQLAlchemy-1.4.37-cp37-cp37m-win_amd64.whl", hash = "sha256:3197441772dc3b1c6419f13304402f2418a18d7fe78000aa5a026e7100836739"}, - {file = "SQLAlchemy-1.4.37-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3862a069a24f354145e01a76c7c720c263d62405fe5bed038c46a7ce900f5dd6"}, - {file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e8706919829d455a9fa687c6bbd1b048e36fec3919a59f2d366247c2bfdbd9c"}, - {file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:06ec11a5e6a4b6428167d3ce33b5bd455c020c867dabe3e6951fa98836e0741d"}, - {file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d58f2d9d1a4b1459e8956a0153a4119da80f54ee5a9ea623cd568e99459a3ef1"}, - {file = "SQLAlchemy-1.4.37-cp38-cp38-win32.whl", hash = "sha256:d6927c9e3965b194acf75c8e0fb270b4d54512db171f65faae15ef418721996e"}, - {file = "SQLAlchemy-1.4.37-cp38-cp38-win_amd64.whl", hash = "sha256:a91d0668cada27352432f15b92ac3d43e34d8f30973fa8b86f5e9fddee928f3b"}, - {file = "SQLAlchemy-1.4.37-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:f9940528bf9c4df9e3c3872d23078b6b2da6431c19565637c09f1b88a427a684"}, - {file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29a742c29fea12259f1d2a9ee2eb7fe4694a85d904a4ac66d15e01177b17ad7f"}, - {file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7e579d6e281cc937bdb59917017ab98e618502067e04efb1d24ac168925e1d2a"}, - {file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a940c551cfbd2e1e646ceea2777944425f5c3edff914bc808fe734d9e66f8d71"}, - {file = "SQLAlchemy-1.4.37-cp39-cp39-win32.whl", hash = "sha256:5e4e517ce72fad35cce364a01aff165f524449e9c959f1837dc71088afa2824c"}, - {file = "SQLAlchemy-1.4.37-cp39-cp39-win_amd64.whl", hash = "sha256:c37885f83b59e248bebe2b35beabfbea398cb40960cdc6d3a76eac863d4e1938"}, - {file = "SQLAlchemy-1.4.37.tar.gz", hash = "sha256:3688f92c62db6c5df268e2264891078f17ecb91e3141b400f2e28d0f75796dea"}, -] -starlette = [ - {file = "starlette-0.14.2-py3-none-any.whl", hash = "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed"}, - {file = "starlette-0.14.2.tar.gz", hash = "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"}, -] -strawberry-graphql = [ - {file = "strawberry-graphql-0.90.3.tar.gz", hash = "sha256:1a6eb2e02fed40a5e5933184bce3fcf314bc108e65b414c0925814117f1df912"}, - {file = "strawberry_graphql-0.90.3-py3-none-any.whl", hash = "sha256:080f3abc3f98c58b9cfc29d43714acdc6b2e35da5a0902153471224b400205dc"}, -] -stripe = [ - {file = "stripe-2.76.0-py2.py3-none-any.whl", hash = "sha256:756bf6c1206f438d1fa23bb90cdf1233c9383478f854f2720a8a3e1eaf1f715b"}, - {file = "stripe-2.76.0.tar.gz", hash = "sha256:fd3fc6935c3b6189967191607b6f38ebe490005a590b4d0d43fbe3aba45deca8"}, -] -taskipy = [ - {file = "taskipy-1.10.1-py3-none-any.whl", hash = "sha256:9b38333654da487b6d16de6fa330b7629d1935d1e74819ba4c5f17a1c372d37b"}, - {file = "taskipy-1.10.1.tar.gz", hash = "sha256:6fa0b11c43d103e376063e90be31d87b435aad50fb7dc1c9a2de9b60a85015ed"}, -] -text-unidecode = [ - {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, - {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, -] -time-machine = [ - {file = "time-machine-2.7.1.tar.gz", hash = "sha256:be6c1f0421a77a046db8fae00886fb364f683a86612b71dd5c74b22891590042"}, - {file = "time_machine-2.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ae93d2f761435d192bc80c148438a0c4261979db0610cef08dfe2c8d21ca1c67"}, - {file = "time_machine-2.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:342b431154fbfb1889f8d7aa3d857373a837106bba395a5cc99123f11a7cea03"}, - {file = "time_machine-2.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4011ea76f6ad2f932f00cf9e77a25b575a024d6bc15bcf891a3f9916ceeb6e"}, - {file = "time_machine-2.7.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43ae8192d370a90d2246fca565a55633f592b314264c65c5c9151c361b715fb9"}, - {file = "time_machine-2.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cea12d0592ebbe738db952ce6fd272ed90e7bbb095e802f4f2145f8f0e322fa3"}, - {file = "time_machine-2.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:732d5fd2d442fa87538b5a6ca623cb205b9b048d2c9aaf79e5cfc7ec7f637848"}, - {file = "time_machine-2.7.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c34e1f49cad2fd41d42c4aabd3d69a32c79d9a8e0779064554843823cd1fb1e4"}, - {file = "time_machine-2.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6af3e81cf663b6d5660953ae59da2bb2ae802452ecbc9907272979ed06253659"}, - {file = "time_machine-2.7.1-cp310-cp310-win32.whl", hash = "sha256:10c2937d3556f4358205dac5c7cd2d33832b8b911f3deff050f59e1fe2be3231"}, - {file = "time_machine-2.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:200974e9bb8a1cb227ce579caafeaeebb0f9de81758c444cbccc0ea464313caf"}, - {file = "time_machine-2.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d5e2376b7922c9d96921709c7e730498b9c69da889f359a465d0c43117b62da3"}, - {file = "time_machine-2.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9117abe223cdc7b4a4432e0a0cfebb1b351a091ee996c653e90f27a734fce"}, - {file = "time_machine-2.7.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:626ef686723147468e84da3edcd67ff757a463250fd35f8f6a8e5b899c43b43d"}, - {file = "time_machine-2.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9331946ed13acd50bc484f408e26b8eefa67e3dbca41927d2052f2148d3661d"}, - {file = "time_machine-2.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3d0612e0323047f29c23732963d9926f1a95e2ce334d86fecd37c803ac240fc6"}, - {file = "time_machine-2.7.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b474499ad0083252240bc5be13f8116cc2ca8a89d1ca4967ed74a7b5f0883f95"}, - {file = "time_machine-2.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7df0857709432585b62d2667c0e6e64029b652e2df776b9fb85223c60dce52c7"}, - {file = "time_machine-2.7.1-cp37-cp37m-win32.whl", hash = "sha256:77c8dfe8dc7f45bbfe73494c72f3728d99abec5a020460ad7ffee5247365eba4"}, - {file = "time_machine-2.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c1fd1c231377ce076f99c8c16999a95510690f8dbd35db0e5fbbc74a17f84b39"}, - {file = "time_machine-2.7.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:462924fb87826882fc7830098e621116599f9259d181a7bbf5a4e49f74ec325b"}, - {file = "time_machine-2.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:46bf3b4a52d43289b23f0015a9d8592ddf621a5058e566c275cb060347d430c1"}, - {file = "time_machine-2.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30afd5b978d8121334c80fa23119d7bd7c9f954169854edf5103e5c8b38358bb"}, - {file = "time_machine-2.7.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:633fb8c47f3cd64690591ca6981e4fdbcaa54c18d8a57a3cdc24638ca98f8216"}, - {file = "time_machine-2.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b6093c3b70d1d1a66b65f18a6e53b233c8dd5d8ffe7ac59e9d048fb1d5e15c"}, - {file = "time_machine-2.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e62ed7d78694b7e0a2ab30b3dd52ebf26b03e17d6eda0f231fd77e24307a55a9"}, - {file = "time_machine-2.7.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0eaf024d16482ec211a579fd389cbbd4fedd8a1f0a0c41642508815f880ca3a9"}, - {file = "time_machine-2.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2688091ce0c16151faa80625efb34e3096731fbdee6d5284c48c984bce95c311"}, - {file = "time_machine-2.7.1-cp38-cp38-win32.whl", hash = "sha256:2e54bf0521b6e397fcaa03060feb187bbe5aa63ac51dbb97d5bc59fb0c4725f8"}, - {file = "time_machine-2.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:cee72d9e14d36e4b8da6af1d2d784f14da53f76aeb5066540a38318aa907b551"}, - {file = "time_machine-2.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:06322d41d45d86e2dc2520794c95129ff25b8620b33851ed40700c859ebf8c30"}, - {file = "time_machine-2.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:193b14daa3b3cf67e6b55d6e2d63c2eb7c1d3f49017704d4b43963b198656888"}, - {file = "time_machine-2.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1367a89fb857f68cfa723e236cd47febaf201a3a625ad8423110fe0509d5fca8"}, - {file = "time_machine-2.7.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce350f7e8bd51a0bb064180486300283bec5cd1a21a318a8ffe5f7df11735f36"}, - {file = "time_machine-2.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68ff623d835760314e279aedc0d19a1dc4dec117c6bca388e1ff077c781256bd"}, - {file = "time_machine-2.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:05fecd818d41727d31109a0d039ce07c8311602b45ffc07bffd8ae8b6f266ee5"}, - {file = "time_machine-2.7.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1fe4e604c5effc290c1bbecd3ea98687690d0a88fd98ba93e0246bf19ae2a520"}, - {file = "time_machine-2.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff07a5635d42957f2bd7eb5ca6579f64de368c842e754a4d3414520693b75db9"}, - {file = "time_machine-2.7.1-cp39-cp39-win32.whl", hash = "sha256:8c6314e7e0ffd7af82c8026786d5551aff973e0c86ec1368b0590be9a7620cad"}, - {file = "time_machine-2.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:d50a2620d726788cbde97c58e0f6f61d10337d16d088a1fad789f50a1b5ff4d1"}, -] -tomli = [ - {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, - {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, -] -typing-extensions = [ - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, -] -urllib3 = [ - {file = "urllib3-1.26.10-py2.py3-none-any.whl", hash = "sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec"}, - {file = "urllib3-1.26.10.tar.gz", hash = "sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"}, -] -uvicorn = [ - {file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"}, - {file = "uvicorn-0.13.4.tar.gz", hash = "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202"}, -] -ward = [ - {file = "ward-0.65.0b0-py3-none-any.whl", hash = "sha256:497e3602768e21c3db85bcf0fe241ee03494f0f46a75b7b2e0e074bfaddc4f5d"}, - {file = "ward-0.65.0b0.tar.gz", hash = "sha256:4f086c746b0d3d4464cf8ddea54f11fa08e02a784d0fda5bdc9b7f9c9fa63db0"}, -] -websockets = [ - {file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"}, - {file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"}, - {file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"}, - {file = "websockets-8.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb"}, - {file = "websockets-8.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5"}, - {file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"}, - {file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"}, - {file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"}, - {file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"}, - {file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"}, - {file = "websockets-8.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8"}, - {file = "websockets-8.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422"}, - {file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"}, - {file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"}, - {file = "websockets-8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092"}, - {file = "websockets-8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485"}, - {file = "websockets-8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1"}, - {file = "websockets-8.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55"}, - {file = "websockets-8.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824"}, - {file = "websockets-8.1-cp38-cp38-win32.whl", hash = "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36"}, - {file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"}, - {file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"}, -] -wmctrl = [ - {file = "wmctrl-0.4.tar.gz", hash = "sha256:66cbff72b0ca06a22ec3883ac3a4d7c41078bdae4fb7310f52951769b10e14e0"}, -] diff --git a/association-backend/pyproject.toml b/association-backend/pyproject.toml deleted file mode 100644 index 3fb4c2787d..0000000000 --- a/association-backend/pyproject.toml +++ /dev/null @@ -1,59 +0,0 @@ -[tool.poetry] -name = "association" -version = "0.1.0" -description = "Association Backend Service" -authors = ["Python Italia"] - -[tool.poetry.dependencies] -python = "^3.9" -starlette = "^0.14.1" -uvicorn = "^0.13.3" -click = "7.1.2" -invoke = "1.5.0" -strawberry-graphql = "^0.90" -alembic = "^1.5.2" -asyncpg = "^0.21.0" -PyJWT = "2.0.1" -pydantic = "^1.7.3" -httpx = "0.20.0" -stripe = "^2.55.2" -email-validator = "^1.1.2" -websockets = "^8.1" -time-machine = "^2.1.0" -python-dateutil = "^2.8.1" -pythonit-toolkit = "0.1.81" -ormar = "0.11.1" -mangum = "^0.11.0" -sentry-sdk = "^1.1.0" -awslambdaric = {version = "^2.0.4", optional = true} -psycopg2 = "^2.9.5" - - -[tool.poetry.dev-dependencies] -factory-boy = "^3.2.0" -isort = "^5.7.0" -flake8 = "^3.8.4" -ward = "^0.65.0b0" -requests = "^2.25.1" -taskipy = "^1.6.0" -asgi-lifespan = "^1.0.1" -coverage = "^5.4" -time-machine = "^2.0.1" -pdbpp = "^0.10.2" -Faker = "^8.1.0" -respx = "0.18.2" - -[tool.poetry.extras] -lambda = ["awslambdaric"] - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" - - -[tool.taskipy.tasks] -server = "uvicorn main:wrapped_app --port 8060 --reload" -test = "RUNNING_TESTS=1 python scripts/prepare_db.py && RUNNING_TESTS=1 coverage run -m ward" -"test:coverage" = "task test || coverage xml -i && coverage xml -i" -migrate = "alembic upgrade head" -makemigrations = "alembic revision --autogenerate -m" diff --git a/association-backend/scripts/__init__.py b/association-backend/scripts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/scripts/db_utils.py b/association-backend/scripts/db_utils.py deleted file mode 100644 index 881d15eb26..0000000000 --- a/association-backend/scripts/db_utils.py +++ /dev/null @@ -1,77 +0,0 @@ -import sqlalchemy -from sqlalchemy.engine.interfaces import Dialect -from sqlalchemy.orm.exc import UnmappedInstanceError -from sqlalchemy.orm.session import object_session - - -def create_database(url, db_name: str) -> None: - with sqlalchemy.create_engine( - url, isolation_level="AUTOCOMMIT" - ).connect() as connection: - query = "CREATE DATABASE {0} TEMPLATE {1}".format( - quote(connection, db_name), "template1" - ) - connection.execute(query) - - -def drop_database(url, db_name: str) -> None: - with sqlalchemy.create_engine( - url, isolation_level="AUTOCOMMIT" - ).connect() as connection: - query = "DROP DATABASE {0}".format(quote(connection, db_name)) - connection.execute(query) - - -def database_exists(url, db_name: str) -> bool: - with sqlalchemy.create_engine( - url, isolation_level="AUTOCOMMIT" - ).connect() as connection: - query = "SELECT 1 FROM pg_database WHERE datname='{0}'".format(db_name) - result = connection.execute(query) - return bool(result.scalar()) - - -def quote(mixed, ident): - """ - Conditionally quote an identifier. - :: - from sqlalchemy_utils import quote - engine = create_engine('sqlite:///:memory:') - quote(engine, 'order') - # '"order"' - quote(engine, 'some_other_identifier') - # 'some_other_identifier' - :param mixed: SQLAlchemy Session / Connection / Engine / Dialect object. - :param ident: identifier to conditionally quote - """ - if isinstance(mixed, Dialect): - dialect = mixed - else: - dialect = get_bind(mixed).dialect - return dialect.preparer(dialect).quote(ident) - - -def get_bind(obj): - """ - Return the bind for given SQLAlchemy Engine / Connection / declarative - model object. - :param obj: SQLAlchemy Engine / Connection / declarative model object - :: - from sqlalchemy_utils import get_bind - get_bind(session) # Connection object - get_bind(user) - """ - if hasattr(obj, "bind"): - conn = obj.bind - else: - try: - conn = object_session(obj).bind - except UnmappedInstanceError: - conn = obj - - if not hasattr(conn, "execute"): - raise TypeError( - "This method accepts only Session, Engine, Connection and " - "declarative model objects." - ) - return conn diff --git a/association-backend/scripts/prepare_db.py b/association-backend/scripts/prepare_db.py deleted file mode 100644 index 50cfd4aabc..0000000000 --- a/association-backend/scripts/prepare_db.py +++ /dev/null @@ -1,31 +0,0 @@ -# isort: off -import asyncio -import os -import sys -from sqlalchemy.engine import create_engine - -from sqlalchemy.engine.url import make_url # noqa - -sys.path.append(os.path.join(os.path.dirname(__file__), "..")) # noqa - -from db_utils import create_database, database_exists - -from src.database.db import metadata -from src.association.settings import DATABASE_URL - - -async def run(): - db_name = make_url(DATABASE_URL).database - sync_database_url = DATABASE_URL.replace("TEST_", "") - engine = create_engine(sync_database_url) - - if not database_exists(sync_database_url, db_name): - create_database(sync_database_url, db_name) - metadata.create_all(engine) - else: - metadata.drop_all(engine) - metadata.create_all(engine) - - -if __name__ == "__main__": - asyncio.run(run()) diff --git a/association-backend/src/__init__.py b/association-backend/src/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/api/__init__.py b/association-backend/src/api/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/api/context.py b/association-backend/src/api/context.py deleted file mode 100644 index 552d957ef6..0000000000 --- a/association-backend/src/api/context.py +++ /dev/null @@ -1,16 +0,0 @@ -import typing -from dataclasses import dataclass - -from starlette.requests import Request -from starlette.websockets import WebSocket - -from src.association_membership.domain.repository import AssociationMembershipRepository - - -@dataclass -class Context: - request: typing.Union[Request, WebSocket] - - @property - def association_repository(self) -> AssociationMembershipRepository: - return AssociationMembershipRepository() diff --git a/association-backend/src/api/mutation.py b/association-backend/src/api/mutation.py deleted file mode 100644 index e973ef7686..0000000000 --- a/association-backend/src/api/mutation.py +++ /dev/null @@ -1,8 +0,0 @@ -from strawberry.tools import create_type - -from .mutations.manage_user_subscription import manage_user_subscription -from .mutations.subscribe_user_to_association import subscribe_user_to_association - -Mutation = create_type( - "Mutation", [subscribe_user_to_association, manage_user_subscription] -) diff --git a/association-backend/src/api/mutations/__init__.py b/association-backend/src/api/mutations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/api/mutations/manage_user_subscription.py b/association-backend/src/api/mutations/manage_user_subscription.py deleted file mode 100644 index 3ee95471d8..0000000000 --- a/association-backend/src/api/mutations/manage_user_subscription.py +++ /dev/null @@ -1,46 +0,0 @@ -from typing import Any - -import strawberry -from pythonit_toolkit.api.permissions import IsAuthenticated -from strawberry.types import Info - -from src.api.context import Context -from src.association_membership.domain import exceptions -from src.association_membership.domain.services.manage_user_association_subscription import ( - manage_user_association_subscription as service_manage_user_association_subscription, -) - - -@strawberry.type -class NoSubscription: - message: str = "No subscription to manage" - - -@strawberry.type -class NotSubscribedViaStripe: - message: str = "Not subscribed via Stripe" - - -@strawberry.type -class CustomerPortalResponse: - billing_portal_url: str - - -CustomerPortalResult = strawberry.union( - "CustomerPortalResult", - (CustomerPortalResponse, NoSubscription, NotSubscribedViaStripe), -) - - -@strawberry.mutation(permission_classes=[IsAuthenticated]) -async def manage_user_subscription(info: Info[Context, Any]) -> CustomerPortalResult: - try: - billing_portal_url = await service_manage_user_association_subscription( - info.context.request.user, - association_repository=info.context.association_repository, - ) - return CustomerPortalResponse(billing_portal_url=billing_portal_url) - except (exceptions.CustomerNotAvailable, exceptions.NoSubscriptionAvailable): - return NoSubscription() - except (exceptions.NotSubscribedViaStripe): - return NotSubscribedViaStripe() diff --git a/association-backend/src/api/mutations/subscribe_user_to_association.py b/association-backend/src/api/mutations/subscribe_user_to_association.py deleted file mode 100644 index 708d9b04a7..0000000000 --- a/association-backend/src/api/mutations/subscribe_user_to_association.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import annotations - -from typing import Any - -import strawberry -from pythonit_toolkit.api.permissions import IsAuthenticated -from strawberry.types import Info - -from src.api.context import Context -from src.association_membership.domain import exceptions -from src.association_membership.domain.services.subscribe_user_to_association import ( - subscribe_user_to_association as service_subscribe_user_to_association, -) - - -@strawberry.type -class AlreadySubscribed: - message: str = "You are already subscribed" - - -@strawberry.type -class CheckoutSession: - stripe_session_id: str - - @classmethod - def from_domain(cls, stripe_session_id: str) -> CheckoutSession: - return cls( - stripe_session_id=stripe_session_id, - ) - - -SubscribeUserResult = strawberry.union( - "SubscribeUserResult", - ( - CheckoutSession, - AlreadySubscribed, - ), -) - - -@strawberry.mutation(permission_classes=[IsAuthenticated]) -async def subscribe_user_to_association( - info: Info[Context, Any] -) -> SubscribeUserResult: - try: - checkout_session_id = await service_subscribe_user_to_association( - info.context.request.user, - association_repository=info.context.association_repository, - ) - return CheckoutSession.from_domain(checkout_session_id) - except exceptions.AlreadySubscribed: - return AlreadySubscribed() diff --git a/association-backend/src/api/query.py b/association-backend/src/api/query.py deleted file mode 100644 index 4b2fe072dc..0000000000 --- a/association-backend/src/api/query.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Any - -import strawberry -from strawberry.types import Info - -from src.api.context import Context - - -@strawberry.federation.type(extend=True) -class Query: - @strawberry.field - async def association_service(self, info: Info[Context, Any]) -> bool: - return True diff --git a/association-backend/src/api/schema.py b/association-backend/src/api/schema.py deleted file mode 100644 index 24b1d80d34..0000000000 --- a/association-backend/src/api/schema.py +++ /dev/null @@ -1,6 +0,0 @@ -import strawberry -from pythonit_toolkit.api.extensions import SentryExtension - -from src.api.query import Query - -schema = strawberry.federation.Schema(query=Query, extensions=[SentryExtension]) diff --git a/association-backend/src/api/tests/__init__.py b/association-backend/src/api/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/api/tests/mutations/__init__.py b/association-backend/src/api/tests/mutations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/api/tests/mutations/test_manage_user_association_subscription.py b/association-backend/src/api/tests/mutations/test_manage_user_association_subscription.py deleted file mode 100644 index fcb70f3410..0000000000 --- a/association-backend/src/api/tests/mutations/test_manage_user_association_subscription.py +++ /dev/null @@ -1,128 +0,0 @@ -from unittest.mock import patch - -from pythonit_toolkit.api.graphql_test_client import SimulatedUser -from ward import test - -from src.association.tests.api import graphql_client -from src.association.tests.session import db -from src.association_membership.domain.entities import SubscriptionStatus -from src.association_membership.tests.factories import ( - StripeCustomerFactory, - SubscriptionFactory, -) - - -@test("Manage user subscription") -async def _(graphql_client=graphql_client, db=db): - await SubscriptionFactory(user_id=1, status=SubscriptionStatus.ACTIVE) - await StripeCustomerFactory(user_id=1) - - graphql_client.force_login( - SimulatedUser(id=1, email="test@user.it", is_staff=False), - ) - - query = """mutation { - manageUserSubscription { - __typename - ... on CustomerPortalResponse { - billingPortalUrl - } - } - }""" - - with patch( - "src.association_membership.domain.repository.stripe.billing_portal.Session.create", - ) as mock_create: - mock_create.return_value.url = ( - "https://stripe.com/stripe_test_customer_portal/cus_test_12345" - ) - response = await graphql_client.query(query, variables={}) - - assert ( - response.data["manageUserSubscription"]["__typename"] - == "CustomerPortalResponse" - ) - assert ( - response.data["manageUserSubscription"]["billingPortalUrl"] - == "https://stripe.com/stripe_test_customer_portal/cus_test_12345" - ) - - -@test("Cannot manage user subscription unlogged") -async def _(graphql_client=graphql_client, db=db): - query = """mutation { - manageUserSubscription { - __typename - ... on CustomerPortalResponse { - billingPortalUrl - } - } - }""" - - response = await graphql_client.query(query, variables={}) - - assert response.errors[0]["message"] == "Not authenticated" - - -@test("Cannot manage subscription if user doesnt have one") -async def _(graphql_client=graphql_client, db=db): - # user id 5 has a subscription - await SubscriptionFactory(user_id=5, status=SubscriptionStatus.ACTIVE) - await StripeCustomerFactory(user_id=5) - - # but user 1 doesn't have one - graphql_client.force_login( - SimulatedUser(id=1, email="test@user.it", is_staff=False), - ) - - query = """mutation { - manageUserSubscription { - __typename - } - }""" - - response = await graphql_client.query(query, variables={}) - - assert response.data["manageUserSubscription"]["__typename"] == "NoSubscription" - - -@test("Cannot manage subscription if all subscriptions are canceled") -async def _(graphql_client=graphql_client, db=db): - await SubscriptionFactory(user_id=1, status=SubscriptionStatus.CANCELED) - await StripeCustomerFactory(user_id=1) - - graphql_client.force_login( - SimulatedUser(id=1, email="test@user.it", is_staff=False), - ) - - query = """mutation { - manageUserSubscription { - __typename - } - }""" - - response = await graphql_client.query(query, variables={}) - - assert response.data["manageUserSubscription"]["__typename"] == "NoSubscription" - - -@test("Cannot manage subscription if not subscribed via stripe") -async def _(graphql_client=graphql_client, db=db): - await SubscriptionFactory(user_id=1, status=SubscriptionStatus.ACTIVE) - - graphql_client.force_login( - SimulatedUser(id=1, email="test@user.it", is_staff=False), - ) - - query = """mutation { - manageUserSubscription { - __typename - } - }""" - - response = await graphql_client.query(query, variables={}) - - assert ( - response.data["manageUserSubscription"]["__typename"] - == "NotSubscribedViaStripe" - ) diff --git a/association-backend/src/api/tests/mutations/test_subscribe_user_to_association.py b/association-backend/src/api/tests/mutations/test_subscribe_user_to_association.py deleted file mode 100644 index 1a633e6746..0000000000 --- a/association-backend/src/api/tests/mutations/test_subscribe_user_to_association.py +++ /dev/null @@ -1,93 +0,0 @@ -from unittest.mock import patch - -from pythonit_toolkit.api.graphql_test_client import SimulatedUser -from ward import test - -from src.association.tests.api import graphql_client -from src.association.tests.session import db -from src.association_membership.domain.entities import SubscriptionStatus -from src.association_membership.tests.factories import ( - StripeCustomerFactory, - SubscriptionFactory, -) - - -@test("Subscribe user to association") -async def _(graphql_client=graphql_client, db=db): - graphql_client.force_login( - SimulatedUser(id=1, email="test@user.it", is_staff=False), - ) - - query = """mutation { - subscribeUserToAssociation { - __typename - ... on CheckoutSession { - stripeSessionId - } - } - }""" - - with patch( - "src.association_membership.domain.repository.stripe.checkout.Session.create", - ) as mock_create_session, patch( - "src.association_membership.domain.repository.stripe.Customer.create" - ) as mock_customers_create: - mock_customers_create.return_value.id = "cus_created" - mock_create_session.return_value.id = "cs_xxx" - response = await graphql_client.query(query, variables={}) - - assert ( - response.data["subscribeUserToAssociation"]["__typename"] == "CheckoutSession" - ) - assert response.data["subscribeUserToAssociation"]["stripeSessionId"] == "cs_xxx" - - -@test("Subscribe user to association with existing canceled subscription") -async def _(graphql_client=graphql_client, db=db): - await SubscriptionFactory(user_id=1, status=SubscriptionStatus.CANCELED) - await StripeCustomerFactory(user_id=1, stripe_customer_id="cus_123") - - graphql_client.force_login( - SimulatedUser(id=1, email="test@user.it", is_staff=False), - ) - - query = """mutation { - subscribeUserToAssociation { - __typename - ... on CheckoutSession { - stripeSessionId - } - } - }""" - - with patch( - "src.association_membership.domain.repository.stripe.checkout.Session.create", - ) as mock_create: - mock_create.return_value.id = "cs_xxx" - response = await graphql_client.query(query, variables={}) - - assert ( - response.data["subscribeUserToAssociation"]["__typename"] == "CheckoutSession" - ) - assert response.data["subscribeUserToAssociation"]["stripeSessionId"] == "cs_xxx" - - -@test("cannot subscribe to association with existing active subscription") -async def _(graphql_client=graphql_client, db=db): - await SubscriptionFactory(user_id=1, status=SubscriptionStatus.ACTIVE) - - graphql_client.force_login( - SimulatedUser(id=1, email="test@user.it", is_staff=False), - ) - - query = """mutation { - subscribeUserToAssociation { - __typename - } - }""" - - response = await graphql_client.query(query, variables={}) - - assert ( - response.data["subscribeUserToAssociation"]["__typename"] == "AlreadySubscribed" - ) diff --git a/association-backend/src/api/views.py b/association-backend/src/api/views.py deleted file mode 100644 index ec29c1cd79..0000000000 --- a/association-backend/src/api/views.py +++ /dev/null @@ -1,22 +0,0 @@ -import logging -from typing import Optional, Union - -from starlette.requests import Request -from starlette.responses import Response -from starlette.websockets import WebSocket -from strawberry.asgi import GraphQL as BaseGraphQL - -from src.api.context import Context -from src.api.schema import schema - -logger = logging.getLogger(__name__) - - -class GraphQL(BaseGraphQL): - def __init__(self) -> None: - super().__init__(schema) - - async def get_context( - self, request: Union[Request, WebSocket], response: Optional[Response] = None - ) -> Context: - return Context(request=request) diff --git a/association-backend/src/association/__init__.py b/association-backend/src/association/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/association/auth.py b/association-backend/src/association/auth.py deleted file mode 100644 index 03512280c5..0000000000 --- a/association-backend/src/association/auth.py +++ /dev/null @@ -1,18 +0,0 @@ -from pythonit_toolkit.starlette_backend.pastaporto_backend import PastaportoAuthBackend -from starlette.authentication import AuthenticationBackend - -from src.association.settings import PASTAPORTO_SECRET -from src.webhooks.auth_backend import PretixAuthBackend - - -class RouterAuthBackend(AuthenticationBackend): - def __init__(self): - self.pretix_auth_backend = PretixAuthBackend() - self.pastaporto_backend = PastaportoAuthBackend(PASTAPORTO_SECRET) - - async def authenticate(self, request): - # Based on the path, use a different authentication method - if request.url.path == "/pretix-webhook": - return await self.pretix_auth_backend.authenticate(request) - - return await self.pastaporto_backend.authenticate(request) diff --git a/association-backend/src/association/settings.py b/association-backend/src/association/settings.py deleted file mode 100644 index 6877fe4ac2..0000000000 --- a/association-backend/src/association/settings.py +++ /dev/null @@ -1,61 +0,0 @@ -import stripe -from sqlalchemy.engine.url import URL, make_url -from starlette.config import Config -from starlette.datastructures import Secret - -config = Config(".env") - -# Unit-tests -RUNNING_TESTS = config("RUNNING_TESTS", cast=bool, default=False) - -ENV = config("ENV", cast=str, default="local") -DEBUG = config("DEBUG", cast=bool, default=False) -DATABASE_URL = config("DATABASE_URL") - -PRETIX_API_URL = config("PRETIX_API", default=None) -PRETIX_API_TOKEN = config("PRETIX_API_TOKEN", cast=Secret, default=None) - -# Services URLs -if not RUNNING_TESTS: - ASSOCIATION_FRONTEND_URL = config( - "ASSOCIATION_FRONTEND_URL", - ) - USERS_SERVICE_URL = config("USERS_SERVICE") - - PRETIX_WEBHOOK_SECRET = config("PRETIX_WEBHOOK_SECRET", cast=Secret) - -# Sentry -SENTRY_DSN = config("SENTRY_DSN", cast=Secret, default="") - -# Stripe -STRIPE_SECRET_API_KEY = config("STRIPE_SECRET_API_KEY", cast=Secret) -STRIPE_SUBSCRIPTION_PRICE_ID = config("STRIPE_SUBSCRIPTION_PRICE_ID", cast=str) -STRIPE_WEBHOOK_SECRET = config("STRIPE_WEBHOOK_SIGNATURE_SECRET", cast=Secret) - -# Set default stripe key -stripe.api_key = str(STRIPE_SECRET_API_KEY) - -# Secrets -PASTAPORTO_SECRET = config("PASTAPORTO_SECRET", cast=str) -SERVICE_TO_SERVICE_SECRET = config("SERVICE_TO_SERVICE_SECRET", cast=Secret) - -if RUNNING_TESTS: - original_url = make_url(DATABASE_URL) - test_db_url = URL( - drivername=original_url.drivername, - username=original_url.username, - password=original_url.password, - host=original_url.host, - port=original_url.port, - database=f"TEST_{original_url.database}", - query=original_url.query, - ) - DATABASE_URL = str(test_db_url) - - PRETIX_API_URL = "http://pretix-api/" - PRETIX_API_TOKEN = "pretix-token" - - ASSOCIATION_FRONTEND_URL = "http://association-frontend-url" - USERS_SERVICE_URL = "http://users-backend-url" - - PRETIX_WEBHOOK_SECRET = "pretix-webhook-secret" diff --git a/association-backend/src/association/tests/__init__.py b/association-backend/src/association/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/association/tests/api.py b/association-backend/src/association/tests/api.py deleted file mode 100644 index 997531a1e3..0000000000 --- a/association-backend/src/association/tests/api.py +++ /dev/null @@ -1,19 +0,0 @@ -from asgi_lifespan import LifespanManager -from httpx import AsyncClient -from pythonit_toolkit.api.graphql_test_client import GraphQLClient -from ward import fixture - -from main import app -from src.association.settings import PASTAPORTO_SECRET - - -@fixture -async def testclient(): - async with LifespanManager(app): - async with AsyncClient(app=app, base_url="http://testserver") as client: - yield client - - -@fixture() -async def graphql_client(testclient=testclient): - yield GraphQLClient(testclient, pastaporto_secret=PASTAPORTO_SECRET) diff --git a/association-backend/src/association/tests/factories.py b/association-backend/src/association/tests/factories.py deleted file mode 100644 index 09d59e1c31..0000000000 --- a/association-backend/src/association/tests/factories.py +++ /dev/null @@ -1,82 +0,0 @@ -import asyncio -import inspect - -import ormar -from factory.base import Factory, FactoryOptions, OptionDefault - - -class Options(FactoryOptions): - def _build_default_options(self): - return super()._build_default_options() + [ - OptionDefault("get_or_create", (), inherit=True), - ] - - -class ModelFactory(Factory): - class Meta: - abstract = True - - _options_class = Options - - @classmethod - def _generate(cls, strategy, params): - # Original params are used in _get_or_create if it cannot build an - # object initially due to an IntegrityError being raised - cls._original_params = params - return super()._generate(strategy, params) - - @classmethod - async def _get_or_create(cls, model_class, *args, **kwargs): - """Create an instance of the model through objects.get_or_create.""" - manager = model_class.objects - - assert "defaults" not in cls._meta.get_or_create, ( - "'defaults' is a reserved keyword for get_or_create " - "(in %s._meta.get_or_create=%r)" % (cls, cls._meta.get_or_create) - ) - - key_fields = {} - for field in cls._meta.get_or_create: - if field not in kwargs: - raise ValueError( - "get_or_create - " - "Unable to find initialization value for '%s' in factory %s" - % (field, cls.__name__) - ) - key_fields[field] = kwargs.pop(field) - key_fields["defaults"] = kwargs - - try: - instance, _created = await manager.get_or_create(*args, **key_fields) - except Exception as e: - # TODO fix - get_or_create_params = { - lookup: value - for lookup, value in cls._original_params.items() - if lookup in cls._meta.get_or_create - } - if get_or_create_params: - try: - instance = await manager.get(**get_or_create_params) - except ormar.NoMatch: - # Original params are not a valid lookup and triggered a create(), - # that resulted in an IntegrityError. Follow Django’s behavior. - raise e - else: - raise e - - return instance - - @classmethod - def _create(cls, model_class, *args, **kwargs): - async def maker_coroutine(): - for key, value in kwargs.items(): - if inspect.isawaitable(value): - kwargs[key] = await value - - if cls._meta.get_or_create: - return await cls._get_or_create(model_class, *args, **kwargs) - - return await model_class.objects.create(*args, **kwargs) - - return asyncio.create_task(maker_coroutine()) diff --git a/association-backend/src/association/tests/session.py b/association-backend/src/association/tests/session.py deleted file mode 100644 index fd7c9fea8a..0000000000 --- a/association-backend/src/association/tests/session.py +++ /dev/null @@ -1,16 +0,0 @@ -from sqlalchemy.engine import create_engine -from ward import fixture - -from src.association.settings import DATABASE_URL -from src.database.db import database, metadata - -engine = create_engine(DATABASE_URL) - - -@fixture -async def db(): - if not database.is_connected: - await database.connect() - - metadata.drop_all(engine) - metadata.create_all(engine) diff --git a/association-backend/src/association_membership/__init__.py b/association-backend/src/association_membership/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/association_membership/domain/__init__.py b/association-backend/src/association_membership/domain/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/association_membership/domain/entities.py b/association-backend/src/association_membership/domain/entities.py deleted file mode 100644 index e984d5a0b8..0000000000 --- a/association-backend/src/association_membership/domain/entities.py +++ /dev/null @@ -1,189 +0,0 @@ -from __future__ import annotations - -import logging -from datetime import datetime -from enum import Enum -from typing import Any, List, Union - -import ormar -import sqlalchemy -from pydantic import PrivateAttr - -from src.database.db import BaseMeta - -logger = logging.getLogger(__name__) - - -class DateTimeWithTimeZone(ormar.DateTime): - @classmethod - def get_column_type(cls, **kwargs: Any) -> Any: - return sqlalchemy.DateTime(timezone=True) - - -class SubscriptionStatus(str, Enum): - PENDING = "pending" - ACTIVE = "active" - CANCELED = "canceled" - - def __str__(self) -> str: - return str.__str__(self) - - -class PaymentStatus(str, Enum): - PAID = "paid" - CANCELED = "canceled" - - def __str__(self) -> str: - return str.__str__(self) - - -class Subscription(ormar.Model): - class Meta(BaseMeta): - tablename = "subscriptions" - - id: int = ormar.Integer(primary_key=True) - user_id: int = ormar.Integer(unique=True) - status: SubscriptionStatus = ormar.String( - max_length=20, - choices=list(SubscriptionStatus), - default=SubscriptionStatus.PENDING, - nullable=False, - ) - - _payments_to_add: List[ - Union[StripeSubscriptionPayment, PretixPayment] - ] = PrivateAttr(default_factory=list) - - def mark_as_canceled(self): - self._change_state(SubscriptionStatus.CANCELED) - - def mark_as_active(self): - self._change_state(SubscriptionStatus.ACTIVE) - - @property - def is_active(self) -> bool: - return self.status == SubscriptionStatus.ACTIVE - - def _change_state(self, to: SubscriptionStatus): - logger.info( - "Switching subscription_id=%s of user_id=%s from old_status=%s to status=%s", - self.id, - self.user_id, - self.status, - to, - ) - self.status = to - - def add_pretix_payment( - self, - *, - organizer: str, - event: str, - order_code: str, - total: int, - status: PaymentStatus, - payment_date: datetime, - period_start: datetime, - period_end: datetime, - ): - self._payments_to_add.append( - PretixPayment( - payment=Payment( - idempotency_key=PretixPayment.generate_idempotency_key( - organizer, event, order_code - ), - total=total, - status=status, - payment_date=payment_date, - period_start=period_start, - period_end=period_end, - subscription=self.id, - ), - order_code=order_code, - event_organizer=organizer, - event_id=event, - ) - ) - - def add_stripe_subscription_payment( - self, - total: int, - status: PaymentStatus, - payment_date: datetime, - period_start: datetime, - period_end: datetime, - stripe_subscription_id: str, - stripe_invoice_id: str, - invoice_pdf: str, - ): - self._payments_to_add.append( - StripeSubscriptionPayment( - payment=Payment( - idempotency_key=stripe_invoice_id, - total=total, - status=status, - payment_date=payment_date, - period_start=period_start, - period_end=period_end, - subscription=self.id, - ), - stripe_subscription_id=stripe_subscription_id, - stripe_invoice_id=stripe_invoice_id, - invoice_pdf=invoice_pdf, - ) - ) - - -class Payment(ormar.Model): - class Meta(BaseMeta): - tablename = "payments" - - id: int = ormar.Integer(primary_key=True) - subscription: Subscription = ormar.ForeignKey(Subscription, nullable=False) - # idempotency_key is used as a generic method to keep track of "already handled payments" - # if a payment comes with the same idempotency_key it gets rejected - idempotency_key: str = ormar.String(max_length=256, nullable=False, unique=True) - total: int = ormar.Integer() - payment_date: datetime = DateTimeWithTimeZone() - period_start: datetime = DateTimeWithTimeZone() - period_end: datetime = DateTimeWithTimeZone() - status: PaymentStatus = ormar.String( - max_length=20, - choices=list(PaymentStatus), - nullable=False, - ) - - -class PretixPayment(ormar.Model): - class Meta(BaseMeta): - tablename = "pretix_payments" - - id: int = ormar.Integer(primary_key=True) - payment: Payment = ormar.ForeignKey(Payment, nullable=False) - order_code: str = ormar.String(max_length=256, unique=True) - event_organizer: str = ormar.String(max_length=512) - event_id: str = ormar.String(max_length=512) - - @staticmethod - def generate_idempotency_key(organizer: str, event: str, order_code: str) -> str: - return f"{organizer}_{event}_{order_code}" - - -class StripeSubscriptionPayment(ormar.Model): - class Meta(BaseMeta): - tablename = "stripe_subscription_payments" - - id: int = ormar.Integer(primary_key=True) - payment: Payment = ormar.ForeignKey(Payment, nullable=False) - stripe_subscription_id: str = ormar.String(max_length=256) - stripe_invoice_id: str = ormar.String(max_length=256, unique=True) - invoice_pdf: str = ormar.Text() - - -class StripeCustomer(ormar.Model): - class Meta(BaseMeta): - tablename = "stripe_customers" - - id: int = ormar.Integer(primary_key=True) - user_id: int = ormar.Integer(unique=True) - stripe_customer_id: str = ormar.String(max_length=256, unique=True) diff --git a/association-backend/src/association_membership/domain/exceptions.py b/association-backend/src/association_membership/domain/exceptions.py deleted file mode 100644 index 52a6a08a3b..0000000000 --- a/association-backend/src/association_membership/domain/exceptions.py +++ /dev/null @@ -1,17 +0,0 @@ -class AlreadySubscribed(Exception): - """Raised when the system tries to associate a new subscription to an already subscribed user - It could be raised by subscribeUserToAssociation mutation""" - - -class CustomerNotAvailable(Exception): - """Raised when a user requests to access his customer portal, but the system has not yet associated the session user with the customer. - The webhook has probably not been called yet or the user has never completed the subscription procedure - It could be raised by manageUserAssociationSubscription mutation""" - - -class NoSubscriptionAvailable(Exception): - """Raised when the user doesn't have any subscription to manage""" - - -class NotSubscribedViaStripe(Exception): - """Raised when the user is not subscribed to Python Italia using Stripe""" diff --git a/association-backend/src/association_membership/domain/repository.py b/association-backend/src/association_membership/domain/repository.py deleted file mode 100644 index 0d3db4efc7..0000000000 --- a/association-backend/src/association_membership/domain/repository.py +++ /dev/null @@ -1,102 +0,0 @@ -import logging -from typing import Optional - -import stripe -from pythonit_toolkit.pastaporto.entities import PastaportoUserInfo - -from src.association.settings import ( - ASSOCIATION_FRONTEND_URL, - STRIPE_SUBSCRIPTION_PRICE_ID, -) -from src.association_membership.domain.entities import ( - Payment, - StripeCustomer, - Subscription, - SubscriptionStatus, -) - -logger = logging.getLogger(__name__) - - -class AssociationMembershipRepository: - async def get_user_subscription(self, user_id: int) -> Optional[Subscription]: - subscription = await Subscription.objects.get_or_none(user_id=user_id) - return subscription - - async def get_stripe_customer_from_user_id( - self, user_id: int - ) -> Optional[StripeCustomer]: - return await StripeCustomer.objects.get_or_none(user_id=user_id) - - async def create_subscription(self, user_id: int) -> Subscription: - subscription = await Subscription.objects.create( - user_id=user_id, status=SubscriptionStatus.PENDING - ) - return subscription - - async def create_stripe_customer(self, user: PastaportoUserInfo): - # TODO check existing customer and re-use it? - stripe_customer = stripe.Customer.create( - email=user.email, metadata={"user_id": user.id} - ) - - await StripeCustomer.objects.create( - user_id=user.id, stripe_customer_id=stripe_customer.id - ) - - async def get_subscription_from_stripe_customer( - self, stripe_customer_id: str - ) -> Optional[Subscription]: - stripe_customer = await StripeCustomer.objects.get_or_none( - stripe_customer_id=stripe_customer_id - ) - - if not stripe_customer: - return None - - subscription = await Subscription.objects.get_or_none( - user_id=stripe_customer.user_id - ) - return subscription - - async def is_payment_already_processed(self, idempotency_key: str) -> bool: - return await Payment.objects.filter(idempotency_key=idempotency_key).exists() - - async def create_checkout_session(self, subscription: Subscription) -> str: - customer = await StripeCustomer.objects.get( - user_id=subscription.user_id, - ) - - checkout_session = stripe.checkout.Session.create( - success_url=f"{ASSOCIATION_FRONTEND_URL}?membership-status=success#membership", - cancel_url=f"{ASSOCIATION_FRONTEND_URL}#membership", - payment_method_types=["card"], - mode="subscription", - customer=customer.stripe_customer_id, - # Note: if adding more line items, make sure webhook handlers - # can handle it when fetching the period start/end dates - line_items=[ - { - "price": STRIPE_SUBSCRIPTION_PRICE_ID, - "quantity": 1, - } - ], - ) - return checkout_session.id - - async def create_stripe_portal_session_url( - self, stripe_customer: StripeCustomer - ) -> str: - session = stripe.billing_portal.Session.create( - customer=stripe_customer.stripe_customer_id - ) - return session.url - - async def save_subscription(self, subscription: Subscription) -> Subscription: - await subscription.update() - - for add_payment in subscription._payments_to_add: - await add_payment.payment.save() - await add_payment.save() - - return subscription diff --git a/association-backend/src/association_membership/domain/services/__init__.py b/association-backend/src/association_membership/domain/services/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/association_membership/domain/services/manage_user_association_subscription.py b/association-backend/src/association_membership/domain/services/manage_user_association_subscription.py deleted file mode 100644 index 30096f9d75..0000000000 --- a/association-backend/src/association_membership/domain/services/manage_user_association_subscription.py +++ /dev/null @@ -1,39 +0,0 @@ -import logging - -from pythonit_toolkit.pastaporto.entities import PastaportoUserInfo - -from src.association_membership.domain.exceptions import ( - CustomerNotAvailable, - NoSubscriptionAvailable, - NotSubscribedViaStripe, -) -from src.association_membership.domain.repository import AssociationMembershipRepository - -logger = logging.getLogger(__name__) - - -async def manage_user_association_subscription( - user: PastaportoUserInfo, - *, - association_repository: AssociationMembershipRepository, -) -> str: - """This service creates a CustomerPortalSession and returns its url""" - subscription = await association_repository.get_user_subscription(user.id) - - if not subscription: - raise CustomerNotAvailable() - - if not subscription.is_active: - raise NoSubscriptionAvailable() - - stripe_customer = await association_repository.get_stripe_customer_from_user_id( - user.id - ) - - if not stripe_customer: - raise NotSubscribedViaStripe() - - billing_portal_url = await association_repository.create_stripe_portal_session_url( - stripe_customer - ) - return billing_portal_url diff --git a/association-backend/src/association_membership/domain/services/subscribe_user_to_association.py b/association-backend/src/association_membership/domain/services/subscribe_user_to_association.py deleted file mode 100644 index 9cedd32dae..0000000000 --- a/association-backend/src/association_membership/domain/services/subscribe_user_to_association.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -from pythonit_toolkit.pastaporto.entities import PastaportoUserInfo - -from src.association_membership.domain.exceptions import AlreadySubscribed -from src.association_membership.domain.repository import AssociationMembershipRepository - -logger = logging.getLogger(__name__) - - -async def subscribe_user_to_association( - user: PastaportoUserInfo, *, association_repository: AssociationMembershipRepository -) -> str: - subscription = await association_repository.get_user_subscription(user.id) - - if not subscription: - subscription = await association_repository.create_subscription(user.id) - await association_repository.create_stripe_customer(user) - - if subscription.is_active: - raise AlreadySubscribed() - - checkout_session_id = await association_repository.create_checkout_session( - subscription - ) - return checkout_session_id diff --git a/association-backend/src/association_membership/tests/__init__.py b/association-backend/src/association_membership/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/association_membership/tests/domain/services/test_manage_user_association_subscription.py b/association-backend/src/association_membership/tests/domain/services/test_manage_user_association_subscription.py deleted file mode 100644 index da3182c650..0000000000 --- a/association-backend/src/association_membership/tests/domain/services/test_manage_user_association_subscription.py +++ /dev/null @@ -1,139 +0,0 @@ -from datetime import datetime, timedelta, timezone - -from pythonit_toolkit.pastaporto.entities import PastaportoUserInfo -from ward import raises, test - -from src.association_membership.domain.entities import ( - Payment, - PaymentStatus, - PretixPayment, - StripeCustomer, - StripeSubscriptionPayment, - Subscription, - SubscriptionStatus, -) -from src.association_membership.domain.exceptions import ( - CustomerNotAvailable, - NoSubscriptionAvailable, - NotSubscribedViaStripe, -) -from src.association_membership.domain.services.manage_user_association_subscription import ( - manage_user_association_subscription, -) -from src.association_membership.tests.fake_repository import ( - FakeAssociationMembershipRepository, -) - - -@test("manage subscription user") -async def _(): - user = PastaportoUserInfo(id=1, email="test@email.it", is_staff=False) - subscription = Subscription(id=1, user_id=user.id, status=SubscriptionStatus.ACTIVE) - payment = Payment( - id=1, - idempotency_key="iv_abcabc", - subscription=subscription, - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.now(timezone.utc), - period_start=datetime.now(timezone.utc) + timedelta(days=0), - period_end=datetime.now(timezone.utc) + timedelta(days=30), - ) - StripeSubscriptionPayment( - id=1, - payment=payment, - stripe_subscription_id="sub_abcabc", - stripe_invoice_id="iv_abcabc", - invoice_pdf="https://stripe.com/pdf/xxx", - ) - stripe_customer = StripeCustomer( - id=1, user_id=user.id, stripe_customer_id="cus_123" - ) - - fake_repository = FakeAssociationMembershipRepository( - [subscription], [stripe_customer] - ) - - billing_portal_url = await manage_user_association_subscription( - user, association_repository=fake_repository - ) - - assert billing_portal_url == "https://fake.stripe/customerportal/cus_hello" - - -@test("with no active subscription fails") -async def _(): - user = PastaportoUserInfo(id=1, email="test@email.it", is_staff=False) - subscription = Subscription( - id=1, user_id=user.id, status=SubscriptionStatus.CANCELED - ) - payment = Payment( - id=1, - total=1000, - idempotency_key="iv_abcabc", - subscription=subscription, - status=PaymentStatus.PAID, - payment_date=datetime.now(timezone.utc), - period_start=datetime.now(timezone.utc) + timedelta(days=0), - period_end=datetime.now(timezone.utc) + timedelta(days=30), - ) - StripeSubscriptionPayment( - id=1, - payment=payment, - stripe_subscription_id="sub_abcabc", - stripe_invoice_id="iv_abcabc", - invoice_pdf="https://stripe.com/pdf/xxx", - ) - stripe_customer = StripeCustomer( - id=1, user_id=user.id, stripe_customer_id="cus_123" - ) - - fake_repository = FakeAssociationMembershipRepository( - [subscription], [stripe_customer] - ) - - with raises(NoSubscriptionAvailable): - await manage_user_association_subscription( - user, association_repository=fake_repository - ) - - -@test("with no subscription fails") -async def _(): - user = PastaportoUserInfo(id=1, email="test@email.it", is_staff=False) - fake_repository = FakeAssociationMembershipRepository([], []) - - with raises(CustomerNotAvailable): - await manage_user_association_subscription( - user, association_repository=fake_repository - ) - - -@test("when subscribed via other means than stripe fails to manage") -async def _(): - user = PastaportoUserInfo(id=1, email="test@email.it", is_staff=False) - subscription = Subscription(id=1, user_id=user.id, status=SubscriptionStatus.ACTIVE) - payment = Payment( - id=1, - total=1000, - idempotency_key="iv_abcabc", - subscription=subscription, - status=PaymentStatus.PAID, - payment_date=datetime.now(timezone.utc), - period_start=datetime.now(timezone.utc) + timedelta(days=0), - period_end=datetime.now(timezone.utc) + timedelta(days=30), - ) - PretixPayment( - id=1, - payment=payment, - order_code="ABC", - event_organizer="local-test", - event_id="pycon-demo", - ) - - fake_repository = FakeAssociationMembershipRepository([subscription], []) - - with raises(NotSubscribedViaStripe): - await manage_user_association_subscription( - user, association_repository=fake_repository - ) diff --git a/association-backend/src/association_membership/tests/domain/services/test_subscribe_user_to_association.py b/association-backend/src/association_membership/tests/domain/services/test_subscribe_user_to_association.py deleted file mode 100644 index ede17253b9..0000000000 --- a/association-backend/src/association_membership/tests/domain/services/test_subscribe_user_to_association.py +++ /dev/null @@ -1,88 +0,0 @@ -from datetime import datetime, timedelta, timezone - -from pythonit_toolkit.pastaporto.entities import PastaportoUserInfo -from ward import raises, test - -from src.association_membership.domain.entities import ( - Payment, - PaymentStatus, - StripeSubscriptionPayment, - Subscription, - SubscriptionStatus, -) -from src.association_membership.domain.exceptions import AlreadySubscribed -from src.association_membership.domain.services.subscribe_user_to_association import ( - subscribe_user_to_association, -) -from src.association_membership.tests.fake_repository import ( - FakeAssociationMembershipRepository, -) - - -@test("subscribe user without existing customer profile") -async def _(): - user = PastaportoUserInfo(id=1, email="test@email.it", is_staff=False) - - checkout_session_id = await subscribe_user_to_association( - user, - association_repository=FakeAssociationMembershipRepository(), - ) - assert checkout_session_id == "cs_xxx" - - -@test("subscribe user with active subscription fails") -async def _(): - user = PastaportoUserInfo(id=1, email="test@email.it", is_staff=False) - subscription = Subscription(id=1, user_id=user.id, status=SubscriptionStatus.ACTIVE) - payment = Payment( - id=1, - total=1000, - subscription=subscription, - idempotency_key="iv_abcabc", - status=PaymentStatus.PAID, - payment_date=datetime.now(timezone.utc), - period_start=datetime.now(timezone.utc) + timedelta(days=0), - period_end=datetime.now(timezone.utc) + timedelta(days=30), - ) - StripeSubscriptionPayment( - id=1, - payment=payment, - stripe_subscription_id="sub_abcabc", - stripe_invoice_id="iv_abcabc", - invoice_pdf="https://stripe.com/pdf/xxx", - ) - - with raises(AlreadySubscribed): - await subscribe_user_to_association( - user, - association_repository=FakeAssociationMembershipRepository([subscription]), - ) - - -@test("subscribe user with canceled subscription works") -async def _(): - user = PastaportoUserInfo(id=1, email="test@email.it", is_staff=False) - subscription = Subscription( - id=1, user_id=user.id, status=SubscriptionStatus.CANCELED - ) - payment = Payment( - total=1000, - subscription=subscription, - idempotency_key="iv_abcabc", - status=PaymentStatus.PAID, - payment_date=datetime.now(timezone.utc), - period_start=datetime.now(timezone.utc) + timedelta(days=0), - period_end=datetime.now(timezone.utc) + timedelta(days=30), - ) - StripeSubscriptionPayment( - payment=payment, - stripe_subscription_id="sub_abcabc", - stripe_invoice_id="iv_abcabc", - invoice_pdf="https://stripe.com/pdf/xxx", - ) - - checkout_session_id = await subscribe_user_to_association( - user, - association_repository=FakeAssociationMembershipRepository([subscription]), - ) - assert checkout_session_id == "cs_xxx" diff --git a/association-backend/src/association_membership/tests/domain/test_entities.py b/association-backend/src/association_membership/tests/domain/test_entities.py deleted file mode 100644 index 3964bceadf..0000000000 --- a/association-backend/src/association_membership/tests/domain/test_entities.py +++ /dev/null @@ -1,60 +0,0 @@ -from datetime import datetime, timezone - -from ward import test - -from src.association_membership.domain.entities import ( - PaymentStatus, - Subscription, - SubscriptionStatus, -) - - -@test("change subscription status to active") -async def _(): - subscription = Subscription( - id=1, - user_id=1, - status=SubscriptionStatus.PENDING, - ) - - subscription.mark_as_active() - - assert subscription.is_active - assert subscription.status == SubscriptionStatus.ACTIVE - - -@test("change subscription status to canceled") -async def _(): - subscription = Subscription( - id=1, - user_id=1, - status=SubscriptionStatus.PENDING, - ) - - subscription.mark_as_canceled() - - assert not subscription.is_active - assert subscription.status == SubscriptionStatus.CANCELED - - -@test("add stripe subscription payment to subscription") -async def _(): - subscription = Subscription( - id=1, - user_id=1, - status=SubscriptionStatus.PENDING, - ) - - subscription.add_stripe_subscription_payment( - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.fromtimestamp(1618062032, tz=timezone.utc), - period_start=datetime.fromtimestamp(1618062032, tz=timezone.utc), - period_end=datetime.fromtimestamp(1618062032, tz=timezone.utc), - stripe_subscription_id="cs_xxx", - stripe_invoice_id="iv_xx", - invoice_pdf="https://pdfpdf", - ) - - assert len(subscription._payments_to_add) == 1 - assert subscription._payments_to_add[0].stripe_subscription_id == "cs_xxx" diff --git a/association-backend/src/association_membership/tests/domain/test_repository.py b/association-backend/src/association_membership/tests/domain/test_repository.py deleted file mode 100644 index 2d01ed8cdc..0000000000 --- a/association-backend/src/association_membership/tests/domain/test_repository.py +++ /dev/null @@ -1,28 +0,0 @@ -from ward import test - -from src.association.tests.session import db -from src.association_membership.domain.repository import AssociationMembershipRepository -from src.association_membership.tests.factories import ( - StripeCustomerFactory, - SubscriptionFactory, -) - - -@test("get user subscription") -async def _(db=db): - subscription = await SubscriptionFactory(user_id=1) - - repository = AssociationMembershipRepository() - found_subscription = await repository.get_user_subscription(1) - - assert found_subscription.id == subscription.id - - -@test("get stripe customer from user id") -async def _(db=db): - customer = await StripeCustomerFactory(user_id=1) - - repository = AssociationMembershipRepository() - found_customer = await repository.get_stripe_customer_from_user_id(1) - - assert found_customer.id == customer.id diff --git a/association-backend/src/association_membership/tests/factories.py b/association-backend/src/association_membership/tests/factories.py deleted file mode 100644 index 173ad09279..0000000000 --- a/association-backend/src/association_membership/tests/factories.py +++ /dev/null @@ -1,19 +0,0 @@ -import factory -from faker import Faker - -from src.association.tests.factories import ModelFactory -from src.association_membership.domain.entities import StripeCustomer, Subscription - -fake = Faker() - - -class SubscriptionFactory(ModelFactory): - class Meta: - model = Subscription - - -class StripeCustomerFactory(ModelFactory): - class Meta: - model = StripeCustomer - - stripe_customer_id = factory.LazyAttribute(lambda _: f"cus_{fake.uuid4()}") diff --git a/association-backend/src/association_membership/tests/fake_repository.py b/association-backend/src/association_membership/tests/fake_repository.py deleted file mode 100644 index c2534bbb26..0000000000 --- a/association-backend/src/association_membership/tests/fake_repository.py +++ /dev/null @@ -1,46 +0,0 @@ -from src.association_membership.domain.entities import ( - StripeCustomer, - Subscription, - SubscriptionStatus, -) - - -class FakeAssociationMembershipRepository: - def __init__(self, subscriptions=None, stripe_customers=None): - self.SUBSCRIPTIONS_BY_USER_ID = ( - {subscription.user_id: subscription for subscription in subscriptions} - if subscriptions - else {} - ) - self.STRIPE_CUSTOMERS_BY_USER_ID = ( - { - stripe_customer.user_id: stripe_customer - for stripe_customer in stripe_customers - } - if stripe_customers - else {} - ) - - async def create_subscription(self, user_id): - sub = Subscription(user_id=user_id, status=SubscriptionStatus.PENDING) - self.SUBSCRIPTIONS_BY_USER_ID[user_id] = sub - return sub - - async def create_stripe_customer(self, user): - stripe_customer = StripeCustomer( - user_id=user.id, stripe_customer_id=f"cus_{user.id}" - ) - self.STRIPE_CUSTOMERS_BY_USER_ID[user.id] = stripe_customer - return stripe_customer - - async def get_stripe_customer_from_user_id(self, user_id): - return self.STRIPE_CUSTOMERS_BY_USER_ID.get(user_id, None) - - async def get_user_subscription(self, user_id): - return self.SUBSCRIPTIONS_BY_USER_ID.get(user_id, None) - - async def create_checkout_session(self, subscription: Subscription): - return "cs_xxx" - - async def create_stripe_portal_session_url(self, stripe_customer): - return "https://fake.stripe/customerportal/cus_hello" diff --git a/association-backend/src/database/__init__.py b/association-backend/src/database/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/database/db.py b/association-backend/src/database/db.py deleted file mode 100644 index 33f479090a..0000000000 --- a/association-backend/src/database/db.py +++ /dev/null @@ -1,13 +0,0 @@ -import databases -import ormar -import sqlalchemy - -from src.association.settings import DATABASE_URL - -database = databases.Database(DATABASE_URL) -metadata = sqlalchemy.MetaData() - - -class BaseMeta(ormar.ModelMeta): - metadata = metadata - database = database diff --git a/association-backend/src/internal_api/__init__.py b/association-backend/src/internal_api/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/internal_api/context.py b/association-backend/src/internal_api/context.py deleted file mode 100644 index 485adf4982..0000000000 --- a/association-backend/src/internal_api/context.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass - -from starlette.requests import Request - -from src.association_membership.domain.repository import AssociationMembershipRepository - - -@dataclass -class Info: - context: Context - - -@dataclass -class Context: - request: Request - - @property - def association_repository(self) -> AssociationMembershipRepository: - return AssociationMembershipRepository() diff --git a/association-backend/src/internal_api/permissions.py b/association-backend/src/internal_api/permissions.py deleted file mode 100644 index 577da57e00..0000000000 --- a/association-backend/src/internal_api/permissions.py +++ /dev/null @@ -1,9 +0,0 @@ -from pythonit_toolkit.api.permissions import IsService as BaseIsService - -from src.association.settings import SERVICE_TO_SERVICE_SECRET - - -def IsService(allowed_callers: list[str]): - return BaseIsService( - allowed_callers, str(SERVICE_TO_SERVICE_SECRET), "association-backend" - ) diff --git a/association-backend/src/internal_api/query.py b/association-backend/src/internal_api/query.py deleted file mode 100644 index dab4dca703..0000000000 --- a/association-backend/src/internal_api/query.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -import strawberry -from strawberry import ID - -from src.internal_api.context import Info -from src.internal_api.permissions import IsService - -logger = logging.getLogger(__name__) - - -@strawberry.type -class Query: - @strawberry.field(permission_classes=[IsService(["pycon-backend"])]) - async def user_id_is_member(self, info: Info, id: ID) -> bool: - if not id: - raise ValueError("Invalid ID") - - parsed_id = int(id) - logger.info( - "Internal api request to check if user_id=%s is a member", parsed_id - ) - subscription = await info.context.association_repository.get_user_subscription( - parsed_id - ) - return subscription.is_active if subscription else False diff --git a/association-backend/src/internal_api/schema.py b/association-backend/src/internal_api/schema.py deleted file mode 100644 index b66fd6425a..0000000000 --- a/association-backend/src/internal_api/schema.py +++ /dev/null @@ -1,5 +0,0 @@ -import strawberry - -from .query import Query - -schema = strawberry.Schema(query=Query) diff --git a/association-backend/src/internal_api/tests/__init__.py b/association-backend/src/internal_api/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/internal_api/tests/fixtures.py b/association-backend/src/internal_api/tests/fixtures.py deleted file mode 100644 index 1ee5f1cd77..0000000000 --- a/association-backend/src/internal_api/tests/fixtures.py +++ /dev/null @@ -1,29 +0,0 @@ -from asgi_lifespan import LifespanManager -from httpx import AsyncClient -from pythonit_toolkit.api.graphql_test_client import GraphQLClient -from ward import Scope, fixture - -from main import app -from src.association.settings import PASTAPORTO_SECRET, SERVICE_TO_SERVICE_SECRET - - -@fixture(scope=Scope.Global) -async def client(): - async with AsyncClient(app=app, base_url="http://testserver") as async_client: - yield async_client - - -@fixture -async def testclient(client=client): - async with LifespanManager(app): - yield client - - -@fixture -async def internalapi_graphql_client(testclient=testclient): - yield GraphQLClient( - testclient, - internal_api_endpoint=True, - pastaporto_secret=PASTAPORTO_SECRET, - service_to_service_secret=SERVICE_TO_SERVICE_SECRET, - ) diff --git a/association-backend/src/internal_api/tests/queries/__init__.py b/association-backend/src/internal_api/tests/queries/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/internal_api/tests/queries/test_user_id_is_member.py b/association-backend/src/internal_api/tests/queries/test_user_id_is_member.py deleted file mode 100644 index d7bb67e000..0000000000 --- a/association-backend/src/internal_api/tests/queries/test_user_id_is_member.py +++ /dev/null @@ -1,139 +0,0 @@ -from ward import each, test - -from src.association.tests.session import db -from src.association_membership.domain.entities import SubscriptionStatus -from src.association_membership.tests.factories import SubscriptionFactory -from src.internal_api.tests.fixtures import internalapi_graphql_client - - -@test("user does not exist") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, -): - internalapi_graphql_client.force_service_login( - issuer="pycon-backend", audience="association-backend" - ) - - query = """query($id: ID!) { - userIdIsMember(id: $id) - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": "1"}) - assert not response.errors - assert response.data["userIdIsMember"] is False - - -@test("user is a member") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, -): - await SubscriptionFactory(user_id=1, status=SubscriptionStatus.ACTIVE) - - internalapi_graphql_client.force_service_login( - issuer="pycon-backend", audience="association-backend" - ) - - query = """query($id: ID!) { - userIdIsMember(id: $id) - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": "1"}) - assert not response.errors - assert response.data["userIdIsMember"] is True - - -@test("user has a {status} membership so is not a member") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - status=each(SubscriptionStatus.CANCELED, SubscriptionStatus.PENDING), -): - await SubscriptionFactory(user_id=1, status=status) - - internalapi_graphql_client.force_service_login( - issuer="pycon-backend", audience="association-backend" - ) - - query = """query($id: ID!) { - userIdIsMember(id: $id) - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": "1"}) - assert not response.errors - assert response.data["userIdIsMember"] is False - - -@test("requires authentication") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, -): - await SubscriptionFactory(user_id=1, status=SubscriptionStatus.ACTIVE) - - query = """query($id: ID!) { - userIdIsMember(id: $id) - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": "1"}) - assert response.errors[0]["message"] == "Forbidden" - assert not response.data - - -@test("requires authentication of allowed service") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, -): - await SubscriptionFactory(user_id=1, status=SubscriptionStatus.ACTIVE) - internalapi_graphql_client.force_service_login( - issuer="random-service", audience="association-backend" - ) - - query = """query($id: ID!) { - userIdIsMember(id: $id) - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": "1"}) - assert response.errors[0]["message"] == "Forbidden" - assert not response.data - - -@test("invalid id raises an error") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, -): - internalapi_graphql_client.force_service_login( - issuer="pycon-backend", audience="association-backend" - ) - - query = """query($id: ID!) { - userIdIsMember(id: $id) - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": "abc-1"}) - assert ( - response.errors[0]["message"] - == "invalid literal for int() with base 10: 'abc-1'" - ) - assert not response.data - - -@test("empty id raises an error") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, -): - internalapi_graphql_client.force_service_login( - issuer="pycon-backend", audience="association-backend" - ) - - query = """query($id: ID!) { - userIdIsMember(id: $id) - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": ""}) - assert response.errors[0]["message"] == "Invalid ID" - assert not response.data diff --git a/association-backend/src/internal_api/views.py b/association-backend/src/internal_api/views.py deleted file mode 100644 index 0b4689af48..0000000000 --- a/association-backend/src/internal_api/views.py +++ /dev/null @@ -1,40 +0,0 @@ -from typing import Optional, Union - -from starlette.requests import Request -from starlette.responses import Response -from starlette.types import Receive, Scope, Send -from starlette.websockets import WebSocket -from strawberry.asgi import GraphQL as BaseGraphQL -from strawberry.utils.debug import pretty_print_graphql_operation - -from src.internal_api.context import Context -from src.internal_api.schema import schema - - -class GraphQL(BaseGraphQL): - def __init__(self) -> None: - super().__init__(schema) - - async def get_context( - self, request: Union[Request, WebSocket], response: Optional[Response] - ) -> Context: - return Context(request=request) - - async def handle_websocket(self, scope: Scope, receive: Receive, send: Send): - websocket = WebSocket(scope=scope, receive=receive, send=send) - await websocket.close() - - async def execute( - self, query, variables=None, context=None, operation_name=None, root_value=None - ): - if self.debug: - pretty_print_graphql_operation(operation_name, query, variables) - - return await self.schema.execute( - query, - root_value=root_value, - variable_values=variables, - operation_name=operation_name, - context_value=context, - validate_queries=False, - ) diff --git a/association-backend/src/webhooks/__init__.py b/association-backend/src/webhooks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/webhooks/auth_backend.py b/association-backend/src/webhooks/auth_backend.py deleted file mode 100644 index 6dbd6e7eae..0000000000 --- a/association-backend/src/webhooks/auth_backend.py +++ /dev/null @@ -1,38 +0,0 @@ -import base64 -import binascii - -from starlette.authentication import ( - AuthCredentials, - AuthenticationBackend, - AuthenticationError, - SimpleUser, -) - -from src.association.settings import PRETIX_WEBHOOK_SECRET - - -class PretixAuthBackend(AuthenticationBackend): - async def authenticate(self, request): - if request.url.path != "/pretix-webhook": - raise ValueError("PretixAuthBackend used outside pretix-webhook") - - if "Authorization" not in request.headers: - return - - auth = request.headers["Authorization"] - try: - scheme, credentials = auth.split() - if scheme.lower() != "basic": - return - decoded = base64.b64decode(credentials).decode("ascii") - except (ValueError, UnicodeDecodeError, binascii.Error) as exc: - raise AuthenticationError("Invalid basic auth credentials") from exc - - username, _, password = decoded.partition(":") - if username != "pretix": - raise AuthenticationError("Invalid auth") - - if password != str(PRETIX_WEBHOOK_SECRET): - raise AuthenticationError("Invalid auth") - - return AuthCredentials(["authenticated", "pretix"]), SimpleUser("pretix") diff --git a/association-backend/src/webhooks/exceptions.py b/association-backend/src/webhooks/exceptions.py deleted file mode 100644 index 241db21e25..0000000000 --- a/association-backend/src/webhooks/exceptions.py +++ /dev/null @@ -1,33 +0,0 @@ -class WebhookError(Exception): - """Base class for all known webhook errors""" - - -class NoCustomerFoundForEvent(WebhookError): - """Raised when receiving an event for a customer that doesn't exist on our system""" - - -class NoSubscriptionFoundForEvent(WebhookError): - """Raised when receiving a subscription event, but the subscription - doesn't exist on our system""" - - -class NoUserFoundWithEmail(WebhookError): - """Raised when no user matches the email""" - - -class NoConfirmedPaymentFound(WebhookError): - """Raised when the pretix order doesn't contain a confirmed payment""" - - -class NotEnoughPaid(WebhookError): - """Raised when the pretix order doesn't have enough payments - to cover the whole position amount""" - - -class UserIsAlreadyAMember(WebhookError): - """Raised when the user already has a membership""" - - -class UnsupportedMultipleMembershipInOneOrder(WebhookError): - """Raised when we receive an order that contains multiple - membership orders. This isn't supported""" diff --git a/association-backend/src/webhooks/handlers/__init__.py b/association-backend/src/webhooks/handlers/__init__.py deleted file mode 100644 index 4129cec21f..0000000000 --- a/association-backend/src/webhooks/handlers/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -import logging -from typing import Any, Callable, Literal, Optional - -from src.webhooks.exceptions import WebhookError - -from .crons.membership_check_status import membership_check_status -from .pretix.pretix_event_order_paid import pretix_event_order_paid -from .stripe.handle_invoice_paid import handle_invoice_paid - -logger = logging.getLogger(__file__) - - -HANDLERS = { - "stripe": { - "invoice.paid": handle_invoice_paid, - }, - "pretix": {"pretix.event.order.paid": pretix_event_order_paid}, - "crons": {"membership.check_status": membership_check_status}, -} - - -def get_handler( - service: Literal["stripe", "pretix"], event: str -) -> Optional[Callable[[Any], None]]: - return HANDLERS.get(service, {}).get(event, None) - - -async def run_handler( - service: Literal["stripe", "pretix", "crons"], event_name: str, payload: Any -): - handler = get_handler(service, event_name) - - if not handler: - logger.info("No handler found for event=%s and service=%s", event_name, service) - return None - - logger.info("Running handler for event_name=%s and service=%s", event_name, service) - try: - await handler(payload) - except WebhookError as e: - logger.exception( - "Known error while handling event_name=%s and service=%s", - event_name, - service, - exc_info=e, - ) diff --git a/association-backend/src/webhooks/handlers/crons/membership_check_status.py b/association-backend/src/webhooks/handlers/crons/membership_check_status.py deleted file mode 100644 index 7255f1cd4b..0000000000 --- a/association-backend/src/webhooks/handlers/crons/membership_check_status.py +++ /dev/null @@ -1,79 +0,0 @@ -import logging -from datetime import datetime, timezone -from typing import Any - -from src.association_membership.domain.entities import ( - PaymentStatus, - Subscription, - SubscriptionStatus, -) -from src.association_membership.domain.repository import AssociationMembershipRepository - -logger = logging.getLogger(__file__) - - -async def membership_check_status(payload: Any): - await update_expired_subscriptions() - await update_now_active_subscriptions() - - -async def update_now_active_subscriptions(): - repository = AssociationMembershipRepository() - - now = datetime.now(timezone.utc) - # Ideally in the future we should use the psql NOW() function - qs = Subscription.objects.filter( - status=SubscriptionStatus.CANCELED, - payments__status=PaymentStatus.PAID, - payments__period_start__lte=now, - payments__period_end__gte=now, - ) - subscriptions_to_enable = await qs.all() - subscriptions_to_enable_count = await qs.count() - - logger.info( - "Found subscriptions_to_enable_count=%s subscriptions to activate", - subscriptions_to_enable_count, - ) - - for subscription in subscriptions_to_enable: - subscription.mark_as_active() - await repository.save_subscription(subscription) - - -async def update_expired_subscriptions(): - repository = AssociationMembershipRepository() - - now = datetime.now(timezone.utc) - - # Ideally in the future we should use the psql NOW() function - subscriptions_with_payment_qs = Subscription.objects.filter( - payments__status=PaymentStatus.PAID, - payments__period_start__lte=now, - payments__period_end__gte=now, - ) - subscriptions_with_payment = set( - await subscriptions_with_payment_qs.values_list("id", flatten=True) - ) - - qs = Subscription.objects.filter(status=SubscriptionStatus.ACTIVE).exclude( - payments__status=PaymentStatus.PAID, - payments__period_start__lte=now, - payments__period_end__gte=now, - ) - - subscriptions_to_cancel = [ - subscription - for subscription in await qs.all() - if subscription.id not in subscriptions_with_payment - ] - subscriptions_to_cancel_count = len(subscriptions_to_cancel) - - logger.info( - "Found subscriptions_to_cancel_count=%s subscriptions to cancel", - subscriptions_to_cancel_count, - ) - - for subscription in subscriptions_to_cancel: - subscription.mark_as_canceled() - await repository.save_subscription(subscription) diff --git a/association-backend/src/webhooks/handlers/pretix/__init__.py b/association-backend/src/webhooks/handlers/pretix/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/webhooks/handlers/pretix/api.py b/association-backend/src/webhooks/handlers/pretix/api.py deleted file mode 100644 index f5eee52716..0000000000 --- a/association-backend/src/webhooks/handlers/pretix/api.py +++ /dev/null @@ -1,43 +0,0 @@ -from typing import Literal - -import httpx - -from src.association.settings import PRETIX_API_TOKEN, PRETIX_API_URL - -METHODS = Literal["get"] - - -class PretixAPI: - def __init__(self, organizer: str, event: str): - self.organizer = organizer - self.event = event - self.base_url = ( - f"{PRETIX_API_URL}organizers/{self.organizer}/events/{self.event}" - ) - - def _request( - self, endpoint: str, *, method: METHODS = "get", qs: dict[str, str] = None - ): - url = f"{self.base_url}/{endpoint}/" - headers = {"Authorization": f"Token {str(PRETIX_API_TOKEN)}"} - - if qs: - url = f"{url}?" + "&".join([f"{key}={value}" for key, value in qs.items()]) - - with httpx.Client(headers=headers) as client: - response = getattr(client, method)(url) - - response.raise_for_status() - return response - - def get_order_data(self, order_code: str) -> dict: - response = self._request(f"orders/{order_code}") - return response.json() - - def get_items(self, qs: dict[str, str]) -> dict: - response = self._request("items", qs=qs) - return response.json() - - def get_categories(self) -> dict: - response = self._request("categories") - return response.json() diff --git a/association-backend/src/webhooks/handlers/pretix/pretix_event_order_paid.py b/association-backend/src/webhooks/handlers/pretix/pretix_event_order_paid.py deleted file mode 100644 index c445730cd4..0000000000 --- a/association-backend/src/webhooks/handlers/pretix/pretix_event_order_paid.py +++ /dev/null @@ -1,239 +0,0 @@ -import logging -from datetime import datetime, timezone -from decimal import Decimal - -from dateutil import parser -from dateutil.relativedelta import relativedelta -from pythonit_toolkit.service_client import ServiceClient - -from src.association.settings import SERVICE_TO_SERVICE_SECRET, USERS_SERVICE_URL -from src.association_membership.domain.entities import PaymentStatus, PretixPayment -from src.association_membership.domain.repository import AssociationMembershipRepository -from src.webhooks.exceptions import ( - NoConfirmedPaymentFound, - NotEnoughPaid, - NoUserFoundWithEmail, - UnsupportedMultipleMembershipInOneOrder, - UserIsAlreadyAMember, -) - -from .api import PretixAPI - -logger = logging.getLogger(__file__) - -GET_USER_ID_BY_EMAIL = """query UserIdByEmail($email: String!) { - userByEmail(email: $email) { - id - } -}""" - - -async def pretix_event_order_paid(payload): - action = payload["action"] - organizer = payload["organizer"] - event = payload["event"] - order_code = payload["code"] - - pretix_api = PretixAPI(organizer, event) - - order_data = pretix_api.get_order_data(order_code) - - invalid_order_statuses = ["n", "e", "c"] - order_status = order_data["status"] - if order_status in invalid_order_statuses: - logger.error( - "Received a paid order event for order_code=%s but the order_status=%s " - "we only want paid orders", - order_code, - order_status, - ) - return - - categories = pretix_api.get_categories()["results"] - association_category = next( - ( - category - for category in categories - if category["internal_name"] == "Association" - ), - None, - ) - - if not association_category: - logger.info( - "Ignoring order_code=%s paid event for organizer=%s event=%s " - "because there isn't an association category", - order_code, - organizer, - event, - ) - return - - valid_items = pretix_api.get_items( - qs={"category": association_category["id"], "active": "true"} - ) - valid_items_ids = [item["id"] for item in valid_items["results"]] - - order_positions = order_data["positions"] - membership_positions = [] - for position in order_positions: - if position["item"] in valid_items_ids: - membership_positions.append(position) - - if not membership_positions: - logger.info( - "No membership positions for order_code=%s so nothing to do", order_code - ) - return - - if len(membership_positions) > 1: - logger.error( - "Multiple positions found in order_code=%s (organizer=%s event=%s) that subscribe " - "the user to the association. " - "This is not supported and needs to be refunded or manually handled", - order_code, - organizer, - event, - ) - raise UnsupportedMultipleMembershipInOneOrder( - f"Multiple positions found in order_code={order_code} that subscribe the user to the association. This is not supported." - ) - - user_email = order_data["email"] - client = ServiceClient( - url=f"{USERS_SERVICE_URL}/internal-api", - service_name="users-backend", - caller="association-backend", - jwt_secret=str(SERVICE_TO_SERVICE_SECRET), - ) - result = await client.execute(GET_USER_ID_BY_EMAIL, {"email": user_email}) - data = result.data - - if not data["userByEmail"]: - raise NoUserFoundWithEmail( - f"No user found with the email of order_code={order_code}" - ) - - user_id = int(data["userByEmail"]["id"]) - repository = AssociationMembershipRepository() - - idempotency_key = PretixPayment.generate_idempotency_key( - organizer, event, order_code - ) - if await repository.is_payment_already_processed(idempotency_key): - logger.info( - "Ignoring action=%s (organizer=%s event=%s) from Pretix because we already processed " - "the payment with key=%s from order_code=%s", - action, - organizer, - event, - idempotency_key, - order_code, - ) - return - - subscription = await repository.get_user_subscription(user_id) - - if not subscription: - subscription = await repository.create_subscription(user_id) - - if subscription.is_active: - logger.error( - "user_id=%s is already subscribed to the association " - "but paid a subscription via order_code=%s (organizer=%s event=%s)!", - user_id, - order_code, - organizer, - event, - ) - raise UserIsAlreadyAMember("User is already subscribed to the association") - - membership_position = membership_positions[0] - membership_price = Decimal(membership_position["price"]) - paid_payments = [ - payment for payment in order_data["payments"] if payment["state"] == "confirmed" - ] - - if not paid_payments: - logger.error( - "user_id=%s is already subscribed to the association " - "but paid a subscription via order_code=%s (organizer=%s event=%s)!", - user_id, - order_code, - organizer, - event, - ) - raise NoConfirmedPaymentFound( - f"No confirmed payment found for order_code={order_code}" - ) - - total_refunded = sum( - [ - Decimal(refund["amount"]) - for refund in order_data["refunds"] - if refund["state"] == "done" - ] - ) - total_paid = sum([Decimal(payment["amount"]) for payment in paid_payments]) - - if (total_paid - total_refunded) < membership_price: - logger.error( - "Received event paid for order_code=%s (organizer=%s event=%s) " - "but the total_paid=%s (total_refunded=%s) doesn't cover the membership_price=%s " - " so the membership cannot be created.", - order_code, - organizer, - event, - total_paid, - total_refunded, - membership_price, - ) - raise NotEnoughPaid( - f"Not enough payments found for order_code={order_code} to cover total={membership_price}" - ) - - payment_date = next( - ( - payment["payment_date"] - for payment in paid_payments - if payment["payment_date"] - ), - None, - ) - if not payment_date: - raise ValueError(f"No payment date for order_code={order_code}") - - payment_date = parser.parse(payment_date) - period_start = payment_date - period_end = payment_date + relativedelta(years=+1) - - # We assume our currency is EUR that has 2 decimal places and works in cents - total = int(membership_price * 10**2) - logger.info( - "Adding new pretix payment to user_id=%s " - "for period_start=%s to period_end=%s for order_code=%s organizer=%s event=%s", - user_id, - period_start, - period_end, - order_code, - organizer, - event, - ) - subscription.add_pretix_payment( - organizer=organizer, - event=event, - order_code=order_code, - total=total, - status=PaymentStatus.PAID, - payment_date=payment_date, - period_start=period_start, - period_end=period_end, - ) - - # If the payment we just received is for the current - # period, we mark the subscription as active - now = datetime.now(timezone.utc) - if period_start <= now <= period_end: - subscription.mark_as_active() - - await repository.save_subscription(subscription) diff --git a/association-backend/src/webhooks/handlers/stripe/__init__.py b/association-backend/src/webhooks/handlers/stripe/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/association-backend/src/webhooks/handlers/stripe/handle_invoice_paid.py b/association-backend/src/webhooks/handlers/stripe/handle_invoice_paid.py deleted file mode 100644 index 1b1a34bc01..0000000000 --- a/association-backend/src/webhooks/handlers/stripe/handle_invoice_paid.py +++ /dev/null @@ -1,74 +0,0 @@ -import logging -from datetime import datetime, timezone - -from src.association_membership.domain.entities import PaymentStatus -from src.association_membership.domain.repository import AssociationMembershipRepository -from src.webhooks.exceptions import NoCustomerFoundForEvent - -logger = logging.getLogger(__name__) - - -async def handle_invoice_paid(event): - invoice = event.data.object - stripe_customer_id = invoice.customer - stripe_subscription_id = invoice.subscription - - # Take the first item they purchased - # users can only buy the subscription - # so the lines will always be 1 - # If we change and allow people to buy - # multiple this we need to update this - assert ( - len(invoice.lines.data) == 1 - ), f"event_id={event.id} has more items than excepted" - assert ( - invoice.status == "paid" - ), f"event_id={event.id} has invoice_status={invoice.status}" - - membership_repository = AssociationMembershipRepository() - subscription = await membership_repository.get_subscription_from_stripe_customer( - stripe_customer_id - ) - - if not subscription: - logger.error( - "Unable to process stripe event_id=%s invoice paid because stripe_customer_id=%s" - " doesn't have an associated Customer locally or a subscription", - event.id, - stripe_customer_id, - ) - raise NoCustomerFoundForEvent() - - if await membership_repository.is_payment_already_processed(invoice.id): - logger.info( - "Ignoring event_id=%s from Stripe because we already processed " - "the payment of invoice_id=%s", - event.id, - invoice.id, - ) - return - - invoice_period = invoice.lines.data[0].period - - period_start = datetime.fromtimestamp(invoice_period.start, tz=timezone.utc) - period_end = datetime.fromtimestamp(invoice_period.end, tz=timezone.utc) - now = datetime.now(timezone.utc) - - subscription.add_stripe_subscription_payment( - total=invoice.total, - status=PaymentStatus.PAID, - payment_date=datetime.fromtimestamp( - invoice.status_transitions.paid_at, tz=timezone.utc - ), - period_start=period_start, - period_end=period_end, - stripe_subscription_id=stripe_subscription_id, - stripe_invoice_id=invoice.id, - invoice_pdf=invoice.invoice_pdf, - ) - - # If the payment we just received is for the current - # period, we mark the subscription as active - if period_start <= now <= period_end: - subscription.mark_as_active() - await membership_repository.save_subscription(subscription) diff --git a/association-backend/src/webhooks/tests/handlers/crons/test_membership_check_status.py b/association-backend/src/webhooks/tests/handlers/crons/test_membership_check_status.py deleted file mode 100644 index 4890f3f413..0000000000 --- a/association-backend/src/webhooks/tests/handlers/crons/test_membership_check_status.py +++ /dev/null @@ -1,476 +0,0 @@ -import datetime -from datetime import timezone -from unittest.mock import call, patch - -import time_machine -from ward import test - -from src.association.tests.session import db -from src.association_membership.domain.entities import ( - PaymentStatus, - Subscription, - SubscriptionStatus, -) -from src.association_membership.domain.repository import AssociationMembershipRepository -from src.association_membership.tests.factories import SubscriptionFactory -from src.webhooks.handlers.crons.membership_check_status import membership_check_status - - -@test("with no expired subscriptions") -async def _(db=db): - repository = AssociationMembershipRepository() - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.ACTIVE - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2021, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - - subscription_2 = await SubscriptionFactory( - user_id=2, status=SubscriptionStatus.ACTIVE - ) - subscription_2.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="AABBCC", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 5, 5, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2020, 5, 5, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2021, 5, 5, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - await repository.save_subscription(subscription_2) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.ACTIVE - - updated_subscription_2 = await Subscription.objects.get_or_none( - id=subscription_2.id - ) - assert updated_subscription_2.status == SubscriptionStatus.ACTIVE - - -@test("with one expired subscription") -async def _(db=db): - repository = AssociationMembershipRepository() - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.ACTIVE - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2021, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - - subscription_2 = await SubscriptionFactory( - user_id=2, status=SubscriptionStatus.ACTIVE - ) - subscription_2.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="AABBCC", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2019, 5, 5, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2019, 5, 5, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2020, 5, 5, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - await repository.save_subscription(subscription_2) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.ACTIVE - - # The only payment of subscription 2 is until 2020-05-05. - # Current time is 2020-10-10. - updated_subscription_2 = await Subscription.objects.get_or_none( - id=subscription_2.id - ) - assert updated_subscription_2.status == SubscriptionStatus.CANCELED - - -@test("subscription that is canceled but has a payment for this range is activated") -async def _(db=db): - repository = AssociationMembershipRepository() - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.CANCELED - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2021, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - - subscription_2 = await SubscriptionFactory( - user_id=2, status=SubscriptionStatus.CANCELED - ) - subscription_2.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="AABBCC", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2019, 5, 5, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2019, 5, 5, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2020, 5, 5, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - await repository.save_subscription(subscription_2) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.ACTIVE - - updated_subscription_2 = await Subscription.objects.get_or_none( - id=subscription_2.id - ) - assert updated_subscription_2.status == SubscriptionStatus.CANCELED - - -@test("subscription with multiple payments") -async def _(db=db): - repository = AssociationMembershipRepository() - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.ACTIVE - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2021, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="ABCABCABC", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2021, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2022, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.ACTIVE - - -@test("subscription with overlapping payments") -async def _(db=db): - repository = AssociationMembershipRepository() - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.ACTIVE - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2019, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2019, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="ABCABCABC", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2020, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2021, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.ACTIVE - - -@test("expired subscription with overlapping payments") -async def _(db=db): - repository = AssociationMembershipRepository() - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.ACTIVE - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2018, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2018, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2019, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="ABCABCABC", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2019, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2019, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2020, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.CANCELED - - -@test("subscription gets activated with overlapping payments") -async def _(db=db): - repository = AssociationMembershipRepository() - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.CANCELED - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="ABCABCABC", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2020, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2021, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2020, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2021, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.ACTIVE - - -@test("pending subscriptions are ignored") -async def _(db=db): - repository = AssociationMembershipRepository() - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.PENDING - ) - - await repository.save_subscription(subscription_1) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.PENDING - - -@test("canceled subscriptions with no payments are left untouched") -async def _(db=db): - repository = AssociationMembershipRepository() - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.CANCELED - ) - - await repository.save_subscription(subscription_1) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.CANCELED - - -@test("subscription with canceled payment gets canceled") -async def _(db=db): - repository = AssociationMembershipRepository() - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.ACTIVE - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.CANCELED, - payment_date=datetime.datetime(2019, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2019, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2022, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.CANCELED - - -@test("subscription with overlapping canceled and valid payment is marked active") -async def _(db=db): - repository = AssociationMembershipRepository() - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.CANCELED - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.CANCELED, - payment_date=datetime.datetime(2019, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2019, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2022, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="ABCABCABC", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2019, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2019, 10, 10, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2020, 11, 10, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.ACTIVE - - -@test("subscription with a finished and a new payment") -async def _(db=db): - repository = AssociationMembershipRepository() - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - subscription_1 = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.ACTIVE - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="ABCABCABC", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2020, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2020, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2021, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - ) - subscription_1.add_pretix_payment( - organizer="python-italia", - event="pycon-demo", - order_code="XXYYZZ", - total=1000, - status=PaymentStatus.PAID, - payment_date=datetime.datetime(2021, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_start=datetime.datetime(2021, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - period_end=datetime.datetime(2022, 1, 1, 1, 4, 43, tzinfo=timezone.utc), - ) - - await repository.save_subscription(subscription_1) - - with time_machine.travel("2021-01-01 10:00:00", tick=False): - with patch( - "src.webhooks.handlers.crons.membership_check_status.logger" - ) as logger_mock: - await membership_check_status({}) - - updated_subscription_1 = await Subscription.objects.get_or_none( - id=subscription_1.id - ) - assert updated_subscription_1.status == SubscriptionStatus.ACTIVE - - logger_mock.info.assert_has_calls( - [ - call("Found subscriptions_to_cancel_count=%s subscriptions to cancel", 0), - call("Found subscriptions_to_enable_count=%s subscriptions to activate", 0), - ], - any_order=True, - ) diff --git a/association-backend/src/webhooks/tests/handlers/pretix/payloads.py b/association-backend/src/webhooks/tests/handlers/pretix/payloads.py deleted file mode 100644 index 2e02233460..0000000000 --- a/association-backend/src/webhooks/tests/handlers/pretix/payloads.py +++ /dev/null @@ -1,746 +0,0 @@ -ORDER_PAID = { - "notification_id": 4122, - "organizer": "test-organizer", - "event": "local-conf-test", - "code": "9YKZK", - "action": "pretix.event.order.paid", -} - -ORDER_DATA_WITH_MEMBERSHIP = { - "code": "9YKZK", - "status": "p", - "testmode": False, - "secret": "bozndi9ck9yh6itl", - "email": "marcoaciernoemail+eee@gmail.com", - "phone": None, - "locale": "en", - "datetime": "2021-12-16T01:04:28.286984Z", - "expires": "2021-12-30T23:59:59Z", - "payment_date": "2021-12-16", - "payment_provider": "stripe", - "fees": [], - "total": "10.00", - "comment": "", - "custom_followup_at": None, - "invoice_address": { - "last_modified": "2021-12-16T01:04:28.289855Z", - "is_business": False, - "company": "", - "name": "gdfgf", - "name_parts": {"_scheme": "full", "full_name": "gdfgf"}, - "street": "gdfgdf", - "zipcode": "gdfgdf", - "city": "gdfdf", - "country": "AW", - "state": "", - "vat_id": "", - "vat_id_validated": False, - "internal_reference": "", - }, - "positions": [ - { - "id": 801, - "order": "9YKZK", - "positionid": 1, - "item": 100, - "variation": None, - "price": "10.00", - "attendee_name": None, - "attendee_name_parts": {}, - "company": None, - "street": None, - "zipcode": None, - "city": None, - "country": None, - "state": None, - "attendee_email": None, - "voucher": None, - "tax_rate": "0.00", - "tax_value": "0.00", - "secret": "123456", - "addon_to": None, - "subevent": None, - "checkins": [], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orderpositions/801/download/pdf/", - } - ], - "answers": [], - "tax_rule": 8, - "pseudonymization_id": "8YVKQWXWTX", - "seat": None, - "canceled": False, - } - ], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orders/9YKZK/download/pdf/", - } - ], - "checkin_attention": False, - "last_modified": "2021-12-16T01:04:43.149421Z", - "payments": [ - { - "local_id": 1, - "state": "confirmed", - "amount": "10.00", - "created": "2021-12-16T01:04:28.318676Z", - "payment_date": "2021-12-16T01:04:43.0Z", - "provider": "stripe", - "payment_url": None, - "details": { - "id": "pi_xxxxxxxxxx", - "payment_method": "pm_xxxxxxxxxxxxxxxxxx", - }, - } - ], - "refunds": [], - "require_approval": False, - "sales_channel": "web", - "url": "https://tickets.pycon.it/test-organizer/local-conf-test/order/9YKZK/bozndi9ck9yh6itl/", - "customer": None, -} - -ORDER_DATA_WITHOUT_MEMBERSHIP = { - "code": "9YKZK", - "status": "p", - "testmode": False, - "secret": "bozndi9ck9yh6itl", - "email": "marcoaciernoemail+eee@gmail.com", - "phone": None, - "locale": "en", - "datetime": "2021-12-16T01:04:28.286984Z", - "expires": "2021-12-30T23:59:59Z", - "payment_date": "2021-12-16", - "payment_provider": "stripe", - "fees": [], - "total": "10.00", - "comment": "", - "custom_followup_at": None, - "invoice_address": { - "last_modified": "2021-12-16T01:04:28.289855Z", - "is_business": False, - "company": "", - "name": "gdfgf", - "name_parts": {"_scheme": "full", "full_name": "gdfgf"}, - "street": "gdfgdf", - "zipcode": "gdfgdf", - "city": "gdfdf", - "country": "AW", - "state": "", - "vat_id": "", - "vat_id_validated": False, - "internal_reference": "", - }, - "positions": [ - { - "id": 801, - "order": "9YKZK", - "positionid": 1, - "item": 200, - "variation": None, - "price": "10.00", - "attendee_name": None, - "attendee_name_parts": {}, - "company": None, - "street": None, - "zipcode": None, - "city": None, - "country": None, - "state": None, - "attendee_email": None, - "voucher": None, - "tax_rate": "0.00", - "tax_value": "0.00", - "secret": "123456", - "addon_to": None, - "subevent": None, - "checkins": [], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orderpositions/801/download/pdf/", - } - ], - "answers": [], - "tax_rule": 8, - "pseudonymization_id": "8YVKQWXWTX", - "seat": None, - "canceled": False, - } - ], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orders/9YKZK/download/pdf/", - } - ], - "checkin_attention": False, - "last_modified": "2021-12-16T01:04:43.149421Z", - "payments": [ - { - "local_id": 1, - "state": "confirmed", - "amount": "10.00", - "created": "2021-12-16T01:04:28.318676Z", - "payment_date": "2021-12-16T01:04:43.0Z", - "provider": "stripe", - "payment_url": None, - "details": { - "id": "pi_xxxxxxxxxx", - "payment_method": "pm_xxxxxxxxxxxxxxxxxx", - }, - } - ], - "refunds": [], - "require_approval": False, - "sales_channel": "web", - "url": "https://tickets.pycon.it/test-organizer/local-conf-test/order/9YKZK/bozndi9ck9yh6itl/", - "customer": None, -} - -ITEMS_WITH_CATEGORY = { - "count": 1, - "next": None, - "previous": None, - "results": [ - { - "id": 100, - "category": 25, - "name": { - "en": "Python Italia Association Membership 2022", - "it": "Iscrizione all'associazione Python Italia 2022", - }, - "internal_name": None, - "active": True, - "sales_channels": ["web"], - "description": {}, - "default_price": "10.00", - "free_price": False, - "tax_rate": "0.00", - "tax_rule": 8, - "admission": False, - "position": 9, - "picture": None, - "available_from": None, - "available_until": None, - "require_voucher": False, - "hide_without_voucher": False, - "allow_cancel": True, - "require_bundling": False, - "min_per_order": None, - "max_per_order": None, - "checkin_attention": False, - "has_variations": False, - "variations": [], - "addons": [], - "bundles": [], - "original_price": None, - "require_approval": False, - "generate_tickets": None, - "show_quota_left": None, - "hidden_if_available": None, - "allow_waitinglist": True, - "issue_giftcard": False, - "meta_data": {}, - "require_membership": False, - "require_membership_types": [], - "require_membership_hidden": False, - "grant_membership_type": None, - "grant_membership_duration_like_event": True, - "grant_membership_duration_days": 0, - "grant_membership_duration_months": 0, - } - ], -} - -CATEGORIES = { - "count": 5, - "next": None, - "previous": None, - "results": [ - { - "id": 16, - "name": {"en": "Tickets"}, - "internal_name": None, - "description": {}, - "position": 0, - "is_addon": False, - }, - { - "id": 17, - "name": {"en": "Hotel"}, - "internal_name": None, - "description": {}, - "position": 0, - "is_addon": False, - }, - { - "id": 18, - "name": {"en": "Gadget"}, - "internal_name": None, - "description": {}, - "position": 0, - "is_addon": False, - }, - { - "id": 22, - "name": {"en": "Tickets Student", "it": "Biglietti Studenti"}, - "internal_name": None, - "description": { - "en": "Category Tickets Student", - "it": "Biglietti Categoria Studenti", - }, - "position": 0, - "is_addon": False, - }, - { - "id": 25, - "name": {"en": "Association", "it": "Associazione"}, - "internal_name": "Association", - "description": {"en": "Association", "it": "Association"}, - "position": 0, - "is_addon": False, - }, - ], -} - -ORDER_DATA_WITH_REFUNDS = { - "code": "9YKZK", - "status": "p", - "testmode": False, - "secret": "yp46s8dnh9work02", - "email": "marcoaciernoemail+eee@gmail.com", - "phone": None, - "locale": "en", - "datetime": "2021-12-16T01:44:50.846879Z", - "expires": "2022-01-06T23:59:59Z", - "payment_date": "2021-12-16", - "payment_provider": "manual", - "fees": [], - "total": "10.00", - "comment": "", - "custom_followup_at": None, - "invoice_address": { - "last_modified": "2021-12-16T01:44:50.853572Z", - "is_business": False, - "company": "", - "name": "sdfds", - "name_parts": {"_scheme": "full", "full_name": "sdfds"}, - "street": "sdfdsfsd", - "zipcode": "ddfds", - "city": "fsdfds", - "country": "AW", - "state": "", - "vat_id": "", - "vat_id_validated": False, - "internal_reference": "", - }, - "positions": [ - { - "id": 804, - "order": "9YKZK", - "positionid": 1, - "item": 100, - "variation": None, - "price": "10.00", - "attendee_name": None, - "attendee_name_parts": {}, - "company": None, - "street": None, - "zipcode": None, - "city": None, - "country": None, - "state": None, - "attendee_email": None, - "voucher": None, - "tax_rate": "0.00", - "tax_value": "0.00", - "secret": "52wh5vnx5gkrhtcbgu5u3zgx7eyvy52e", - "addon_to": None, - "subevent": None, - "checkins": [], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orderpositions/804/download/pdf/", - } - ], - "answers": [], - "tax_rule": 8, - "pseudonymization_id": "33NQRDXMJ3", - "seat": None, - "canceled": False, - } - ], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orders/9YKZK/download/pdf/", - } - ], - "checkin_attention": False, - "last_modified": "2021-12-16T00:08:47.970914Z", - "payments": [ - { - "local_id": 1, - "state": "confirmed", - "amount": "10.00", - "created": "2021-12-16T00:00:50.882421Z", - "payment_date": "2021-12-16T00:00:01.056360Z", - "provider": "stripe", - "payment_url": None, - "details": { - "id": "pi_3K791jD5MZ3GejSO1WPYVcsJ", - "payment_method": "pm_1K791hD5MZ3GejSO0vsNBBp2", - }, - }, - { - "local_id": 2, - "state": "confirmed", - "amount": "3.00", - "created": "2021-12-16T00:00:47.943860Z", - "payment_date": "2021-12-16T00:00:47.949903Z", - "provider": "manual", - "payment_url": None, - "details": {}, - }, - ], - "refunds": [ - { - "local_id": 1, - "state": "done", - "source": "admin", - "amount": "3.00", - "payment": 1, - "created": "2021-12-16T00:00:05.076318Z", - "execution_date": "2021-12-16T00:00:06.585765Z", - "comment": None, - "provider": "stripe", - } - ], - "require_approval": False, - "sales_channel": "web", - "url": "https://tickets.pycon.it/test-organizer/local-conf-test/order/9YKZK/yp46s8dnh9work02/", - "customer": None, -} - -ORDER_DATA_WITH_REFUND_EVERYTHING_BUT_MEMBERSHIP = { - "code": "9YKZK", - "status": "p", - "testmode": False, - "secret": "257fg221mfd9hdxd", - "email": "marcoaciernoemail+eee@gmail.com", - "phone": None, - "locale": "en", - "datetime": "2021-12-16T00:19:48.747751Z", - "expires": "2022-01-06T23:59:59Z", - "payment_date": "2021-12-16", - "payment_provider": "stripe", - "fees": [], - "total": "90.00", - "comment": "", - "custom_followup_at": None, - "invoice_address": { - "last_modified": "2021-12-16T00:19:48.753369Z", - "is_business": False, - "company": "", - "name": "vfgfs", - "name_parts": {"_scheme": "full", "full_name": "vfgfs"}, - "street": "gfdgdf", - "zipcode": "423", - "city": "vf", - "country": "AU", - "state": "", - "vat_id": "", - "vat_id_validated": False, - "internal_reference": "", - }, - "positions": [ - { - "id": 818, - "order": "9YKZK", - "positionid": 1, - "item": 100, - "variation": None, - "price": "10.00", - "attendee_name": None, - "attendee_name_parts": {}, - "company": None, - "street": None, - "zipcode": None, - "city": None, - "country": None, - "state": None, - "attendee_email": None, - "voucher": None, - "tax_rate": "0.00", - "tax_value": "0.00", - "secret": "ja3jm7qq9seedq2edhnwsjm6trv7a52d", - "addon_to": None, - "subevent": None, - "checkins": [], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orderpositions/818/download/pdf/", - } - ], - "answers": [], - "tax_rule": 8, - "pseudonymization_id": "LNSEYS3M8J", - "seat": None, - "canceled": False, - }, - { - "id": 819, - "order": "9YKZK", - "positionid": 2, - "item": 67, - "variation": None, - "price": "80.00", - "attendee_name": "vfvdf", - "attendee_name_parts": {"_legacy": "vfvdf"}, - "company": None, - "street": None, - "zipcode": None, - "city": None, - "country": None, - "state": None, - "attendee_email": "vdfvfdv@vsfs.it", - "voucher": None, - "tax_rate": "22.00", - "tax_value": "14.43", - "secret": "n2aqpyyfr7eszubwqwvdr87jhzgfr42x", - "addon_to": None, - "subevent": None, - "checkins": [], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orderpositions/819/download/pdf/", - } - ], - "answers": [ - { - "question": 31, - "answer": "No preferences", - "question_identifier": "URHUPTLM", - "options": [16], - "option_identifiers": ["TRBQ9KH7"], - } - ], - "tax_rule": 6, - "pseudonymization_id": "QHQHJMTECQ", - "seat": None, - "canceled": False, - }, - ], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orders/9YKZK/download/pdf/", - } - ], - "checkin_attention": False, - "last_modified": "2021-12-16T00:19:59.404591Z", - "payments": [ - { - "local_id": 1, - "state": "confirmed", - "amount": "90.00", - "created": "2021-12-16T00:19:48.813365Z", - "payment_date": "2021-12-16T00:19:59.385206Z", - "provider": "stripe", - "payment_url": None, - "details": { - "id": "pi_3K9s9BD5MZ3GejSO0S3T4z5X", - "payment_method": "pm_1K9s99D5MZ3GejSOpPJztfTj", - }, - } - ], - "refunds": [ - { - "local_id": 1, - "state": "done", - "source": "admin", - "amount": "80.00", - "payment": 1, - "created": "2021-12-16T00:20:39.415758Z", - "execution_date": "2021-12-16T00:20:40.938300Z", - "comment": None, - "provider": "stripe", - } - ], - "require_approval": False, - "sales_channel": "web", - "url": "https://tickets.pycon.it/test-organizer/local-conf-test/order/9YKZK/257fg221mfd9hdxd/", - "customer": None, -} - -ORDER_DATA_WITH_REFUND_EVERYTHING_AND_NOT_ENOUGH_TO_COVER_MEMBERSHIP = { - "code": "9YKZK", - "status": "p", - "testmode": False, - "secret": "257fg221mfd9hdxd", - "email": "marcoaciernoemail+eee@gmail.com", - "phone": None, - "locale": "en", - "datetime": "2021-12-16T00:19:48.747751Z", - "expires": "2022-01-06T23:59:59Z", - "payment_date": "2021-12-16", - "payment_provider": "stripe", - "fees": [], - "total": "90.00", - "comment": "", - "custom_followup_at": None, - "invoice_address": { - "last_modified": "2021-12-16T00:19:48.753369Z", - "is_business": False, - "company": "", - "name": "vfgfs", - "name_parts": {"_scheme": "full", "full_name": "vfgfs"}, - "street": "gfdgdf", - "zipcode": "423", - "city": "vf", - "country": "AU", - "state": "", - "vat_id": "", - "vat_id_validated": False, - "internal_reference": "", - }, - "positions": [ - { - "id": 818, - "order": "9YKZK", - "positionid": 1, - "item": 100, - "variation": None, - "price": "10.00", - "attendee_name": None, - "attendee_name_parts": {}, - "company": None, - "street": None, - "zipcode": None, - "city": None, - "country": None, - "state": None, - "attendee_email": None, - "voucher": None, - "tax_rate": "0.00", - "tax_value": "0.00", - "secret": "ja3jm7qq9seedq2edhnwsjm6trv7a52d", - "addon_to": None, - "subevent": None, - "checkins": [], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orderpositions/818/download/pdf/", - } - ], - "answers": [], - "tax_rule": 8, - "pseudonymization_id": "LNSEYS3M8J", - "seat": None, - "canceled": False, - }, - { - "id": 819, - "order": "9YKZK", - "positionid": 2, - "item": 67, - "variation": None, - "price": "80.00", - "attendee_name": "vfvdf", - "attendee_name_parts": {"_legacy": "vfvdf"}, - "company": None, - "street": None, - "zipcode": None, - "city": None, - "country": None, - "state": None, - "attendee_email": "vdfvfdv@vsfs.it", - "voucher": None, - "tax_rate": "22.00", - "tax_value": "14.43", - "secret": "n2aqpyyfr7eszubwqwvdr87jhzgfr42x", - "addon_to": None, - "subevent": None, - "checkins": [], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orderpositions/819/download/pdf/", - } - ], - "answers": [ - { - "question": 31, - "answer": "No preferences", - "question_identifier": "URHUPTLM", - "options": [16], - "option_identifiers": ["TRBQ9KH7"], - } - ], - "tax_rule": 6, - "pseudonymization_id": "QHQHJMTECQ", - "seat": None, - "canceled": False, - }, - ], - "downloads": [ - { - "output": "pdf", - "url": "http://tickets.pycon.it/api/v1/organizers/test-organizer/events/local-conf-test/orders/9YKZK/download/pdf/", - } - ], - "checkin_attention": False, - "last_modified": "2021-12-16T00:19:59.404591Z", - "payments": [ - { - "local_id": 1, - "state": "confirmed", - "amount": "90.00", - "created": "2021-12-16T00:19:48.813365Z", - "payment_date": "2021-12-16T00:19:59.385206Z", - "provider": "stripe", - "payment_url": None, - "details": { - "id": "pi_3K9s9BD5MZ3GejSO0S3T4z5X", - "payment_method": "pm_1K9s99D5MZ3GejSOpPJztfTj", - }, - } - ], - "refunds": [ - { - "local_id": 1, - "state": "done", - "source": "admin", - "amount": "80.00", - "payment": 1, - "created": "2021-12-16T00:20:39.415758Z", - "execution_date": "2021-12-16T00:20:40.938300Z", - "comment": None, - "provider": "stripe", - }, - { - "local_id": 2, - "state": "done", - "source": "admin", - "amount": "5.00", - "payment": 1, - "created": "2021-12-16T00:26:38.941174Z", - "execution_date": "2021-12-16T00:26:40.569829Z", - "comment": None, - "provider": "stripe", - }, - ], - "require_approval": False, - "sales_channel": "web", - "url": "https://tickets.pycon.it/test-organizer/local-conf-test/order/9YKZK/257fg221mfd9hdxd/", - "customer": None, -} diff --git a/association-backend/src/webhooks/tests/handlers/pretix/test_pretix_event_order_paid.py b/association-backend/src/webhooks/tests/handlers/pretix/test_pretix_event_order_paid.py deleted file mode 100644 index 9898a25446..0000000000 --- a/association-backend/src/webhooks/tests/handlers/pretix/test_pretix_event_order_paid.py +++ /dev/null @@ -1,336 +0,0 @@ -import datetime -from datetime import timezone - -import respx -import time_machine -from ward import raises, test - -from src.association.tests.session import db -from src.association_membership.domain.entities import ( - PaymentStatus, - Subscription, - SubscriptionStatus, -) -from src.association_membership.tests.factories import SubscriptionFactory -from src.webhooks.exceptions import ( - NotEnoughPaid, - NoUserFoundWithEmail, - UserIsAlreadyAMember, -) -from src.webhooks.handlers.pretix.pretix_event_order_paid import pretix_event_order_paid -from src.webhooks.tests.handlers.pretix.payloads import ( - CATEGORIES, - ITEMS_WITH_CATEGORY, - ORDER_DATA_WITH_MEMBERSHIP, - ORDER_DATA_WITH_REFUND_EVERYTHING_AND_NOT_ENOUGH_TO_COVER_MEMBERSHIP, - ORDER_DATA_WITH_REFUND_EVERYTHING_BUT_MEMBERSHIP, - ORDER_DATA_WITH_REFUNDS, - ORDER_DATA_WITHOUT_MEMBERSHIP, - ORDER_PAID, -) - - -@test("receive order paid with membership") -async def _(db=db): - with respx.mock as mock, time_machine.travel("2021-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond(json=ORDER_DATA_WITH_MEMBERSHIP) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": {"id": 1}}} - ) - await pretix_event_order_paid(ORDER_PAID) - - created_subscription = await Subscription.objects.select_related( - ["payments", "payments__pretixpayments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - - assert created_subscription.status == SubscriptionStatus.ACTIVE - assert created_subscription.user_id == 1 - assert len(created_subscription.payments) == 1 - - payment = created_subscription.payments[0] - assert payment.total == 1000 - assert payment.payment_date == datetime.datetime( - 2021, 12, 16, 1, 4, 43, tzinfo=timezone.utc - ) - assert payment.period_start == datetime.datetime( - 2021, 12, 16, 1, 4, 43, tzinfo=timezone.utc - ) - assert payment.period_end == datetime.datetime( - 2022, 12, 16, 1, 4, 43, tzinfo=timezone.utc - ) - assert payment.status == PaymentStatus.PAID - - pretix_payment = payment.pretixpayments[0] - assert pretix_payment.order_code == "9YKZK" - assert pretix_payment.event_organizer == "test-organizer" - assert pretix_payment.event_id == "local-conf-test" - - -@test("receive order paid twice doesn't process the payment twice") -async def _(db=db): - with respx.mock as mock, time_machine.travel("2021-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond(json=ORDER_DATA_WITH_MEMBERSHIP) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": {"id": 1}}} - ) - await pretix_event_order_paid(ORDER_PAID) - - created_subscription = await Subscription.objects.select_related( - [ - "payments", - "payments__pretixpayments", - "payments__stripesubscriptionpayments", - ] - ).get(user_id=1) - - assert created_subscription.status == SubscriptionStatus.ACTIVE - assert len(created_subscription.payments) == 1 - - await pretix_event_order_paid(ORDER_PAID) - - created_subscription = await Subscription.objects.select_related( - ["payments", "payments__pretixpayments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - - assert created_subscription.status == SubscriptionStatus.ACTIVE - assert len(created_subscription.payments) == 1 - - -@test("receive order paid without membership purchase") -async def _(db=db): - with respx.mock as mock, time_machine.travel("2021-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond(json=ORDER_DATA_WITHOUT_MEMBERSHIP) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": {"id": 1}}} - ) - await pretix_event_order_paid(ORDER_PAID) - - assert not await Subscription.objects.filter(user_id=1).exists() - - -@test("receive order paid fails if no user maps to the email") -async def _(db=db): - with respx.mock as mock, time_machine.travel("2021-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond(json=ORDER_DATA_WITH_MEMBERSHIP) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": None}} - ) - - with raises(NoUserFoundWithEmail) as exc: - await pretix_event_order_paid(ORDER_PAID) - - assert not await Subscription.objects.filter(user_id=1).exists() - assert str(exc.raised) == "No user found with the email of order_code=9YKZK" - - -@test("receive order paid with canceled subscription") -async def _(db=db): - existing_subscription = await SubscriptionFactory( - user_id=1, status=SubscriptionStatus.CANCELED - ) - - with respx.mock as mock, time_machine.travel("2021-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond(json=ORDER_DATA_WITH_MEMBERSHIP) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": {"id": 1}}} - ) - await pretix_event_order_paid(ORDER_PAID) - - created_subscription = await Subscription.objects.select_related( - ["payments", "payments__pretixpayments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - - assert created_subscription.id == existing_subscription.id - assert created_subscription.status == SubscriptionStatus.ACTIVE - assert created_subscription.user_id == 1 - assert len(created_subscription.payments) == 1 - - -@test("receive order paid of period outside current one") -async def _(db=db): - with respx.mock as mock, time_machine.travel("2023-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond(json=ORDER_DATA_WITH_MEMBERSHIP) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": {"id": 1}}} - ) - await pretix_event_order_paid(ORDER_PAID) - - created_subscription = await Subscription.objects.select_related( - ["payments", "payments__pretixpayments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - - # We still accept the payment but we don't change to ACTIVE - assert created_subscription.status == SubscriptionStatus.PENDING - assert created_subscription.user_id == 1 - assert len(created_subscription.payments) == 1 - - -@test("receive order paid of already subscribed fails with error message") -async def _(db=db): - await SubscriptionFactory(user_id=1, status=SubscriptionStatus.ACTIVE) - - with respx.mock as mock, time_machine.travel("2023-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond(json=ORDER_DATA_WITH_MEMBERSHIP) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": {"id": 1}}} - ) - - with raises(UserIsAlreadyAMember) as exc: - await pretix_event_order_paid(ORDER_PAID) - - created_subscription = await Subscription.objects.select_related( - ["payments", "payments__pretixpayments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - - # We still accept the payment but we don't change ACTIVE - assert created_subscription.status == SubscriptionStatus.ACTIVE - assert len(created_subscription.payments) == 0 - assert str(exc.raised) == "User is already subscribed to the association" - - -@test("can subscribe with mix of manual and refunds") -async def _(db=db): - with respx.mock as mock, time_machine.travel("2021-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond(json=ORDER_DATA_WITH_REFUNDS) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": {"id": 1}}} - ) - - await pretix_event_order_paid(ORDER_PAID) - - created_subscription = await Subscription.objects.select_related( - ["payments", "payments__pretixpayments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - - assert created_subscription.status == SubscriptionStatus.ACTIVE - assert created_subscription.user_id == 1 - assert len(created_subscription.payments) == 1 - - payment = created_subscription.payments[0] - assert payment.total == 1000 - - -@test( - "if some has been refunded but the user paid enough to cover the membership price it gets accepted" -) -async def _(db=db): - with respx.mock as mock, time_machine.travel("2021-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond(json=ORDER_DATA_WITH_REFUND_EVERYTHING_BUT_MEMBERSHIP) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": {"id": 1}}} - ) - - await pretix_event_order_paid(ORDER_PAID) - - created_subscription = await Subscription.objects.select_related( - ["payments", "payments__pretixpayments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - - assert created_subscription.status == SubscriptionStatus.ACTIVE - assert created_subscription.user_id == 1 - assert len(created_subscription.payments) == 1 - - payment = created_subscription.payments[0] - assert payment.total == 1000 - - -@test("if the order doesn't have enough to cover the price gets rejected") -async def _(db=db): - with respx.mock as mock, time_machine.travel("2021-12-16 01:04:50Z", tick=False): - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/orders/9YKZK/" - ).respond( - json=ORDER_DATA_WITH_REFUND_EVERYTHING_AND_NOT_ENOUGH_TO_COVER_MEMBERSHIP - ) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/items/?active=true&category=25" - ).respond(json=ITEMS_WITH_CATEGORY) - mock.get( - "http://pretix-api/organizers/test-organizer/events/local-conf-test/categories/" - ).respond(json=CATEGORIES) - mock.post("http://users-backend-url/internal-api").respond( - json={"data": {"userByEmail": {"id": 1}}} - ) - - with raises(NotEnoughPaid): - await pretix_event_order_paid(ORDER_PAID) - - created_subscription = await Subscription.objects.select_related( - ["payments", "payments__pretixpayments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - - assert created_subscription.status == SubscriptionStatus.PENDING - assert created_subscription.user_id == 1 - assert len(created_subscription.payments) == 0 diff --git a/association-backend/src/webhooks/tests/handlers/stripe/payloads.py b/association-backend/src/webhooks/tests/handlers/stripe/payloads.py deleted file mode 100644 index 8facd19178..0000000000 --- a/association-backend/src/webhooks/tests/handlers/stripe/payloads.py +++ /dev/null @@ -1,177 +0,0 @@ -import json - -from stripe import util - -RAW_INVOICE_PAID_PAYLOAD = json.loads( - """{ - "api_version": "2019-03-14", - "created": 1618062033, - "data": { - "object": { - "account_country": "GB", - "account_name": "Testen", - "account_tax_ids": null, - "amount_due": 1000, - "amount_paid": 1000, - "amount_remaining": 0, - "application_fee_amount": null, - "attempt_count": 1, - "attempted": true, - "auto_advance": false, - "billing": "charge_automatically", - "billing_reason": "subscription_create", - "charge": "ch_1Ieh36AQv52awOkHAOJGVXb2", - "collection_method": "charge_automatically", - "created": 1618062031, - "currency": "gbp", - "custom_fields": null, - "customer": "cus_customer_id", - "customer_address": null, - "customer_email": "marcoaciernoemail@gmail.com", - "customer_name": null, - "customer_phone": null, - "customer_shipping": null, - "customer_tax_exempt": "none", - "customer_tax_ids": [], - "default_payment_method": null, - "default_source": null, - "default_tax_rates": [], - "description": null, - "discount": null, - "discounts": [], - "due_date": null, - "ending_balance": 0, - "footer": null, - "hosted_invoice_url": "https://invoice.stripe.com/i/acct_1AEbpzAQv52awOkH/invst_JHFYBOjnsmDH6yxnhbD2jeX09nN1QUC", - "id": "in_1Ieh35AQv52awOkHrNk0JBjD", - "invoice_pdf": "https://pay.stripe.com/invoice/acct_1AEbpzAQv52awOkH/invst_JHFYBOjnsmDH6yxnhbD2jeX09nN1QUC/pdf", - "last_finalization_error": null, - "lines": { - "data": [ - { - "amount": 1000, - "currency": "gbp", - "description": "1 \u00d7 Iscrizione Associazione Python Italia (at \u00a310.00 / year)", - "discount_amounts": [], - "discountable": true, - "discounts": [], - "id": "sli_7a6f818189903e", - "livemode": false, - "metadata": {}, - "object": "line_item", - "period": { - "end": 1649598031, - "start": 1618062031 - }, - "plan": { - "active": true, - "aggregate_usage": null, - "amount": 1000, - "amount_decimal": "1000", - "billing_scheme": "per_unit", - "created": 1612806117, - "currency": "gbp", - "id": "price_1IIdkHAQv52awOkHISxsHtG5", - "interval": "year", - "interval_count": 1, - "livemode": false, - "metadata": {}, - "nickname": null, - "object": "plan", - "product": "prod_IuSf6H0sVxclQQ", - "tiers": null, - "tiers_mode": null, - "transform_usage": null, - "trial_period_days": null, - "usage_type": "licensed" - }, - "price": { - "active": true, - "billing_scheme": "per_unit", - "created": 1612806117, - "currency": "gbp", - "id": "price_1IIdkHAQv52awOkHISxsHtG5", - "livemode": false, - "lookup_key": null, - "metadata": {}, - "nickname": null, - "object": "price", - "product": "prod_IuSf6H0sVxclQQ", - "recurring": { - "aggregate_usage": null, - "interval": "year", - "interval_count": 1, - "trial_period_days": null, - "usage_type": "licensed" - }, - "tiers_mode": null, - "transform_quantity": null, - "type": "recurring", - "unit_amount": 1000, - "unit_amount_decimal": "1000" - }, - "proration": false, - "quantity": 1, - "subscription": "sub_subscription_id", - "subscription_item": "si_JHFYTzY7HHw2TL", - "tax_amounts": [], - "tax_rates": [], - "type": "subscription", - "unique_id": "il_1Ieh35AQv52awOkHlHKcmvE7" - } - ], - "has_more": false, - "object": "list", - "total_count": 1, - "url": "/v1/invoices/in_1Ieh35AQv52awOkHrNk0JBjD/lines" - }, - "livemode": false, - "metadata": {}, - "next_payment_attempt": null, - "number": "F668967A-0008", - "object": "invoice", - "on_behalf_of": null, - "paid": true, - "payment_intent": "pi_1Ieh35AQv52awOkH1SXRtQJr", - "payment_settings": { - "payment_method_options": null, - "payment_method_types": null - }, - "period_end": 1618062031, - "period_start": 1618062031, - "post_payment_credit_notes_amount": 0, - "pre_payment_credit_notes_amount": 0, - "receipt_number": null, - "starting_balance": 0, - "statement_descriptor": null, - "status": "paid", - "status_transitions": { - "finalized_at": 1618062031, - "marked_uncollectible_at": null, - "paid_at": 1618062032, - "voided_at": null - }, - "subscription": "sub_subscription_id", - "subtotal": 1000, - "tax": null, - "tax_percent": null, - "total": 1000, - "total_discount_amounts": [], - "total_tax_amounts": [], - "transfer_data": null, - "webhooks_delivered_at": null - } - }, - "id": "evt_1Ieh38AQv52awOkHQMSLm5AU", - "livemode": false, - "object": "event", - "pending_webhooks": 2, - "request": { - "id": "req_7tZTpsoUXh4rvB", - "idempotency_key": null - }, - "type": "invoice.paid" -}""" -) - -INVOICE_PAID_PAYLOAD = util.convert_to_stripe_object(RAW_INVOICE_PAID_PAYLOAD) diff --git a/association-backend/src/webhooks/tests/handlers/stripe/test_handle_invoice_paid.py b/association-backend/src/webhooks/tests/handlers/stripe/test_handle_invoice_paid.py deleted file mode 100644 index be147153bb..0000000000 --- a/association-backend/src/webhooks/tests/handlers/stripe/test_handle_invoice_paid.py +++ /dev/null @@ -1,124 +0,0 @@ -from datetime import datetime, timezone - -import time_machine -from stripe import util -from ward import raises, test - -from src.association.tests.session import db -from src.association_membership.domain.entities import ( - PaymentStatus, - Subscription, - SubscriptionStatus, -) -from src.association_membership.tests.factories import ( - StripeCustomerFactory, - SubscriptionFactory, -) -from src.webhooks.exceptions import NoCustomerFoundForEvent -from src.webhooks.handlers.stripe.handle_invoice_paid import handle_invoice_paid -from src.webhooks.tests.handlers.stripe.payloads import ( - INVOICE_PAID_PAYLOAD, - RAW_INVOICE_PAID_PAYLOAD, -) - - -@test("receive a paid stripe subscription invoice") -async def _(db=db): - subscription = await SubscriptionFactory(user_id=1) - await StripeCustomerFactory(user_id=1, stripe_customer_id="cus_customer_id") - - with time_machine.travel("2022-02-10 12:00:00", tick=False): - await handle_invoice_paid(INVOICE_PAID_PAYLOAD) - - subscription = await Subscription.objects.select_related( - ["payments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - - assert subscription.status == SubscriptionStatus.ACTIVE - - payment = subscription.payments[0] - assert payment.status == PaymentStatus.PAID - assert payment.total == 1000 - assert payment.subscription.id == subscription.id - assert payment.payment_date == datetime.fromtimestamp(1618062032, tz=timezone.utc) - assert payment.period_start == datetime.fromtimestamp(1618062031, tz=timezone.utc) - assert payment.period_end == datetime.fromtimestamp(1649598031, tz=timezone.utc) - - stripe_subscription_payment = payment.stripesubscriptionpayments[0] - assert stripe_subscription_payment.stripe_subscription_id == "sub_subscription_id" - assert ( - stripe_subscription_payment.stripe_invoice_id == "in_1Ieh35AQv52awOkHrNk0JBjD" - ) - assert ( - stripe_subscription_payment.invoice_pdf - == "https://pay.stripe.com/invoice/acct_1AEbpzAQv52awOkH/invst_JHFYBOjnsmDH6yxnhbD2jeX09nN1QUC/pdf" - ) - - -@test("receive the same paid invoice twice doesn't record the payment twice") -async def _(db=db): - subscription = await SubscriptionFactory(user_id=1) - await StripeCustomerFactory(user_id=1, stripe_customer_id="cus_customer_id") - - with time_machine.travel("2022-02-10 12:00:00", tick=False): - await handle_invoice_paid(INVOICE_PAID_PAYLOAD) - - subscription = await Subscription.objects.select_related( - ["payments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - assert subscription.status == SubscriptionStatus.ACTIVE - assert len(subscription.payments) == 1 - - await handle_invoice_paid(INVOICE_PAID_PAYLOAD) - - subscription = await Subscription.objects.select_related( - ["payments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - assert subscription.status == SubscriptionStatus.ACTIVE - assert len(subscription.payments) == 1 - - -@test("receive a paid invoice for the past doesn't mark it as active") -async def _(db=db): - subscription = await SubscriptionFactory(user_id=1) - await StripeCustomerFactory(user_id=1, stripe_customer_id="cus_customer_id") - - with time_machine.travel("2021-10-10 12:00:00", tick=False): - await handle_invoice_paid( - util.convert_to_stripe_object( - { - **RAW_INVOICE_PAID_PAYLOAD, - "data": { - **RAW_INVOICE_PAID_PAYLOAD["data"], - "object": { - **RAW_INVOICE_PAID_PAYLOAD["data"]["object"], - "lines": { - "data": [ - { - **RAW_INVOICE_PAID_PAYLOAD["data"]["object"][ - "lines" - ]["data"][0], - "period": { - "end": 1607979272, # Mon Dec 14 2020 20:54:32 GMT+0000 - "start": 1576356872, # Sat Dec 14 2019 20:54:32 GMT+0000 - }, - } - ], - }, - }, - }, - } - ) - ) - - subscription = await Subscription.objects.select_related( - ["payments", "payments__stripesubscriptionpayments"] - ).get(user_id=1) - assert subscription.status == SubscriptionStatus.PENDING - assert len(subscription.payments) == 1 - - -@test("receiving a paid invoice for a customer without subscription throws an error") -async def _(db=db): - with raises(NoCustomerFoundForEvent): - await handle_invoice_paid(INVOICE_PAID_PAYLOAD) diff --git a/association-backend/src/webhooks/tests/test_views.py b/association-backend/src/webhooks/tests/test_views.py deleted file mode 100644 index 137b1f3510..0000000000 --- a/association-backend/src/webhooks/tests/test_views.py +++ /dev/null @@ -1,52 +0,0 @@ -import stripe -from starlette.testclient import TestClient -from ward import each, raises, test - -from main import app - - -@test("cannot call pretix webhook without auth") -def _(): - client = TestClient(app) - response = client.post("/pretix-webhook") - assert response.status_code == 404 - - -@test("pretix webhook doesn't allow method {method}") -def _(method=each("get", "delete", "patch")): - client = TestClient(app) - response = getattr(client, method)("/pretix-webhook") - assert response.status_code == 405 - - -@test("cannot call pretix webhook with incorrect basic auth") -def _(): - client = TestClient(app) - response = client.post("/pretix-webhook", auth=("pretix", "secret")) - assert response.status_code == 401 - assert "Invalid auth" in str(response.content) - - -@test("can call pretix webhook with correct basic auth") -def _(): - client = TestClient(app) - response = client.post( - "/pretix-webhook", - auth=("pretix", "pretix-webhook-secret"), - json={"action": "undefined"}, - ) - assert response.status_code == 200 - - -@test("call stripe webhook works without auth") -def _(): - client = TestClient(app) - with raises(stripe.error.SignatureVerificationError): - client.post("/stripe-webhook") - - -@test("call graphql url works without auth") -def _(): - client = TestClient(app) - response = client.get("/graphql") - assert response.status_code == 200 diff --git a/association-backend/src/webhooks/views.py b/association-backend/src/webhooks/views.py deleted file mode 100644 index e8080f0a47..0000000000 --- a/association-backend/src/webhooks/views.py +++ /dev/null @@ -1,40 +0,0 @@ -import logging - -import stripe -from starlette.authentication import requires -from starlette.responses import Response - -from src.association.settings import STRIPE_WEBHOOK_SECRET -from src.webhooks.handlers import run_handler - -logger = logging.getLogger(__file__) - - -async def stripe_webhook(request): - payload = await request.body() - - try: - signature = request.headers.get("stripe-signature") - event = stripe.Webhook.construct_event( - payload=payload, sig_header=signature, secret=str(STRIPE_WEBHOOK_SECRET) - ) - except ValueError as e: - logger.exception("Called stripe webhook but parsing failed!", exc_info=e) - raise - except stripe.error.SignatureVerificationError as e: - logger.exception( - "Called stripe webhook but signature validation failed!", exc_info=e - ) - raise - - event_type = event["type"] - await run_handler("stripe", event_type, event) - return Response(None, 200) - - -@requires(["authenticated", "pretix"], status_code=404) -async def pretix_webhook(request): - payload = await request.json() - action = payload["action"] - await run_handler("pretix", action, payload) - return Response(None, 200) diff --git a/cms/.coveragerc b/cms/.coveragerc deleted file mode 100644 index 5792127b07..0000000000 --- a/cms/.coveragerc +++ /dev/null @@ -1,34 +0,0 @@ -# .coveragerc to control coverage.py -[run] -branch = True - -source = . - -[report] -# Regexes for lines to exclude from consideration -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - - # Don't complain about missing debug-only code: - def __repr__ - def __str__ - if self\.debug - - # Don't complain if tests don't hit defensive assertion code: - raise AssertionError - raise NotImplementedError - - # Don't complain if non-runnable code isn't run: - if 0: - if __name__ == .__main__.: - - -omit = - ./.venv/** - ./**/tests/* - ./cms/settings/** - ./cms/wsgi.py - ./**/migrations/*.py - ./manage.py - ./conftest.py diff --git a/cms/.dockerignore b/cms/.dockerignore deleted file mode 100644 index 0c991ebe68..0000000000 --- a/cms/.dockerignore +++ /dev/null @@ -1,42 +0,0 @@ -# Django project -/media/ -/static/ -*.sqlite3 - -# Python and others -__pycache__ -*.pyc -.DS_Store -*.swp -/venv/ -/tmp/ -/.vagrant/ -/Vagrantfile.local -node_modules/ -/npm-debug.log -/.idea/ -.vscode -coverage -.python-version - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg - -.pdm.toml -.venv/ diff --git a/cms/.tool-versions b/cms/.tool-versions deleted file mode 100644 index 5686ee0dbc..0000000000 --- a/cms/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -nodejs 18.12.1 diff --git a/cms/Dockerfile b/cms/Dockerfile deleted file mode 100644 index 44c71725e0..0000000000 --- a/cms/Dockerfile +++ /dev/null @@ -1,53 +0,0 @@ -# Use an official Python runtime based on Debian 10 "buster" as a parent image. -FROM python:3.11-slim-bullseye - -# Add user that will be used in the container. -RUN useradd wagtail - -# Port used by this container to serve HTTP. -EXPOSE 8000 - -# Set environment variables. -# 1. Force Python stdout and stderr streams to be unbuffered. -# 2. Set PORT variable that is used by Gunicorn. This should match "EXPOSE" -# command. -ENV PYTHONUNBUFFERED=1 \ - PORT=8000 - -# Install system packages required by Wagtail and Django. -RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \ - build-essential \ - libpq-dev \ - libjpeg62-turbo-dev \ - zlib1g-dev \ - libwebp-dev \ - && rm -rf /var/lib/apt/lists/* - -# install PDM -RUN pip install -U pip setuptools wheel -RUN pip install pdm - -# Use /app folder as a directory where the source code is stored. -WORKDIR /app - -# Install the project requirements. -COPY pdm.lock pyproject.toml /app/ -RUN mkdir __pypackages__ && pdm install --no-lock --no-editable - -ENV PYTHONPATH=/app/pkgs - -# Set this directory to be owned by the "wagtail" user. This Wagtail project -# uses SQLite, the folder needs to be owned by the user that -# will be writing to the database file. -RUN chown -R wagtail:wagtail /app - -# Copy the source code of the project into the container. -COPY --chown=wagtail:wagtail . . - -# Use user "wagtail" to run the build commands below and the server itself. -USER wagtail - -# Collect static files. -RUN DJANGO_SETTINGS_MODULE=cms.settings.dev USERS_SERVICE=local SERVICE_TO_SERVICE_SECRET=secret pdm run python manage.py collectstatic --noinput --clear - -CMD pdm run gunicorn cms.wsgi:application diff --git a/cms/api/__init__.py b/cms/api/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/base/__init__.py b/cms/api/base/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/base/blocks/__init__.py b/cms/api/base/blocks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/base/blocks/accordion.py b/cms/api/base/blocks/accordion.py deleted file mode 100644 index 8854ade57e..0000000000 --- a/cms/api/base/blocks/accordion.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Self -import strawberry - - -@strawberry.type -class Accordion: - title: str - body: str - is_open: bool - - @classmethod - def from_block(cls, block) -> Self: - return cls( - title=block["title"], - body=block["body"], - is_open=block["is_open"], - ) diff --git a/cms/api/base/blocks/cta.py b/cms/api/base/blocks/cta.py deleted file mode 100644 index 79ea80a356..0000000000 --- a/cms/api/base/blocks/cta.py +++ /dev/null @@ -1,12 +0,0 @@ -from typing import Self -import strawberry - - -@strawberry.type(name="CTA") -class CTA: - label: str - link: str - - @classmethod - def from_block(cls, block) -> Self: - return cls(label=block["label"], link=block["link"]) diff --git a/cms/api/base/blocks/map.py b/cms/api/base/blocks/map.py deleted file mode 100644 index 52d6670925..0000000000 --- a/cms/api/base/blocks/map.py +++ /dev/null @@ -1,52 +0,0 @@ -from decimal import Decimal -from typing import Self -from django.conf import settings -import strawberry - - -def generate_map_image( - latitude: Decimal, longitude: Decimal, width: int, height: int, zoom: int -) -> str: - base = "https://api.mapbox.com/styles/v1/" - style = "mapbox/streets-v12" - token = f"access_token={settings.MAPBOX_PUBLIC_API_KEY}" - - coordinates = f"{longitude},{latitude}" - size = f"{width}x{height}@2x" - marker = f"pin-s-heart+285A98({coordinates})" - - return f"{base}{style}/static/{marker}/{coordinates},{zoom},0,13/{size}?{token}" - - -@strawberry.type -class CMSMap: - id: strawberry.ID - latitude: Decimal - longitude: Decimal - link: str - zoom: int - - @classmethod - def from_block(cls, block) -> Self: - return cls( - id=block.id, - latitude=block.value["latitude"], - longitude=block.value["longitude"], - link=block.value["link"], - zoom=block.value["zoom"], - ) - - @strawberry.field - def image( - self, - info, - width: int = 1280, - height: int = 400, - ) -> str: - return generate_map_image( - latitude=self.latitude, - longitude=self.longitude, - width=width, - height=height, - zoom=self.zoom, - ) diff --git a/cms/api/base/types.py b/cms/api/base/types.py deleted file mode 100644 index 7edc421705..0000000000 --- a/cms/api/base/types.py +++ /dev/null @@ -1,8 +0,0 @@ -import strawberry -import enum - - -@strawberry.enum -class Spacing(enum.Enum): - XL = "xl" - _3XL = "3xl" diff --git a/cms/api/home/__init__.py b/cms/api/home/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/home/blocks/__init__.py b/cms/api/home/blocks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/home/blocks/home_intro_section.py b/cms/api/home/blocks/home_intro_section.py deleted file mode 100644 index 066312efb1..0000000000 --- a/cms/api/home/blocks/home_intro_section.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Self -import strawberry - - -@strawberry.type -class HomeIntroSection: - id: strawberry.ID - pretitle: str - title: str - - @classmethod - def from_block(cls, block) -> Self: - return cls( - id=block.id, - pretitle=block.value["pretitle"], - title=block.value["title"], - ) diff --git a/cms/api/news/__init__.py b/cms/api/news/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/news/blocks/__init__.py b/cms/api/news/blocks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/news/blocks/news_grid_section.py b/cms/api/news/blocks/news_grid_section.py deleted file mode 100644 index 8ba230e94e..0000000000 --- a/cms/api/news/blocks/news_grid_section.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import Self -import strawberry - - -@strawberry.type -class NewsGridSection: - id: strawberry.ID - - @classmethod - def from_block(cls, block) -> Self: - return cls(id=block.id) diff --git a/cms/api/news/queries/__init__.py b/cms/api/news/queries/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/news/queries/news_article.py b/cms/api/news/queries/news_article.py deleted file mode 100644 index 504b5180fb..0000000000 --- a/cms/api/news/queries/news_article.py +++ /dev/null @@ -1,28 +0,0 @@ -import strawberry -from api.news.types import NewsArticle -from wagtail.models import Site -from news.models import NewsArticle as NewsArticleModel - - -@strawberry.field -def news_article(hostname: str, slug: str, language: str) -> NewsArticle | None: - site = Site.objects.filter(hostname=hostname).first() - - if not site: - raise ValueError(f"Site {hostname} not found") - - article = NewsArticleModel.objects.in_site(site).filter(slug=slug).first() - - if not article: - return None - - translated_article = ( - article.get_translations(inclusive=True) - .filter(locale__language_code=language) - .first() - ) - - if not translated_article: - return None - - return NewsArticle.from_model(translated_article) diff --git a/cms/api/news/queries/news_articles.py b/cms/api/news/queries/news_articles.py deleted file mode 100644 index e007a16350..0000000000 --- a/cms/api/news/queries/news_articles.py +++ /dev/null @@ -1,21 +0,0 @@ -import strawberry -from api.news.types import NewsArticle -from wagtail.models import Site -from news.models import NewsArticle as NewsArticleModel - - -@strawberry.field -def news_articles(hostname: str, language: str) -> list[NewsArticle]: - site = Site.objects.filter(hostname=hostname).first() - - if not site: - raise ValueError(f"Site {hostname} not found") - - return [ - NewsArticle.from_model(article) - for article in NewsArticleModel.objects.in_site(site) - .order_by("-first_published_at") - .filter( - locale__language_code=language, - ) - ] diff --git a/cms/api/news/types.py b/cms/api/news/types.py deleted file mode 100644 index 62861e82d7..0000000000 --- a/cms/api/news/types.py +++ /dev/null @@ -1,26 +0,0 @@ -import datetime -from typing import Self -import strawberry - - -@strawberry.type -class NewsArticle: - id: strawberry.ID - title: str - slug: str - excerpt: str - body: str - published_at: datetime.datetime - author_fullname: str - - @classmethod - def from_model(cls, model) -> Self: - return cls( - id=model.id, - title=model.title, - slug=model.slug, - excerpt=model.excerpt, - body=model.body, - published_at=model.first_published_at, - author_fullname=model.owner.get_full_name(), - ) diff --git a/cms/api/page/__init__.py b/cms/api/page/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/page/blocks/__init__.py b/cms/api/page/blocks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/page/blocks/checkout_section.py b/cms/api/page/blocks/checkout_section.py deleted file mode 100644 index ff8496db98..0000000000 --- a/cms/api/page/blocks/checkout_section.py +++ /dev/null @@ -1,45 +0,0 @@ -from enum import Enum -from typing import Self -import strawberry - - -class CheckoutCategory(Enum): - TICKETS = "tickets" - SOCIAL_EVENTS = "social_events" - TOURS = "tours" - GADGETS = "gadgets" - MEMBERSHIP = "membership" - HOTEL = "hotel" - - -CheckoutCategoryEnum = strawberry.enum(CheckoutCategory, name="CheckoutCategory") - - -@strawberry.type -class CheckoutSection: - id: strawberry.ID - visible_categories: list[CheckoutCategoryEnum] - - @classmethod - def from_block(cls, block) -> Self: - return cls(id=block.id, visible_categories=cls._get_visible_categories(block)) - - @staticmethod - def _get_visible_categories(block) -> list[CheckoutCategory]: - categories = [] - - match block.value: - case {"show_conference_tickets_products": True}: - categories.append(CheckoutCategory.TICKETS) - case {"show_social_events_products": True}: - categories.append(CheckoutCategory.SOCIAL_EVENTS) - case {"show_tours_products": True}: - categories.append(CheckoutCategory.TOURS) - case {"show_gadgets_products": True}: - categories.append(CheckoutCategory.GADGETS) - case {"show_membership_products": True}: - categories.append(CheckoutCategory.MEMBERSHIP) - case {"show_hotel_products": True}: - categories.append(CheckoutCategory.HOTEL) - - return categories diff --git a/cms/api/page/blocks/information_section.py b/cms/api/page/blocks/information_section.py deleted file mode 100644 index c630aad61a..0000000000 --- a/cms/api/page/blocks/information_section.py +++ /dev/null @@ -1,31 +0,0 @@ -import datetime -from typing import Self -import strawberry - -from api.base.blocks.cta import CTA - - -@strawberry.type -class InformationSection: - id: strawberry.ID - title: str - body: str - illustration: str - background_color: str - countdown_to_datetime: datetime.datetime | None - countdown_to_deadline: str | None - cta: CTA | None - - @classmethod - def from_block(cls, block) -> Self: - cta = block.value["cta"] - return cls( - id=block.id, - title=block.value["title"], - body=block.value["body"], - illustration=block.value["illustration"], - background_color=block.value["background_color"], - countdown_to_datetime=block.value["countdown_to_datetime"], - countdown_to_deadline=block.value["countdown_to_deadline"], - cta=CTA.from_block(cta) if cta["label"] else None, - ) diff --git a/cms/api/page/blocks/keynoters_section.py b/cms/api/page/blocks/keynoters_section.py deleted file mode 100644 index ee351d2c86..0000000000 --- a/cms/api/page/blocks/keynoters_section.py +++ /dev/null @@ -1,20 +0,0 @@ -from typing import Self -import strawberry - -from api.base.blocks.cta import CTA - - -@strawberry.type -class KeynotersSection: - id: strawberry.ID - title: str - cta: CTA | None - - @classmethod - def from_block(cls, block) -> Self: - cta = block.value["cta"] - return cls( - id=block.id, - title=block.value["title"], - cta=CTA.from_block(cta) if cta["label"] else None, - ) diff --git a/cms/api/page/blocks/live_streaming_section.py b/cms/api/page/blocks/live_streaming_section.py deleted file mode 100644 index fc0a326732..0000000000 --- a/cms/api/page/blocks/live_streaming_section.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Self -import strawberry - - -@strawberry.type -class LiveStreamingSection: - id: strawberry.ID - - @classmethod - def from_block(cls, block) -> Self: - return cls( - id=block.id, - ) diff --git a/cms/api/page/blocks/schedule_preview_section.py b/cms/api/page/blocks/schedule_preview_section.py deleted file mode 100644 index cd6136ee5a..0000000000 --- a/cms/api/page/blocks/schedule_preview_section.py +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Self -import strawberry - -from api.base.blocks.cta import CTA - - -@strawberry.type -class SchedulePreviewSection: - id: strawberry.ID - title: str - primary_cta: CTA | None - secondary_cta: CTA | None - - @classmethod - def from_block(cls, block) -> Self: - primary_cta = block.value["primary_cta"] - secondary_cta = block.value["secondary_cta"] - - return cls( - id=block.id, - title=block.value["title"], - primary_cta=(CTA.from_block(primary_cta) if primary_cta["label"] else None), - secondary_cta=( - CTA.from_block(secondary_cta) if secondary_cta["label"] else None - ), - ) diff --git a/cms/api/page/blocks/slider_cards_section.py b/cms/api/page/blocks/slider_cards_section.py deleted file mode 100644 index cfa2da3ab0..0000000000 --- a/cms/api/page/blocks/slider_cards_section.py +++ /dev/null @@ -1,71 +0,0 @@ -from typing import Self -import strawberry - -from api.base.blocks.cta import CTA -from api.base.types import Spacing - - -@strawberry.type -class SimpleTextCard: - title: str - body: str - cta: CTA | None - - @classmethod - def from_block(cls, block) -> Self: - cta = block.value["cta"] - return cls( - title=block.value["title"], - body=block.value["body"], - cta=CTA.from_block(cta) if cta["label"] else None, - ) - - -@strawberry.type -class PriceCard: - title: str - body: str - price: str - price_tier: str - cta: CTA | None - - @classmethod - def from_block(cls, block) -> Self: - cta = block.value["cta"] - return cls( - title=block.value["title"], - body=block.value["body"], - price=block.value["price"], - price_tier=block.value["price_tier"], - cta=CTA.from_block(cta) if cta["label"] else None, - ) - - -AvailableCards = strawberry.union("AvailableCards", (SimpleTextCard, PriceCard)) - - -@strawberry.type -class SliderCardsSection: - id: strawberry.ID - title: str - spacing: Spacing - snake_background: bool - cards: list[AvailableCards] - - @classmethod - def from_block(cls, block) -> Self: - cards = [] - for card in block.value["cards"]: - match card.block_type: - case "simple_text_card": - cards.append(SimpleTextCard.from_block(card)) - case "price_card": - cards.append(PriceCard.from_block(card)) - - return cls( - id=block.id, - title=block.value["title"], - snake_background=block.value["snake_background"], - spacing=Spacing(block.value["spacing"]), - cards=cards, - ) diff --git a/cms/api/page/blocks/socials_section.py b/cms/api/page/blocks/socials_section.py deleted file mode 100644 index 4eb017c975..0000000000 --- a/cms/api/page/blocks/socials_section.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Self -import strawberry - - -@strawberry.type -class SocialsSection: - id: strawberry.ID - label: str - hashtag: str - - @classmethod - def from_block(cls, block) -> Self: - return cls( - id=block.id, - label=block.value["label"], - hashtag=block.value["hashtag"], - ) diff --git a/cms/api/page/blocks/special_guest_section.py b/cms/api/page/blocks/special_guest_section.py deleted file mode 100644 index e7d6db63bb..0000000000 --- a/cms/api/page/blocks/special_guest_section.py +++ /dev/null @@ -1,34 +0,0 @@ -import datetime -from typing import Any -import strawberry - -from api.base.blocks.cta import CTA - - -@strawberry.type -class SpecialGuestSection: - id: strawberry.ID - title: str - guest_name: str - guest_job_title: str - event_date: datetime.date - cta: CTA | None - _block: strawberry.Private[Any] - - @strawberry.field - def guest_photo(self) -> str: - guest_photo = self._block.value["guest_photo"] - return guest_photo.get_rendition("fill-600x600|jpegquality-60").full_url - - @classmethod - def from_block(cls, block): - cta = block.value["cta"] - return cls( - id=block.id, - title=block.value["title"], - guest_name=block.value["guest_name"], - guest_job_title=block.value["guest_job_title"], - event_date=block.value["event_date"], - cta=CTA.from_block(cta) if cta["label"] else None, - _block=block, - ) diff --git a/cms/api/page/blocks/sponsors_section.py b/cms/api/page/blocks/sponsors_section.py deleted file mode 100644 index 26f24102a5..0000000000 --- a/cms/api/page/blocks/sponsors_section.py +++ /dev/null @@ -1,30 +0,0 @@ -import enum -from typing import Self -import strawberry - -from api.base.blocks.cta import CTA - - -@strawberry.enum -class SponsorsSectionLayout(enum.Enum): - SIDE_BY_SIDE = "side-by-side" - VERTICAL = "vertical" - - -@strawberry.type -class SponsorsSection: - id: strawberry.ID - title: str - body: str - cta: CTA | None - layout: SponsorsSectionLayout - - @classmethod - def from_block(cls, block) -> Self: - return cls( - id=block.id, - title=block.value["title"], - body=block.value["body"], - cta=CTA.from_block(block.value["cta"]), - layout=SponsorsSectionLayout(block.value["layout"]), - ) diff --git a/cms/api/page/blocks/text_section.py b/cms/api/page/blocks/text_section.py deleted file mode 100644 index 49583286aa..0000000000 --- a/cms/api/page/blocks/text_section.py +++ /dev/null @@ -1,43 +0,0 @@ -from enum import Enum -from typing import Self -import strawberry - -from api.base.blocks.accordion import Accordion -from api.base.blocks.cta import CTA - - -@strawberry.enum -class BodyTextSize(Enum): - TEXT_1 = "text-1" - TEXT_2 = "text-2" - - -@strawberry.type -class TextSection: - id: strawberry.ID - title: str - is_main_title: bool - subtitle: str - body: str - body_text_size: BodyTextSize - illustration: str - accordions: list[Accordion] - cta: CTA | None - - @classmethod - def from_block(cls, block) -> Self: - cta = block.value["cta"] - return cls( - id=block.id, - title=block.value["title"], - is_main_title=block.value["is_main_title"], - subtitle=block.value["subtitle"], - body=block.value["body"], - body_text_size=BodyTextSize(block.value["body_text_size"]), - illustration=block.value["illustration"], - accordions=[ - Accordion.from_block(accordion) - for accordion in block.value["accordions"] - ], - cta=CTA.from_block(cta) if cta["label"] else None, - ) diff --git a/cms/api/page/queries/__init__.py b/cms/api/page/queries/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/page/queries/cms_page.py b/cms/api/page/queries/cms_page.py deleted file mode 100644 index f135dbd829..0000000000 --- a/cms/api/page/queries/cms_page.py +++ /dev/null @@ -1,32 +0,0 @@ -from page.models import GenericPage as GenericPageModel -from wagtail.models import Site - -import strawberry - -from api.page.types import GenericPage, SiteNotFoundError - - -@strawberry.field -def cms_page( - hostname: str, - slug: str, - language: str, -) -> GenericPage | SiteNotFoundError | None: - if not (site := Site.objects.filter(hostname=hostname).first()): - return SiteNotFoundError(message=f"Site `{hostname}` not found") - - page = GenericPageModel.objects.in_site(site).filter(slug=slug).first() - - if not page: - return None - - translated_page = ( - page.get_translations(inclusive=True) - .filter(locale__language_code=language) - .first() - ) - - if not translated_page: - return None - - return GenericPage.from_model(translated_page) diff --git a/cms/api/page/queries/cms_pages.py b/cms/api/page/queries/cms_pages.py deleted file mode 100644 index a0d20c02a4..0000000000 --- a/cms/api/page/queries/cms_pages.py +++ /dev/null @@ -1,19 +0,0 @@ -import strawberry -from wagtail.models import Site -from page.models import GenericPage as GenericPageModel - -from api.page.types import GenericPage - - -@strawberry.field -def cms_pages(hostname: str, language: str) -> list[GenericPage]: - if not (site := Site.objects.filter(hostname=hostname).first()): - return [] - - return [ - GenericPage.from_model(page) - for page in GenericPageModel.objects.in_site(site).filter( - locale__language_code=language, - ) - if page.slug != "homepage" - ] diff --git a/cms/api/page/types.py b/cms/api/page/types.py deleted file mode 100644 index 5ba93bb8dd..0000000000 --- a/cms/api/page/types.py +++ /dev/null @@ -1,66 +0,0 @@ -from typing import Self -from page.models import GenericPage as GenericPageModel - -import strawberry - -from api.page.blocks.slider_cards_section import SliderCardsSection -from api.page.blocks.text_section import TextSection -from api.base.blocks.map import CMSMap -from api.home.blocks.home_intro_section import HomeIntroSection -from api.page.blocks.sponsors_section import SponsorsSection -from api.page.blocks.schedule_preview_section import SchedulePreviewSection -from api.page.blocks.keynoters_section import KeynotersSection -from api.page.blocks.socials_section import SocialsSection -from api.page.blocks.special_guest_section import SpecialGuestSection -from api.page.blocks.information_section import InformationSection -from api.news.blocks.news_grid_section import NewsGridSection -from api.page.blocks.checkout_section import CheckoutSection -from api.page.blocks.live_streaming_section import LiveStreamingSection - - -@strawberry.type -class SiteNotFoundError: - message: str - - -REGISTRY = { - "text_section": TextSection, - "map": CMSMap, - "slider_cards_section": SliderCardsSection, - "sponsors_section": SponsorsSection, - "home_intro_section": HomeIntroSection, - "keynoters_section": KeynotersSection, - "schedule_preview_section": SchedulePreviewSection, - "socials_section": SocialsSection, - "special_guest_section": SpecialGuestSection, - "information_section": InformationSection, - "news_grid_section": NewsGridSection, - "checkout_section": CheckoutSection, - "live_streaming_section": LiveStreamingSection, -} - -Block = strawberry.union( - "Block", - REGISTRY.values(), -) - - -@strawberry.type -class GenericPage: - id: strawberry.ID - title: str - search_description: str - slug: str - body: list[Block] - - @classmethod - def from_model(cls, obj: GenericPageModel) -> Self: - return cls( - id=obj.id, - title=obj.seo_title or obj.title, - search_description=obj.search_description, - slug=obj.slug, - body=[ - REGISTRY.get(block.block_type).from_block(block) for block in obj.body - ], - ) diff --git a/cms/api/schema.py b/cms/api/schema.py deleted file mode 100644 index 622729d1bb..0000000000 --- a/cms/api/schema.py +++ /dev/null @@ -1,12 +0,0 @@ -from strawberry.tools import create_type - -import strawberry - - -@strawberry.type -def cms_empty_query(): - return "cms" - - -Query = create_type("Query", fields=[cms_empty_query]) -schema = strawberry.federation.Schema(query=Query) diff --git a/cms/api/tests/__init__.py b/cms/api/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/tests/factories.py b/cms/api/tests/factories.py deleted file mode 100644 index 7352bd09bf..0000000000 --- a/cms/api/tests/factories.py +++ /dev/null @@ -1,84 +0,0 @@ -from page.models import GenericPage -from page.blocks.text_section import TextSection -from page.blocks.slider_cards_section import SimpleTextCard, SliderCardsSection -from page.models import BodyBlock -from base.blocks.map import Map -from wagtail_factories import ( - CharBlockFactory, - StreamBlockFactory, - StructBlockFactory, - PageFactory, - StreamFieldFactory, - SiteFactory, -) -import factory -from decimal import Decimal -from pytest_factoryboy import register -from wagtail.rich_text import RichText - - -register(SiteFactory) -register(PageFactory) - - -@register -class MapFactory(StructBlockFactory): - latitude = Decimal(43.766200) - longitude = Decimal(11.272250) - zoom = 15 - - class Meta: - model = Map - - -@register -class TextSectionFactory(StructBlockFactory): - title = factory.SubFactory(CharBlockFactory) - subtitle = factory.SubFactory(CharBlockFactory) - body = factory.LazyAttribute(lambda o: RichText(f"

{o.h2}

" f"

{o.p}

")) - illustration = factory.SubFactory(CharBlockFactory) - - class Meta: - model = TextSection - - class Params: - h2 = factory.Faker("text", max_nb_chars=20) - p = factory.Faker("text", max_nb_chars=300) - - -@register -class SimpleTextCardFactory(StructBlockFactory): - title = factory.SubFactory(CharBlockFactory) - body = factory.LazyAttribute(lambda o: RichText(f"

{o.h2}

" f"

{o.p}

")) - - class Meta: - model = SimpleTextCard - - class Params: - h2 = factory.Faker("text", max_nb_chars=20) - p = factory.Faker("text", max_nb_chars=300) - - -@register -class SliderCardsSectionFactory(StreamBlockFactory): - cards = factory.SubFactory(SimpleTextCardFactory) - - class Meta: - model = SliderCardsSection - - -@register -class BodyBlockFactory(StreamBlockFactory): - text_section = factory.SubFactory(TextSectionFactory) - map = factory.SubFactory(MapFactory) - - class Meta: - model = BodyBlock - - -@register -class GenericPageFactory(PageFactory): - body = StreamFieldFactory(BodyBlockFactory) - - class Meta: - model = GenericPage diff --git a/cms/api/tests/news/__init__.py b/cms/api/tests/news/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/tests/news/test_queries.py b/cms/api/tests/news/test_queries.py deleted file mode 100644 index f587127870..0000000000 --- a/cms/api/tests/news/test_queries.py +++ /dev/null @@ -1,277 +0,0 @@ -import datetime -import pytest - -pytestmark = pytest.mark.django_db - - -def test_get_news_articles( - graphql_client, - generic_page_factory, - news_article_factory, - site_factory, - create_user, -): - user = create_user(username="test") - parent = generic_page_factory() - article_1 = news_article_factory( - title="Article 1", - parent=parent, - owner=user, - first_published_at=datetime.datetime(2010, 1, 1, 10, 0, 0), - ) - article_2 = news_article_factory( - title="Article 2", - parent=parent, - owner=user, - first_published_at=datetime.datetime(2012, 1, 1, 10, 0, 0), - ) - site_factory(hostname="pycon", root_page=parent) - - parent_2 = generic_page_factory() - news_article_factory(title="Invalid", parent=parent_2) - site_factory(hostname="pycon2", root_page=parent_2) - - query = """query NewsArticles($hostname: String!, $language: String!) { - newsArticles(hostname: $hostname, language: $language) { - id - title - } - }""" - - response = graphql_client.query( - query, variables={"hostname": "pycon", "language": "en"} - ) - - assert response.data["newsArticles"] == [ - {"id": str(article_2.id), "title": article_2.title}, - {"id": str(article_1.id), "title": article_1.title}, - ] - - -def test_get_news_articles_with_invalid_site( - graphql_client, - generic_page_factory, - news_article_factory, - site_factory, - create_user, -): - user = create_user(username="test") - parent = generic_page_factory() - news_article_factory( - title="Article 1", - parent=parent, - owner=user, - first_published_at=datetime.datetime(2010, 1, 1, 10, 0, 0), - ) - site_factory(hostname="pycon2", root_page=parent) - - query = """query NewsArticles($hostname: String!, $language: String!) { - newsArticles(hostname: $hostname, language: $language) { - id - title - } - }""" - - response = graphql_client.query( - query, variables={"hostname": "invalid", "language": "en"}, asserts_errors=False - ) - assert response.errors[0]["message"] == "Site invalid not found" - - -def test_get_news_article( - graphql_client, - generic_page_factory, - news_article_factory, - site_factory, - create_user, -): - user = create_user(username="test", first_name="marco", last_name="world") - parent = generic_page_factory() - article_1 = news_article_factory( - title="Article 1", - parent=parent, - owner=user, - slug="slug", - first_published_at=datetime.datetime(2010, 1, 1, 10, 0, 0), - ) - site_factory(hostname="pycon", root_page=parent) - - query = """query NewsArticle( - $hostname: String!, - $slug: String!, - $language: String! - ) { - newsArticle(hostname: $hostname, slug: $slug, language: $language) { - id - title - authorFullname - } - }""" - - response = graphql_client.query( - query, variables={"hostname": "pycon", "slug": article_1.slug, "language": "en"} - ) - - assert response.data["newsArticle"] == { - "id": str(article_1.id), - "title": article_1.title, - "authorFullname": "marco world", - } - - -def test_get_news_article_another_locale( - graphql_client, - generic_page_factory, - news_article_factory, - site_factory, - create_user, - locale, -): - user = create_user(username="test", first_name="marco", last_name="world") - parent = generic_page_factory() - article_1 = news_article_factory( - title="Article 1", - parent=parent, - owner=user, - slug="slug", - first_published_at=datetime.datetime(2010, 1, 1, 10, 0, 0), - ) - site_factory(hostname="pycon", root_page=parent) - it_article = article_1.copy_for_translation(locale=locale("it")) - it_article.title = "test" - it_article.save() - - query = """query NewsArticle( - $hostname: String!, - $slug: String!, - $language: String! - ) { - newsArticle(hostname: $hostname, slug: $slug, language: $language) { - id - title - authorFullname - } - }""" - - response = graphql_client.query( - query, variables={"hostname": "pycon", "slug": article_1.slug, "language": "it"} - ) - - assert response.data["newsArticle"]["title"] == "test" - - -def test_get_news_article_with_unknown_slug( - graphql_client, - generic_page_factory, - news_article_factory, - site_factory, - create_user, -): - user = create_user(username="test", first_name="marco", last_name="world") - parent = generic_page_factory() - news_article_factory( - title="Article 1", - parent=parent, - owner=user, - slug="slug", - first_published_at=datetime.datetime(2010, 1, 1, 10, 0, 0), - ) - site_factory(hostname="pycon", root_page=parent) - - query = """query NewsArticle( - $hostname: String!, - $slug: String!, - $language: String! - ) { - newsArticle(hostname: $hostname, slug: $slug, language: $language) { - id - title - authorFullname - } - }""" - - response = graphql_client.query( - query, variables={"hostname": "pycon", "slug": "other", "language": "en"} - ) - - assert response.data["newsArticle"] is None - - -def test_get_news_article_with_unknown_locale( - graphql_client, - locale, - generic_page_factory, - news_article_factory, - site_factory, - create_user, -): - user = create_user(username="test", first_name="marco", last_name="world") - parent = generic_page_factory() - news_article_factory( - title="Article 1", - parent=parent, - locale=locale("en"), - owner=user, - slug="slug", - first_published_at=datetime.datetime(2010, 1, 1, 10, 0, 0), - ) - site_factory(hostname="pycon", root_page=parent) - - query = """query NewsArticle( - $hostname: String!, - $slug: String!, - $language: String! - ) { - newsArticle(hostname: $hostname, slug: $slug, language: $language) { - id - title - authorFullname - } - }""" - - response = graphql_client.query( - query, variables={"hostname": "pycon", "slug": "other", "language": "de"} - ) - - assert response.data["newsArticle"] is None - - -def test_get_news_article_with_invalid_site( - graphql_client, - locale, - generic_page_factory, - news_article_factory, - site_factory, - create_user, -): - user = create_user(username="test", first_name="marco", last_name="world") - parent = generic_page_factory() - news_article_factory( - title="Article 1", - parent=parent, - locale=locale("en"), - owner=user, - slug="slug", - first_published_at=datetime.datetime(2010, 1, 1, 10, 0, 0), - ) - site_factory(hostname="pycon", root_page=parent) - - query = """query NewsArticle( - $hostname: String!, - $slug: String!, - $language: String! - ) { - newsArticle(hostname: $hostname, slug: $slug, language: $language) { - id - title - authorFullname - } - }""" - - response = graphql_client.query( - query, - variables={"hostname": "invalid", "slug": "other", "language": "de"}, - asserts_errors=False, - ) - - assert response.errors[0]["message"] == "Site invalid not found" diff --git a/cms/api/tests/pages/__init__.py b/cms/api/tests/pages/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/api/tests/pages/test_queries.py b/cms/api/tests/pages/test_queries.py deleted file mode 100644 index 5878ce8379..0000000000 --- a/cms/api/tests/pages/test_queries.py +++ /dev/null @@ -1,236 +0,0 @@ -from decimal import Decimal -import pytest - -pytestmark = pytest.mark.django_db - - -def test_page(graphql_client, generic_page_factory, locale, site_factory): - parent = generic_page_factory() - page = generic_page_factory( - slug="bubble-tea", - locale=locale("en"), - parent=parent, - title="Bubble", - body__0__text_section__title__value="I've Got a Lovely Bunch of Coconuts", - body__1__map__longitude=Decimal(3.14), - ) - site_factory(hostname="pycon", root_page=parent) - page.copy_for_translation(locale=locale("it")) - query = """ - query Page ($hostname: String!, $language: String!, $slug: String!) { - cmsPage(hostname: $hostname, language: $language, slug: $slug){ - ...on GenericPage { - title - slug - body { - ...on TextSection { - title - } - ...on CMSMap { - latitude - longitude - } - } - } - } - } - """ - - response = graphql_client.query( - query, variables={"hostname": "pycon", "slug": "bubble-tea", "language": "en"} - ) - - assert response.data == { - "cmsPage": { - "title": "Bubble", - "slug": "bubble-tea", - "body": [ - {"title": "I've Got a Lovely Bunch of " "Coconuts"}, - { - "latitude": "43.766199999999997771737980656325817108154296875", # noqa: E501 - "longitude": "3.140000000000000124344978758017532527446746826171875", # noqa: E501 - }, - ], - } - } - - -def test_page_for_unknown_locale( - graphql_client, generic_page_factory, locale, site_factory -): - parent = generic_page_factory() - page = generic_page_factory( - slug="bubble-tea", - locale=locale("en"), - parent=parent, - title="Bubble", - body__0__text_section__title__value="I've Got a Lovely Bunch of Coconuts", - body__1__map__longitude=Decimal(3.14), - ) - site_factory(hostname="pycon", root_page=parent) - page.copy_for_translation(locale=locale("it")) - query = """ - query Page ($hostname: String!, $language: String!, $slug: String!) { - cmsPage(hostname: $hostname, language: $language, slug: $slug){ - ...on GenericPage { - title - body { - ...on TextSection { - title - } - } - } - } - } - """ - - response = graphql_client.query( - query, variables={"hostname": "pycon", "slug": "bubble-tea", "language": "de"} - ) - - assert response.data == {"cmsPage": None} - - -def test_page_not_found(graphql_client, site_factory): - site_factory(hostname="not-found") - query = """ - query Page ($hostname: String!, $language: String!, $slug: String!) { - cmsPage(hostname: $hostname, language: $language, slug: $slug){ - ...on GenericPage { - body { - ...on TextSection { - title - } - } - } - } - } - """ - - response = graphql_client.query( - query, variables={"hostname": "not-found", "slug": "hot-tea", "language": "en"} - ) - assert response.data == {"cmsPage": None} - - -def test_page_site_not_found(graphql_client): - query = """ - query Page ($hostname: String!, $language: String!, $slug: String!) { - cmsPage(hostname: $hostname, language: $language, slug: $slug){ - ...on SiteNotFoundError { - message - } - } - } - """ - - response = graphql_client.query( - query, variables={"hostname": "not-found", "slug": "hot-tea", "language": "en"} - ) - assert response.data == {"cmsPage": {"message": "Site `not-found` not found"}} - - -def test_pages(graphql_client, site_factory, generic_page_factory, locale): - parent = generic_page_factory() - generic_page_factory( - slug="bubble-tea", - locale=locale("en"), - parent=parent, - body__0__text_section__title__value="I've Got a Lovely Bunch of Coconuts", - ) - generic_page_factory( - slug="chocolate", - locale=locale("en"), - parent=parent, - body__0__text_section__title__value="There they are, all standing in a row", - ) - site_factory(hostname="pycon", root_page=parent) - - query = """ - query Page ($hostname: String!, $language: String!) { - cmsPages(hostname: $hostname, language: $language){ - body { - ...on TextSection { - title - } - } - } - } - """ - - response = graphql_client.query( - query, variables={"hostname": "pycon", "language": "en"} - ) - - assert response.data == { - "cmsPages": [ - {"body": []}, - {"body": [{"title": "I've Got a Lovely Bunch of Coconuts"}]}, - {"body": [{"title": "There they are, all standing in a row"}]}, - ] - } - - -def test_pages_site_not_found(graphql_client): - query = """ - query Page ($hostname: String!, $language: String!) { - cmsPages(hostname: $hostname, language: $language){ - body { - ...on TextSection { - title - } - } - } - } - """ - - response = graphql_client.query( - query, variables={"hostname": "not-found", "slug": "hot-tea", "language": "en"} - ) - - assert response.data == {"cmsPages": []} - - -def test_page_filter_by_site_and_language( - graphql_client, site_factory, generic_page_factory, locale -): - root_site_1 = generic_page_factory() - root_site_2 = generic_page_factory() - page_1 = generic_page_factory( - slug="bubble-tea", - locale=locale("en"), - parent=root_site_1, - body__0__text_section__title__value="I've Got a Lovely Bunch of Coconuts", - ) - page_2 = generic_page_factory( - slug="chocolate", - locale=locale("en"), - parent=root_site_2, - body__0__text_section__title__value="There they are, all standing in a row", - ) - site_factory(hostname="site1", root_page=root_site_1) - site_factory(hostname="site2", root_page=root_site_2) - page_1.copy_for_translation(locale=locale("it")) - page_2.copy_for_translation(locale=locale("it")) - - query = """ - query Page ($hostname: String!, $language: String!, $slug: String!) { - cmsPage(hostname: $hostname, language: $language, slug: $slug){ - ...on GenericPage { - body { - ...on TextSection { - title - } - } - } - } - } - """ - - response = graphql_client.query( - query, variables={"hostname": "site2", "slug": "chocolate", "language": "en"} - ) - - assert response.data == { - "cmsPage": {"body": [{"title": "There they are, all standing in a row"}]} - } diff --git a/cms/api/views.py b/cms/api/views.py deleted file mode 100644 index 0c2c9ad129..0000000000 --- a/cms/api/views.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.http import HttpRequest, HttpResponse -from strawberry.django.views import GraphQLView as BaseGraphQLView - - -class GraphQLView(BaseGraphQLView): - def get_context(self, request: HttpRequest, response: HttpResponse) -> dict: - return { - "request": request, - } diff --git a/cms/base/__init__.py b/cms/base/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/base/blocks/__init__.py b/cms/base/blocks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/base/blocks/accordion.py b/cms/base/blocks/accordion.py deleted file mode 100644 index 4619a18d33..0000000000 --- a/cms/base/blocks/accordion.py +++ /dev/null @@ -1,10 +0,0 @@ -from wagtail import blocks - - -class Accordion(blocks.StructBlock): - title = blocks.CharBlock() - body = blocks.RichTextBlock() - is_open = blocks.BooleanBlock(required=False, default=True) - - class Meta: - icon = "arrow-down" diff --git a/cms/base/blocks/cta.py b/cms/base/blocks/cta.py deleted file mode 100644 index c949110e5f..0000000000 --- a/cms/base/blocks/cta.py +++ /dev/null @@ -1,10 +0,0 @@ -from wagtail import blocks - - -class CTA(blocks.StructBlock): - label = blocks.CharBlock(required=False) - link = blocks.CharBlock(required=False) - - class Meta: - label = "Call to Action" - icon = "site" diff --git a/cms/base/blocks/map.py b/cms/base/blocks/map.py deleted file mode 100644 index 79b41321ba..0000000000 --- a/cms/base/blocks/map.py +++ /dev/null @@ -1,11 +0,0 @@ -from wagtail import blocks - - -class Map(blocks.StructBlock): - longitude = blocks.DecimalBlock(max_digits=9, decimal_places=6) - latitude = blocks.DecimalBlock(max_digits=9, decimal_places=6) - zoom = blocks.IntegerBlock(default=15) - link = blocks.URLBlock() - - class Meta: - icon = "globe" diff --git a/cms/cms/__init__.py b/cms/cms/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/cms/settings/__init__.py b/cms/cms/settings/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/cms/settings/base.py b/cms/cms/settings/base.py deleted file mode 100644 index 1738d32404..0000000000 --- a/cms/cms/settings/base.py +++ /dev/null @@ -1,170 +0,0 @@ -""" -Django settings for cms project. - -Generated by 'django-admin startproject' using Django 4.1.7. - -For more information on this file, see -https://docs.djangoproject.com/en/4.1/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/4.1/ref/settings/ -""" - -import environ -import sentry_sdk -from sentry_sdk.integrations.django import DjangoIntegration - -root = environ.Path(__file__) - 3 -env = environ.Env( - DEBUG=(bool, False), -) - -environ.Env.read_env(root(".env")) - -DEBUG = env("DEBUG") - -INSTALLED_APPS = [ - "home", - "page.apps.PageConfig", - "wagtail_localize", - "wagtail_localize.locales", - "wagtail.contrib.forms", - "wagtail.contrib.redirects", - "wagtail.contrib.settings", - "wagtail.embeds", - "wagtail.sites", - "wagtail.users", - "wagtail.snippets", - "wagtail.documents", - "wagtail.images", - "wagtail.search", - "wagtail.admin", - "wagtail", - "modelcluster", - "taggit", - "sites.apps.SitesConfig", - "news.apps.NewsConfig", - "django.contrib.admin", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.staticfiles", -] - -MIDDLEWARE = [ - "django.contrib.sessions.middleware.SessionMiddleware", - "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", - "django.middleware.security.SecurityMiddleware", - "whitenoise.middleware.WhiteNoiseMiddleware", - "django.middleware.locale.LocaleMiddleware", - "wagtail.contrib.redirects.middleware.RedirectMiddleware", -] - -ROOT_URLCONF = "cms.urls" - - -TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [ - root("templates"), - ], - "APP_DIRS": True, - "OPTIONS": { - "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.contrib.messages.context_processors.messages", - ], - }, - }, -] - -WSGI_APPLICATION = "cms.wsgi.application" - -DATABASES = {"default": env.db(default="sqlite:///{}".format(root("db.sqlite3")))} - -AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", # noqa: E501 - }, - { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", - }, -] - -LANGUAGE_CODE = "en-us" - -TIME_ZONE = "UTC" - -USE_I18N = True -WAGTAIL_I18N_ENABLED = True - -USE_TZ = True - -STATICFILES_FINDERS = [ - "django.contrib.staticfiles.finders.FileSystemFinder", - "django.contrib.staticfiles.finders.AppDirectoriesFinder", -] - -STATICFILES_DIRS = [] - -STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" - -STATIC_ROOT = root("static") -STATIC_URL = "/static/" - -MEDIA_ROOT = root("media") -MEDIA_URL = "/media/" - -USERS_SERVICE_URL = env("USERS_SERVICE") -SERVICE_TO_SERVICE_SECRET = env("SERVICE_TO_SERVICE_SECRET") - -AUTHENTICATION_BACKENDS = ("custom_auth.backend.UsersAuthBackend",) - -# Wagtail settings - -WAGTAIL_SITE_NAME = "cms" - -# Search -# https://docs.wagtail.org/en/stable/topics/search/backends.html -WAGTAILSEARCH_BACKENDS = { - "default": { - "BACKEND": "wagtail.search.backends.database", - } -} - -# Base URL to use when referring to full URLs within the Wagtail admin backend - -# e.g. in notification emails. Don't include '/admin' or a trailing slash -WAGTAILADMIN_BASE_URL = "http://cms.python.it" - -WAGTAIL_CONTENT_LANGUAGES = LANGUAGES = [ - ("en", "English"), - ("it", "Italian"), -] - -MAPBOX_PUBLIC_API_KEY = env("MAPBOX_PUBLIC_API_KEY", default="") - -SENTRY_DSN = env("SENTRY_DSN", default="") -ENVIRONMENT = env("ENVIRONMENT", default="local") - -if SENTRY_DSN: - sentry_sdk.init( - dsn=SENTRY_DSN, - integrations=[ - DjangoIntegration(), - ], - environment=ENVIRONMENT, - ) diff --git a/cms/cms/settings/dev.py b/cms/cms/settings/dev.py deleted file mode 100644 index d9a1c5052a..0000000000 --- a/cms/cms/settings/dev.py +++ /dev/null @@ -1,12 +0,0 @@ -from .base import * # noqa - -DEBUG = True -SECRET_KEY = "django-insecure-ss+@vr3zfthhbi%i7epq=6ul1pu-)(wgi@(am^3u-!g#eq@=8l" -ALLOWED_HOSTS = ["*"] -EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" - - -try: - from .local import * # noqa -except ImportError: - pass diff --git a/cms/cms/settings/prod.py b/cms/cms/settings/prod.py deleted file mode 100644 index c11ea56f22..0000000000 --- a/cms/cms/settings/prod.py +++ /dev/null @@ -1,22 +0,0 @@ -from .base import * # noqa -from .base import env - -SECRET_KEY = env("SECRET_KEY") -ALLOWED_HOSTS = [ - "cms.python.it", - "staging-cms.python.it", - "pythonit-staging-cms.politesky-d9883aec.westeurope.azurecontainerapps.io", - "pythonit-production-cms.blueisland-671ab1bc.westeurope.azurecontainerapps.io", -] -CSRF_TRUSTED_ORIGINS = [ - "https://cms.python.it", - "https://staging-cms.python.it", - "https://pythonit-staging-cms.politesky-d9883aec.westeurope.azurecontainerapps.io", - "https://pythonit-production-cms.blueisland-671ab1bc.westeurope.azurecontainerapps.io", -] - -DEFAULT_FILE_STORAGE = "storages.backends.azure_storage.AzureStorage" - -AZURE_ACCOUNT_NAME = env("AZURE_ACCOUNT_NAME") -AZURE_ACCOUNT_KEY = env("AZURE_ACCOUNT_KEY") -AZURE_CONTAINER = env("AZURE_CONTAINER") diff --git a/cms/cms/urls.py b/cms/cms/urls.py deleted file mode 100644 index 04ccc08ab1..0000000000 --- a/cms/cms/urls.py +++ /dev/null @@ -1,33 +0,0 @@ -from django.conf import settings -from django.urls import include, path -from django.contrib import admin - -from wagtail.admin import urls as wagtailadmin_urls -from wagtail import urls as wagtail_urls -from wagtail.documents import urls as wagtaildocs_urls - -from api.schema import schema -from api.views import GraphQLView - -urlpatterns = [ - path("django-admin/", admin.site.urls), - path("admin/", include(wagtailadmin_urls)), - path("documents/", include(wagtaildocs_urls)), - path("graphql/", GraphQLView.as_view(schema=schema)), -] - - -if settings.DEBUG: - from django.conf.urls.static import static - - urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) - -urlpatterns = urlpatterns + [ - # For anything not caught by a more specific rule above, hand over to - # Wagtail's page serving mechanism. This should be the last pattern in - # the list: - path("", include(wagtail_urls)), - # Alternatively, if you want Wagtail pages to be served from a subpath - # of your site, rather than the site root: - # path("pages/", include(wagtail_urls)), -] diff --git a/cms/cms/wsgi.py b/cms/cms/wsgi.py deleted file mode 100644 index 1a8f7fa09c..0000000000 --- a/cms/cms/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for cms project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings.prod") - -application = get_wsgi_application() diff --git a/cms/conftest.py b/cms/conftest.py deleted file mode 100644 index 0dd9945f27..0000000000 --- a/cms/conftest.py +++ /dev/null @@ -1,42 +0,0 @@ -import os -from api.tests.factories import * # noqa -from sites.tests.factories import * # noqa -from news.tests.factories import * # noqa - -import pytest -from wagtail.models import Locale -from strawberry.django.test.client import GraphQLTestClient -from io import BytesIO - -import PIL.Image -from django.core.files.images import ImageFile -from django.contrib.auth import get_user_model - - -@pytest.fixture -def create_user(): - return lambda **kwargs: get_user_model().objects.create_user(**kwargs) - - -@pytest.fixture -def locale(): - return lambda code: Locale.objects.get_or_create(language_code=code)[0] - - -@pytest.fixture -def graphql_client(client): - return GraphQLTestClient(client=client) - - -@pytest.fixture -def image_file(): - def wrapper(filename: str = "test.jpg"): - file = BytesIO() - image = PIL.Image.new("RGB", (640, 480), "white") - image.save(file, "JPEG") - - yield ImageFile(file, name=filename) - - os.remove(filename) - - return wrapper diff --git a/cms/custom_auth/backend.py b/cms/custom_auth/backend.py deleted file mode 100644 index 0f49c0e369..0000000000 --- a/cms/custom_auth/backend.py +++ /dev/null @@ -1,69 +0,0 @@ -import logging - -from django.contrib.auth.backends import BaseBackend - - -logger = logging.getLogger(__name__) - - -LOGIN_QUERY = """mutation($input: LoginInput!) { - login(input: $input) { - __typename - id - email - fullname - } -} -""" - - -class UsersAuthBackend(BaseBackend): - def authenticate(self, request, username=None, password=None): - return None - # client = ServiceClient( - # url=f"{settings.USERS_SERVICE_URL}/internal-api", - # service_name="users-backend", - # caller="pycon-backend", - # jwt_secret=str(settings.SERVICE_TO_SERVICE_SECRET), - # ) - # client_execute = async_to_sync(client.execute) - # try: - # login_data = client_execute( - # LOGIN_QUERY, - # {"input": {"email": username, - # "password": password, "staffOnly": True}}, - # ).data - # except ServiceError as e: - # logger.exception("Failed to login in django admin", exc_info=e) - # return None - - # if login_data["login"] is None: - # # User not found / not active / or anything else - # return None - - # user_data = login_data["login"] - # user_model = get_user_model() - - # try: - # django_user = user_model.objects.get(email=user_data["email"]) - # django_user.first_name = user_data["fullname"] - # django_user.save() - # except user_model.DoesNotExist: - # django_user = user_model.objects.create( - # email=user_data["email"], - # username=user_data["email"], - # first_name=user_data["fullname"], - # is_active=True, - # is_staff=True, - # is_superuser=False, - # ) - # return django_user - - def get_user(self, user_id): - return None - # user_model = get_user_model() - - # try: - # return user_model.objects.get(pk=user_id) - # except user_model.DoesNotExist: - # return None diff --git a/cms/custom_auth/tests/__init__.py b/cms/custom_auth/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/custom_auth/tests/test_backend.py b/cms/custom_auth/tests/test_backend.py deleted file mode 100644 index 47b003d886..0000000000 --- a/cms/custom_auth/tests/test_backend.py +++ /dev/null @@ -1,117 +0,0 @@ -# import pytest -# import respx -# from django.conf import settings - -# from custom_auth.backend import UsersAuthBackend -# from django.contrib.auth import get_user_model - - -# pytestmark = pytest.mark.django_db - - -# def test_authenticate(): -# User = get_user_model() -# unrelated_user = User.objects.create( -# first_name="Hello", email="anotheremail@test.it" -# ) - -# backend = UsersAuthBackend() - -# with respx.mock as mock: -# mock.post(f"{settings.USERS_SERVICE_URL}/internal-api").respond( -# json={ -# "data": { -# "login": { -# "id": 1, -# "email": "marco@test.it", -# "fullname": "Test user", -# } -# } -# } -# ) - -# logged_user = backend.authenticate( -# request=None, username="marco@test.it", password="hello" -# ) - -# assert logged_user.id != unrelated_user.id -# assert logged_user.email == "marco@test.it" -# assert logged_user.username == "marco@test.it" -# assert logged_user.first_name == "Test user" -# assert logged_user.is_staff -# assert not logged_user.is_superuser - - -# def test_authenticate_when_wrong_username_or_password(): -# backend = UsersAuthBackend() - -# with respx.mock as mock: -# mock.post(f"{settings.USERS_SERVICE_URL}/internal-api").respond( -# json={"data": {"login": None}} -# ) - -# logged_user = backend.authenticate( -# request=None, username="marco@test.it", password="hello" -# ) - -# assert logged_user is None - - -# def test_authenticate_with_existing_user(): -# User = get_user_model() - -# existing_user = User.objects.create(first_name="Hello", email="marco@test.it") -# backend = UsersAuthBackend() - -# with respx.mock as mock: -# mock.post(f"{settings.USERS_SERVICE_URL}/internal-api").respond( -# json={ -# "data": { -# "login": { -# "id": 1, -# "email": "marco@test.it", -# "fullname": "Test user", -# } -# } -# } -# ) - -# logged_user = backend.authenticate( -# request=None, username="marco@test.it", password="hello" -# ) - -# assert logged_user.id == existing_user.id -# assert logged_user.first_name == "Test user" - - -# def test_authenticate_with_validation_error(): -# backend = UsersAuthBackend() - -# with respx.mock as mock: -# mock.post(f"{settings.USERS_SERVICE_URL}/internal-api").respond( -# json={"errors": [{"message": "Invalid data"}]} -# ) - -# logged_user = backend.authenticate( -# request=None, username="marco", password="hello" -# ) - -# assert logged_user is None - - -# def test_get_user(): -# User = get_user_model() - -# backend = UsersAuthBackend() -# user = User.objects.create(first_name="Hello", email="anotheremail@test.it") - -# assert backend.get_user(user.id).id == user.id - - -# def test_not_existent_user(): -# User = get_user_model() - -# backend = UsersAuthBackend() -# User.objects.create(id=1, first_name="Hello", email="anotheremail@test.it") - -# assert backend.get_user(5000) is None diff --git a/cms/gunicorn.conf.py b/cms/gunicorn.conf.py deleted file mode 100644 index 1007462210..0000000000 --- a/cms/gunicorn.conf.py +++ /dev/null @@ -1,4 +0,0 @@ -import multiprocessing - -bind = "0.0.0.0:8000" -workers = multiprocessing.cpu_count() * 2 + 1 diff --git a/cms/home/__init__.py b/cms/home/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/home/blocks/__init__.py b/cms/home/blocks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/home/blocks/home_intro_section.py b/cms/home/blocks/home_intro_section.py deleted file mode 100644 index 9ce3f786fb..0000000000 --- a/cms/home/blocks/home_intro_section.py +++ /dev/null @@ -1,10 +0,0 @@ -from wagtail import blocks - - -class HomeIntroSection(blocks.StructBlock): - pretitle = blocks.CharBlock(required=False) - title = blocks.CharBlock(required=False) - - class Meta: - label = "Homepage: Intro section" - icon = "crosshairs" diff --git a/cms/home/migrations/0001_initial.py b/cms/home/migrations/0001_initial.py deleted file mode 100644 index 266374fb1c..0000000000 --- a/cms/home/migrations/0001_initial.py +++ /dev/null @@ -1,52 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-04 13:58 - -from django.db import migrations, models -import django.db.models.deletion -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - initial = True - - dependencies = [ - ("wagtailcore", "0083_workflowcontenttype"), - ] - - operations = [ - migrations.CreateModel( - name="HomePage", - fields=[ - ( - "page_ptr", - models.OneToOneField( - auto_created=True, - on_delete=django.db.models.deletion.CASCADE, - parent_link=True, - primary_key=True, - serialize=False, - to="wagtailcore.page", - ), - ), - ( - "body", - wagtail.fields.StreamField( - [ - ( - "heading", - wagtail.blocks.CharBlock(form_classname="title"), - ), - ("paragraph", wagtail.blocks.RichTextBlock()), - ("image", wagtail.images.blocks.ImageChooserBlock()), - ], - use_json_field=True, - ), - ), - ], - options={ - "abstract": False, - }, - bases=("wagtailcore.page",), - ), - ] diff --git a/cms/home/migrations/__init__.py b/cms/home/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/home/models.py b/cms/home/models.py deleted file mode 100644 index 9039186269..0000000000 --- a/cms/home/models.py +++ /dev/null @@ -1,21 +0,0 @@ -from wagtail.models import Page -from wagtail.admin.panels import FieldPanel - -from wagtail.fields import StreamField -from wagtail import blocks -from wagtail.images.blocks import ImageChooserBlock - - -class HomePage(Page): - body = StreamField( - [ - ("heading", blocks.CharBlock(form_classname="title")), - ("paragraph", blocks.RichTextBlock()), - ("image", ImageChooserBlock()), - ], - use_json_field=True, - ) - - content_panels = Page.content_panels + [ - FieldPanel("body"), - ] diff --git a/cms/manage.py b/cms/manage.py deleted file mode 100755 index c00f3df2bc..0000000000 --- a/cms/manage.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings.prod") - - from django.core.management import execute_from_command_line - - execute_from_command_line(sys.argv) diff --git a/cms/news/__init__.py b/cms/news/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/news/admin.py b/cms/news/admin.py deleted file mode 100644 index 846f6b4061..0000000000 --- a/cms/news/admin.py +++ /dev/null @@ -1 +0,0 @@ -# Register your models here. diff --git a/cms/news/apps.py b/cms/news/apps.py deleted file mode 100644 index e50c454072..0000000000 --- a/cms/news/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class NewsConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "news" diff --git a/cms/news/blocks/__init__.py b/cms/news/blocks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/news/blocks/news_grid_section.py b/cms/news/blocks/news_grid_section.py deleted file mode 100644 index d04ae68d1d..0000000000 --- a/cms/news/blocks/news_grid_section.py +++ /dev/null @@ -1,7 +0,0 @@ -from wagtail import blocks - - -class NewsGridSection(blocks.StructBlock): - class Meta: - label = "News Grid Section" - icon = "crosshairs" diff --git a/cms/news/migrations/0001_news_articles.py b/cms/news/migrations/0001_news_articles.py deleted file mode 100644 index a7da4bf950..0000000000 --- a/cms/news/migrations/0001_news_articles.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 4.1.7 on 2023-04-10 14:22 - -from django.db import migrations, models -import django.db.models.deletion -import wagtail.fields - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('wagtailcore', '0083_workflowcontenttype'), - ] - - operations = [ - migrations.CreateModel( - name='NewsArticle', - fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('excerpt', models.CharField(max_length=255)), - ('body', wagtail.fields.RichTextField()), - ], - options={ - 'abstract': False, - }, - bases=('wagtailcore.page',), - ), - ] diff --git a/cms/news/migrations/0002_alter_newsarticle_excerpt.py b/cms/news/migrations/0002_alter_newsarticle_excerpt.py deleted file mode 100644 index bd5fda7e2d..0000000000 --- a/cms/news/migrations/0002_alter_newsarticle_excerpt.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.1.7 on 2023-05-13 19:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('news', '0001_news_articles'), - ] - - operations = [ - migrations.AlterField( - model_name='newsarticle', - name='excerpt', - field=models.TextField(max_length=255), - ), - ] diff --git a/cms/news/migrations/__init__.py b/cms/news/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/news/models.py b/cms/news/models.py deleted file mode 100644 index 8331b73af0..0000000000 --- a/cms/news/models.py +++ /dev/null @@ -1,15 +0,0 @@ -from wagtail.models import Page -from django.db import models -from wagtail.admin.panels import FieldPanel -from wagtail.fields import RichTextField - - -class NewsArticle(Page): - excerpt = models.TextField(max_length=255) - body = RichTextField() - - content_panels = Page.content_panels + [ - FieldPanel("excerpt"), - FieldPanel("body"), - FieldPanel("first_published_at"), - ] diff --git a/cms/news/tests/__init__.py b/cms/news/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/news/tests/factories.py b/cms/news/tests/factories.py deleted file mode 100644 index ee43abfe04..0000000000 --- a/cms/news/tests/factories.py +++ /dev/null @@ -1,20 +0,0 @@ -from pytest_factoryboy import register -from wagtail_factories import ( - PageFactory, -) -from news.models import NewsArticle -from wagtail.rich_text import RichText -import factory - - -@register -class NewsArticleFactory(PageFactory): - class Meta: - model = NewsArticle - - excerpt = "Test" - body = factory.LazyAttribute(lambda o: RichText(f"

{o.h2}

" f"

{o.p}

")) - - class Params: - h2 = factory.Faker("text", max_nb_chars=20) - p = factory.Faker("text", max_nb_chars=300) diff --git a/cms/page/__init__.py b/cms/page/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/page/apps.py b/cms/page/apps.py deleted file mode 100644 index 0e93c96e91..0000000000 --- a/cms/page/apps.py +++ /dev/null @@ -1,11 +0,0 @@ -from wagtail.signals import page_published -from django.apps import AppConfig - - -class PageConfig(AppConfig): - name = "page" - - def ready(self): - from . import signals - - page_published.connect(signals.revalidate_vercel_frontend) diff --git a/cms/page/blocks/__init__.py b/cms/page/blocks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/page/blocks/checkout_section.py b/cms/page/blocks/checkout_section.py deleted file mode 100644 index 8273e83afa..0000000000 --- a/cms/page/blocks/checkout_section.py +++ /dev/null @@ -1,16 +0,0 @@ -from wagtail import blocks - - -class CheckoutSection(blocks.StructBlock): - show_conference_tickets_products = blocks.BooleanBlock( - required=False, default=False - ) - show_social_events_products = blocks.BooleanBlock(required=False, default=False) - show_tours_products = blocks.BooleanBlock(required=False, default=False) - show_gadgets_products = blocks.BooleanBlock(required=False, default=False) - show_membership_products = blocks.BooleanBlock(required=False, default=False) - show_hotel_products = blocks.BooleanBlock(required=False, default=False) - - class Meta: - label = "Checkout Section" - icon = "crosshairs" diff --git a/cms/page/blocks/information_section.py b/cms/page/blocks/information_section.py deleted file mode 100644 index 8fb38b0530..0000000000 --- a/cms/page/blocks/information_section.py +++ /dev/null @@ -1,22 +0,0 @@ -from wagtail import blocks -from base.blocks.cta import CTA -from page.fields import BackgroundColorChoiceBlock, IllustrationChoiceBlock - - -class InformationSection(blocks.StructBlock): - title = blocks.CharBlock( - required=True, - ) - body = blocks.RichTextBlock(required=False) - illustration = IllustrationChoiceBlock( - required=False, - ) - background_color = BackgroundColorChoiceBlock(required=True) - countdown_to_datetime = blocks.DateTimeBlock(required=False) - countdown_to_deadline = blocks.CharBlock(required=False) - - cta = CTA() - - class Meta: - label = "Information Section" - icon = "crosshairs" diff --git a/cms/page/blocks/keynoters_section.py b/cms/page/blocks/keynoters_section.py deleted file mode 100644 index b56503a02b..0000000000 --- a/cms/page/blocks/keynoters_section.py +++ /dev/null @@ -1,11 +0,0 @@ -from base.blocks.cta import CTA -from wagtail import blocks - - -class KeynotersSection(blocks.StructBlock): - title = blocks.CharBlock(required=True) - cta = CTA() - - class Meta: - label = "Keynoters Section" - icon = "crosshairs" diff --git a/cms/page/blocks/live_streaming_section.py b/cms/page/blocks/live_streaming_section.py deleted file mode 100644 index e076b4bc7c..0000000000 --- a/cms/page/blocks/live_streaming_section.py +++ /dev/null @@ -1,7 +0,0 @@ -from wagtail import blocks - - -class LiveStreamingSection(blocks.StructBlock): - class Meta: - label = "Live Streaming Section" - icon = "crosshairs" diff --git a/cms/page/blocks/schedule_preview_section.py b/cms/page/blocks/schedule_preview_section.py deleted file mode 100644 index ae562054a3..0000000000 --- a/cms/page/blocks/schedule_preview_section.py +++ /dev/null @@ -1,12 +0,0 @@ -from base.blocks.cta import CTA -from wagtail import blocks - - -class SchedulePreviewSection(blocks.StructBlock): - title = blocks.CharBlock(required=True) - primary_cta = CTA(label="Primary CTA") - secondary_cta = CTA(label="Secondary CTA") - - class Meta: - label = "Schedule Preview Section" - icon = "crosshairs" diff --git a/cms/page/blocks/slider_cards_section.py b/cms/page/blocks/slider_cards_section.py deleted file mode 100644 index f226af9881..0000000000 --- a/cms/page/blocks/slider_cards_section.py +++ /dev/null @@ -1,48 +0,0 @@ -from wagtail import blocks -from base.blocks.cta import CTA - - -class SimpleTextCard(blocks.StructBlock): - title = blocks.CharBlock() - body = blocks.RichTextBlock() - cta = CTA() - - class Meta: - label = "Card: Simple Text" - icon = "doc-full" - - -class PriceCard(blocks.StructBlock): - title = blocks.CharBlock() - body = blocks.RichTextBlock() - price = blocks.CharBlock() - price_tier = blocks.CharBlock() - cta = CTA() - - class Meta: - label = "Card: Price" - icon = "doc-full" - - -class SliderCardsSection(blocks.StructBlock): - title = blocks.CharBlock( - required=False, - ) - spacing = blocks.ChoiceBlock( - default="xl", - choices=[ - ("xl", "Extra Large"), - ("3xl", "3 Extra Large"), - ], - ) - snake_background = blocks.BooleanBlock(required=False, default=False) - cards = blocks.StreamBlock( - [ - ("simple_text_card", SimpleTextCard()), - ("price_card", PriceCard()), - ] - ) - - class Meta: - label = "Slider Cards Section" - icon = "crosshairs" diff --git a/cms/page/blocks/socials_section.py b/cms/page/blocks/socials_section.py deleted file mode 100644 index 3269e850cb..0000000000 --- a/cms/page/blocks/socials_section.py +++ /dev/null @@ -1,10 +0,0 @@ -from wagtail import blocks - - -class SocialsSection(blocks.StructBlock): - label = blocks.CharBlock(required=True) - hashtag = blocks.CharBlock(required=True) - - class Meta: - label = "Socials Section" - icon = "crosshairs" diff --git a/cms/page/blocks/special_guest_section.py b/cms/page/blocks/special_guest_section.py deleted file mode 100644 index 471517cd3f..0000000000 --- a/cms/page/blocks/special_guest_section.py +++ /dev/null @@ -1,17 +0,0 @@ -from base.blocks.cta import CTA -from wagtail import blocks -from wagtail.images.blocks import ImageChooserBlock - - -class SpecialGuestSection(blocks.StructBlock): - title = blocks.CharBlock(required=True) - guest_name = blocks.CharBlock(required=True) - guest_photo = ImageChooserBlock(required=True) - guest_job_title = blocks.CharBlock(required=True) - event_date = blocks.DateBlock(required=True) - - cta = CTA() - - class Meta: - label = "Special Guest Section" - icon = "crosshairs" diff --git a/cms/page/blocks/sponsors_section.py b/cms/page/blocks/sponsors_section.py deleted file mode 100644 index f3a21aa5d6..0000000000 --- a/cms/page/blocks/sponsors_section.py +++ /dev/null @@ -1,17 +0,0 @@ -from base.blocks.cta import CTA -from wagtail import blocks - - -class SponsorsSection(blocks.StructBlock): - title = blocks.CharBlock(required=True) - body = blocks.CharBlock(required=True) - cta = CTA() - layout = blocks.ChoiceBlock( - required=False, - default="side-by-side", - choices=[("side-by-side", "Side-by-side"), ("vertical", "Vertical")], - ) - - class Meta: - label = "Sponsors Section" - icon = "crosshairs" diff --git a/cms/page/blocks/text_section.py b/cms/page/blocks/text_section.py deleted file mode 100644 index 953f7f1f61..0000000000 --- a/cms/page/blocks/text_section.py +++ /dev/null @@ -1,24 +0,0 @@ -from base.blocks.accordion import Accordion -from base.blocks.cta import CTA -from page.fields import IllustrationChoiceBlock -from wagtail import blocks - - -class TextSection(blocks.StructBlock): - title = blocks.CharBlock(required=False) - is_main_title = blocks.BooleanBlock(required=False, default=False) - subtitle = blocks.CharBlock(required=False) - body = blocks.RichTextBlock(required=False) - body_text_size = blocks.ChoiceBlock( - required=False, - default="text-1", - choices=[("text-1", "Text-1"), ("text-2", "Text-2")], - ) - illustration = IllustrationChoiceBlock( - required=False, - ) - accordions = blocks.ListBlock(Accordion) - cta = CTA() - - class Meta: - icon = "doc-full" diff --git a/cms/page/fields.py b/cms/page/fields.py deleted file mode 100644 index becb5bb733..0000000000 --- a/cms/page/fields.py +++ /dev/null @@ -1,59 +0,0 @@ -from wagtail import blocks - - -class BackgroundColorChoiceBlock(blocks.ChoiceBlock): - choices = [ - ("coral", "coral"), - ("caramel", "caramel"), - ("cream", "cream"), - ("yellow", "yellow"), - ("green", "green"), - ("purple", "purple"), - ("pink", "pink"), - ("blue", "blue"), - ("red", "red"), - ("success", "success"), - ("warning", "warning"), - ("neutral", "neutral"), - ("error", "error"), - ("black", "black"), - ("grey", "grey"), - ("white", "white"), - ("milk", "milk"), - ] - - class Meta: - icon = "crosshairs" - - -class IllustrationChoiceBlock(blocks.ChoiceBlock): - choices = [ - ("cathedral", "Cathedral"), - ("florence", "Florence"), - ("florence2", "Florence2"), - ("handWithSnakeInside", "Hand With Snake Inside"), - ("snake1", "Snake1"), - ("snake2", "Snake2"), - ("snake4", "Snake4"), - ("snake5", "Snake5"), - ("snakeBody", "Snake Body"), - ("snakeCouple", "Snake Couple"), - ("snakeDNA", "Snake D N A"), - ("snakeHead", "Snake Head"), - ("snakeInDragon", "Snake In Dragon"), - ("snakeInDragonInverted", "Snake In Dragon Inverted"), - ("snakeLetter", "Snake Letter"), - ("snakeLongNeck", "Snake Long Neck"), - ("snakePencil", "Snake Pencil"), - ("snakeTail", "Snake Tail"), - ("snakeWithBalloon", "Snake With Balloon"), - ("snakeWithContacts", "Snake With Contacts"), - ("snakesWithBanner", "Snakes With Banner"), - ("snakesWithCocktail", "Snakes With Cocktail"), - ("snakesWithDirections", "Snakes With Directions"), - ("snakesWithOutlines", "Snakes With Outlines"), - ("tripleSnakes", "Triple Snakes"), - ] - - class Meta: - icon = "image" diff --git a/cms/page/migrations/0001_initial.py b/cms/page/migrations/0001_initial.py deleted file mode 100644 index 93120376e4..0000000000 --- a/cms/page/migrations/0001_initial.py +++ /dev/null @@ -1,52 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-04 13:58 - -from django.db import migrations, models -import django.db.models.deletion -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - initial = True - - dependencies = [ - ("wagtailcore", "0083_workflowcontenttype"), - ] - - operations = [ - migrations.CreateModel( - name="GenericPage", - fields=[ - ( - "page_ptr", - models.OneToOneField( - auto_created=True, - on_delete=django.db.models.deletion.CASCADE, - parent_link=True, - primary_key=True, - serialize=False, - to="wagtailcore.page", - ), - ), - ( - "body", - wagtail.fields.StreamField( - [ - ( - "heading", - wagtail.blocks.CharBlock(form_classname="title"), - ), - ("paragraph", wagtail.blocks.RichTextBlock()), - ("image", wagtail.images.blocks.ImageChooserBlock()), - ], - use_json_field=True, - ), - ), - ], - options={ - "abstract": False, - }, - bases=("wagtailcore.page",), - ), - ] diff --git a/cms/page/migrations/0002_alter_genericpage_body.py b/cms/page/migrations/0002_alter_genericpage_body.py deleted file mode 100644 index 226dd3f5c4..0000000000 --- a/cms/page/migrations/0002_alter_genericpage_body.py +++ /dev/null @@ -1,74 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-19 21:43 - -from django.db import migrations -import base -import base.blocks -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - dependencies = [ - ("page", "0001_initial"), - ] - - operations = [ - migrations.AlterField( - model_name="genericpage", - name="body", - field=wagtail.fields.StreamField( - [ - ( - "text_section", - wagtail.blocks.StructBlock( - [ - ("title", wagtail.blocks.CharBlock(required=False)), - ("subtitle", wagtail.blocks.CharBlock(required=False)), - ("body", wagtail.blocks.RichTextBlock()), - ( - "illustration", - wagtail.blocks.ChoiceBlock( - choices=[ - ("snakeTail", "Snake Tail"), - ("snakeHead", "Snake Head"), - ("snakesWithSigns", "Snakes with Signs"), - ], - icon="image", - required=False, - ), - ), - ( - "accordions", - wagtail.blocks.ListBlock(base.blocks.accordion.Accordion), - ), - ] - ), - ), - ( - "map", - wagtail.blocks.StructBlock( - [ - ( - "longitude", - wagtail.blocks.DecimalBlock( - decimal_places=6, max_digits=9 - ), - ), - ( - "latitude", - wagtail.blocks.DecimalBlock( - decimal_places=6, max_digits=9 - ), - ), - ("zoom", wagtail.blocks.IntegerBlock(default=15)), - ("link", wagtail.blocks.URLBlock()), - ] - ), - ), - ("image", wagtail.images.blocks.ImageChooserBlock()), - ], - use_json_field=True, - ), - ), - ] diff --git a/cms/page/migrations/0003_update_blocks.py b/cms/page/migrations/0003_update_blocks.py deleted file mode 100644 index b5ef3dd893..0000000000 --- a/cms/page/migrations/0003_update_blocks.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-20 19:34 - -from django.db import migrations -import page.blocks -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks -import base.blocks - -class Migration(migrations.Migration): - - dependencies = [ - ('page', '0002_alter_genericpage_body'), - ] - - operations = [ - migrations.AlterField( - model_name='genericpage', - name='body', - field=wagtail.fields.StreamField([('text_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('is_main_title', wagtail.blocks.BooleanBlock(default=False, required=False)), ('subtitle', wagtail.blocks.CharBlock(required=False)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('snakeTail', 'Snake Tail'), ('snakeHead', 'Snake Head'), ('snakesWithSigns', 'Snakes with Signs')], icon='image', required=False)), ('accordions', wagtail.blocks.ListBlock(base.blocks.accordion.Accordion)), ('cta', wagtail.blocks.StructBlock([('text', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('map', wagtail.blocks.StructBlock([('longitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('latitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('zoom', wagtail.blocks.IntegerBlock(default=15)), ('link', wagtail.blocks.URLBlock())])), ('image', wagtail.images.blocks.ImageChooserBlock()), ('slider_cards_section', wagtail.blocks.StructBlock([('cards', wagtail.blocks.ListBlock(page.blocks.slider_cards_section.SimpleTextCard))]))], use_json_field=True), - ), - ] diff --git a/cms/page/migrations/0004_update_blocks.py b/cms/page/migrations/0004_update_blocks.py deleted file mode 100644 index 69323969ea..0000000000 --- a/cms/page/migrations/0004_update_blocks.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-20 19:48 - -from django.db import migrations -import base -import base.blocks -import page.blocks -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - - dependencies = [ - ('page', '0003_update_blocks'), - ] - - operations = [ - migrations.AlterField( - model_name='genericpage', - name='body', - field=wagtail.fields.StreamField([('text_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('is_main_title', wagtail.blocks.BooleanBlock(default=False, required=False)), ('subtitle', wagtail.blocks.CharBlock(required=False)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('snakeTail', 'Snake Tail'), ('snakeHead', 'Snake Head'), ('snakesWithSigns', 'Snakes with Signs')], icon='image', required=False)), ('accordions', wagtail.blocks.ListBlock(base.blocks.accordion.Accordion)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('map', wagtail.blocks.StructBlock([('longitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('latitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('zoom', wagtail.blocks.IntegerBlock(default=15)), ('link', wagtail.blocks.URLBlock())])), ('image', wagtail.images.blocks.ImageChooserBlock()), ('slider_cards_section', wagtail.blocks.StructBlock([('cards', wagtail.blocks.ListBlock(page.blocks.slider_cards_section.SimpleTextCard))]))], use_json_field=True), - ), - ] diff --git a/cms/page/migrations/0005_add_illustration_choices.py b/cms/page/migrations/0005_add_illustration_choices.py deleted file mode 100644 index 771d6d394c..0000000000 --- a/cms/page/migrations/0005_add_illustration_choices.py +++ /dev/null @@ -1,153 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-20 21:26 - -from django.db import migrations -import page.blocks -import base -import base.blocks.accordion -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - dependencies = [ - ("page", "0004_update_blocks"), - ] - - operations = [ - migrations.AlterField( - model_name="genericpage", - name="body", - field=wagtail.fields.StreamField( - [ - ( - "text_section", - wagtail.blocks.StructBlock( - [ - ("title", wagtail.blocks.CharBlock(required=False)), - ( - "is_main_title", - wagtail.blocks.BooleanBlock( - default=False, required=False - ), - ), - ("subtitle", wagtail.blocks.CharBlock(required=False)), - ("body", wagtail.blocks.RichTextBlock(required=False)), - ( - "illustration", - wagtail.blocks.ChoiceBlock( - choices=[ - ("cathedral", "Cathedral"), - ("florence", "Florence"), - ("florence2", "Florence2"), - ( - "handWithSnakeInside", - "Hand With Snake Inside", - ), - ("snake1", "Snake1"), - ("snake2", "Snake2"), - ("snake4", "Snake4"), - ("snake5", "Snake5"), - ("snakeBody", "Snake Body"), - ("snakeCouple", "Snake Couple"), - ("snakeDNA", "Snake D N A"), - ("snakeHead", "Snake Head"), - ("snakeInDragon", "Snake In Dragon"), - ( - "snakeInDragonInverted", - "Snake In Dragon Inverted", - ), - ("snakeLetter", "Snake Letter"), - ("snakeLongNeck", "Snake Long Neck"), - ("snakePencil", "Snake Pencil"), - ("snakeTail", "Snake Tail"), - ("snakeWithBalloon", "Snake With Balloon"), - ( - "snakeWithContacts", - "Snake With Contacts", - ), - ("snakesWithBanner", "Snakes With Banner"), - ( - "snakesWithCocktail", - "Snakes With Cocktail", - ), - ( - "snakesWithDirections", - "Snakes With Directions", - ), - ( - "snakesWithOutlines", - "Snakes With Outlines", - ), - ("tripleSnakes", "Triple Snakes"), - ], - icon="image", - required=False, - ), - ), - ( - "accordions", - wagtail.blocks.ListBlock(base.blocks.accordion.Accordion), - ), - ( - "cta", - wagtail.blocks.StructBlock( - [ - ( - "label", - wagtail.blocks.CharBlock( - required=False - ), - ), - ( - "link", - wagtail.blocks.CharBlock( - required=False - ), - ), - ] - ), - ), - ] - ), - ), - ( - "map", - wagtail.blocks.StructBlock( - [ - ( - "longitude", - wagtail.blocks.DecimalBlock( - decimal_places=6, max_digits=9 - ), - ), - ( - "latitude", - wagtail.blocks.DecimalBlock( - decimal_places=6, max_digits=9 - ), - ), - ("zoom", wagtail.blocks.IntegerBlock(default=15)), - ("link", wagtail.blocks.URLBlock()), - ] - ), - ), - ("image", wagtail.images.blocks.ImageChooserBlock()), - ( - "slider_cards_section", - wagtail.blocks.StructBlock( - [ - ( - "cards", - wagtail.blocks.ListBlock( - page.blocks.slider_cards_section.SimpleTextCard - ), - ) - ] - ), - ), - ], - use_json_field=True, - ), - ), - ] diff --git a/cms/page/migrations/0006_refactor_structure.py b/cms/page/migrations/0006_refactor_structure.py deleted file mode 100644 index 42724b453d..0000000000 --- a/cms/page/migrations/0006_refactor_structure.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-24 01:55 - -import base.blocks.accordion -from django.db import migrations -import page.blocks.slider_cards_section -import wagtail.blocks -import wagtail.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('page', '0005_add_illustration_choices'), - ] - - operations = [ - migrations.AlterField( - model_name='genericpage', - name='body', - field=wagtail.fields.StreamField([('text_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('is_main_title', wagtail.blocks.BooleanBlock(default=False, required=False)), ('subtitle', wagtail.blocks.CharBlock(required=False)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], icon='image', required=False)), ('accordions', wagtail.blocks.ListBlock(base.blocks.accordion.Accordion)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('map', wagtail.blocks.StructBlock([('longitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('latitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('zoom', wagtail.blocks.IntegerBlock(default=15)), ('link', wagtail.blocks.URLBlock())])), ('slider_cards_section', wagtail.blocks.StructBlock([('cards', wagtail.blocks.ListBlock(page.blocks.slider_cards_section.SimpleTextCard))]))], use_json_field=True), - ), - ] diff --git a/cms/page/migrations/0007_add_body_size_in_text_section.py b/cms/page/migrations/0007_add_body_size_in_text_section.py deleted file mode 100644 index 9926373c99..0000000000 --- a/cms/page/migrations/0007_add_body_size_in_text_section.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-29 16:00 - -import base.blocks.accordion -from django.db import migrations -import page.blocks.slider_cards_section -import wagtail.blocks -import wagtail.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('page', '0006_refactor_structure'), - ] - - operations = [ - migrations.AlterField( - model_name='genericpage', - name='body', - field=wagtail.fields.StreamField([('text_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('is_main_title', wagtail.blocks.BooleanBlock(default=False, required=False)), ('subtitle', wagtail.blocks.CharBlock(required=False)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('body_text_size', wagtail.blocks.ChoiceBlock(choices=[('text-1', 'Text-1'), ('text-2', 'Text-2')], required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], icon='image', required=False)), ('accordions', wagtail.blocks.ListBlock(base.blocks.accordion.Accordion)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('map', wagtail.blocks.StructBlock([('longitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('latitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('zoom', wagtail.blocks.IntegerBlock(default=15)), ('link', wagtail.blocks.URLBlock())])), ('slider_cards_section', wagtail.blocks.StructBlock([('cards', wagtail.blocks.ListBlock(page.blocks.slider_cards_section.SimpleTextCard))]))], use_json_field=True), - ), - ] diff --git a/cms/page/migrations/0008_homepage_blocks.py b/cms/page/migrations/0008_homepage_blocks.py deleted file mode 100644 index d29a0769a6..0000000000 --- a/cms/page/migrations/0008_homepage_blocks.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.1.7 on 2023-04-04 20:34 - -import base.blocks.accordion -from django.db import migrations -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - - dependencies = [ - ('page', '0007_add_body_size_in_text_section'), - ] - - operations = [ - migrations.AlterField( - model_name='genericpage', - name='body', - field=wagtail.fields.StreamField([('text_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('is_main_title', wagtail.blocks.BooleanBlock(default=False, required=False)), ('subtitle', wagtail.blocks.CharBlock(required=False)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('body_text_size', wagtail.blocks.ChoiceBlock(choices=[('text-1', 'Text-1'), ('text-2', 'Text-2')], required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], icon='image', required=False)), ('accordions', wagtail.blocks.ListBlock(base.blocks.accordion.Accordion)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('map', wagtail.blocks.StructBlock([('longitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('latitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('zoom', wagtail.blocks.IntegerBlock(default=15)), ('link', wagtail.blocks.URLBlock())])), ('slider_cards_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('spacing', wagtail.blocks.ChoiceBlock(choices=[('xl', 'Extra Large'), ('3xl', '3 Extra Large')])), ('snake_background', wagtail.blocks.BooleanBlock(default=False, required=False)), ('cards', wagtail.blocks.StreamBlock([('simple_text_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('price_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('price', wagtail.blocks.CharBlock()), ('price_tier', wagtail.blocks.CharBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))]))]))])), ('sponsors_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('body', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))])), ('layout', wagtail.blocks.ChoiceBlock(choices=[('side-by-side', 'Side-by-side'), ('vertical', 'Vertical')], required=False))])), ('home_intro_section', wagtail.blocks.StructBlock([('pretitle', wagtail.blocks.CharBlock(required=False)), ('title', wagtail.blocks.CharBlock(required=False))])), ('keynoters_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('schedule_preview_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('primary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Primary CTA')), ('secondary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Secondary CTA'))])), ('socials_section', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=True)), ('hashtag', wagtail.blocks.CharBlock(required=True))])), ('special_guest_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('guest_name', wagtail.blocks.CharBlock(required=True)), ('guest_photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('guest_job_title', wagtail.blocks.CharBlock(required=True)), ('event_date', wagtail.blocks.DateBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))]))], use_json_field=True), - ), - ] diff --git a/cms/page/migrations/0009_vercel_frontend_settings.py b/cms/page/migrations/0009_vercel_frontend_settings.py deleted file mode 100644 index cee91f8df1..0000000000 --- a/cms/page/migrations/0009_vercel_frontend_settings.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.1.7 on 2023-04-10 11:40 - -import base.blocks.accordion -from django.db import migrations -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - - dependencies = [ - ('page', '0008_homepage_blocks'), - ] - - operations = [ - migrations.AlterField( - model_name='genericpage', - name='body', - field=wagtail.fields.StreamField([('text_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('is_main_title', wagtail.blocks.BooleanBlock(default=False, required=False)), ('subtitle', wagtail.blocks.CharBlock(required=False)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('body_text_size', wagtail.blocks.ChoiceBlock(choices=[('text-1', 'Text-1'), ('text-2', 'Text-2')], required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], required=False)), ('accordions', wagtail.blocks.ListBlock(base.blocks.accordion.Accordion)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('map', wagtail.blocks.StructBlock([('longitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('latitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('zoom', wagtail.blocks.IntegerBlock(default=15)), ('link', wagtail.blocks.URLBlock())])), ('slider_cards_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('spacing', wagtail.blocks.ChoiceBlock(choices=[('xl', 'Extra Large'), ('3xl', '3 Extra Large')])), ('snake_background', wagtail.blocks.BooleanBlock(default=False, required=False)), ('cards', wagtail.blocks.StreamBlock([('simple_text_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('price_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('price', wagtail.blocks.CharBlock()), ('price_tier', wagtail.blocks.CharBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))]))]))])), ('sponsors_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('body', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))])), ('layout', wagtail.blocks.ChoiceBlock(choices=[('side-by-side', 'Side-by-side'), ('vertical', 'Vertical')], required=False))])), ('home_intro_section', wagtail.blocks.StructBlock([('pretitle', wagtail.blocks.CharBlock(required=False)), ('title', wagtail.blocks.CharBlock(required=False))])), ('keynoters_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('schedule_preview_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('primary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Primary CTA')), ('secondary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Secondary CTA'))])), ('socials_section', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=True)), ('hashtag', wagtail.blocks.CharBlock(required=True))])), ('special_guest_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('guest_name', wagtail.blocks.CharBlock(required=True)), ('guest_photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('guest_job_title', wagtail.blocks.CharBlock(required=True)), ('event_date', wagtail.blocks.DateBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('information_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], required=False)), ('background_color', wagtail.blocks.ChoiceBlock(choices=[('coral', 'coral'), ('caramel', 'caramel'), ('cream', 'cream'), ('yellow', 'yellow'), ('green', 'green'), ('purple', 'purple'), ('pink', 'pink'), ('blue', 'blue'), ('red', 'red'), ('success', 'success'), ('warning', 'warning'), ('neutral', 'neutral'), ('error', 'error'), ('black', 'black'), ('grey', 'grey'), ('white', 'white'), ('milk', 'milk')])), ('countdown_to_datetime', wagtail.blocks.DateTimeBlock(required=False)), ('countdown_to_deadline', wagtail.blocks.CharBlock(required=False)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))]))], use_json_field=True), - ), - ] diff --git a/cms/page/migrations/0010_news_articles.py b/cms/page/migrations/0010_news_articles.py deleted file mode 100644 index 32263794bb..0000000000 --- a/cms/page/migrations/0010_news_articles.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.1.7 on 2023-04-10 14:22 - -import base.blocks.accordion -from django.db import migrations -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - - dependencies = [ - ('page', '0009_vercel_frontend_settings'), - ] - - operations = [ - migrations.AlterField( - model_name='genericpage', - name='body', - field=wagtail.fields.StreamField([('text_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('is_main_title', wagtail.blocks.BooleanBlock(default=False, required=False)), ('subtitle', wagtail.blocks.CharBlock(required=False)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('body_text_size', wagtail.blocks.ChoiceBlock(choices=[('text-1', 'Text-1'), ('text-2', 'Text-2')], required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], required=False)), ('accordions', wagtail.blocks.ListBlock(base.blocks.accordion.Accordion)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('map', wagtail.blocks.StructBlock([('longitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('latitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('zoom', wagtail.blocks.IntegerBlock(default=15)), ('link', wagtail.blocks.URLBlock())])), ('slider_cards_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('spacing', wagtail.blocks.ChoiceBlock(choices=[('xl', 'Extra Large'), ('3xl', '3 Extra Large')])), ('snake_background', wagtail.blocks.BooleanBlock(default=False, required=False)), ('cards', wagtail.blocks.StreamBlock([('simple_text_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('price_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('price', wagtail.blocks.CharBlock()), ('price_tier', wagtail.blocks.CharBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))]))]))])), ('sponsors_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('body', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))])), ('layout', wagtail.blocks.ChoiceBlock(choices=[('side-by-side', 'Side-by-side'), ('vertical', 'Vertical')], required=False))])), ('home_intro_section', wagtail.blocks.StructBlock([('pretitle', wagtail.blocks.CharBlock(required=False)), ('title', wagtail.blocks.CharBlock(required=False))])), ('keynoters_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('schedule_preview_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('primary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Primary CTA')), ('secondary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Secondary CTA'))])), ('socials_section', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=True)), ('hashtag', wagtail.blocks.CharBlock(required=True))])), ('special_guest_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('guest_name', wagtail.blocks.CharBlock(required=True)), ('guest_photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('guest_job_title', wagtail.blocks.CharBlock(required=True)), ('event_date', wagtail.blocks.DateBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('information_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], required=False)), ('background_color', wagtail.blocks.ChoiceBlock(choices=[('coral', 'coral'), ('caramel', 'caramel'), ('cream', 'cream'), ('yellow', 'yellow'), ('green', 'green'), ('purple', 'purple'), ('pink', 'pink'), ('blue', 'blue'), ('red', 'red'), ('success', 'success'), ('warning', 'warning'), ('neutral', 'neutral'), ('error', 'error'), ('black', 'black'), ('grey', 'grey'), ('white', 'white'), ('milk', 'milk')])), ('countdown_to_datetime', wagtail.blocks.DateTimeBlock(required=False)), ('countdown_to_deadline', wagtail.blocks.CharBlock(required=False)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('news_grid_section', wagtail.blocks.StructBlock([]))], use_json_field=True), - ), - ] diff --git a/cms/page/migrations/0011_alter_genericpage_body.py b/cms/page/migrations/0011_alter_genericpage_body.py deleted file mode 100644 index 4c6c1db657..0000000000 --- a/cms/page/migrations/0011_alter_genericpage_body.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.1.7 on 2023-05-13 19:15 - -import base.blocks.accordion -from django.db import migrations -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - - dependencies = [ - ('page', '0010_news_articles'), - ] - - operations = [ - migrations.AlterField( - model_name='genericpage', - name='body', - field=wagtail.fields.StreamField([('text_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('is_main_title', wagtail.blocks.BooleanBlock(default=False, required=False)), ('subtitle', wagtail.blocks.CharBlock(required=False)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('body_text_size', wagtail.blocks.ChoiceBlock(choices=[('text-1', 'Text-1'), ('text-2', 'Text-2')], required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], required=False)), ('accordions', wagtail.blocks.ListBlock(base.blocks.accordion.Accordion)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('map', wagtail.blocks.StructBlock([('longitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('latitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('zoom', wagtail.blocks.IntegerBlock(default=15)), ('link', wagtail.blocks.URLBlock())])), ('slider_cards_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('spacing', wagtail.blocks.ChoiceBlock(choices=[('xl', 'Extra Large'), ('3xl', '3 Extra Large')])), ('snake_background', wagtail.blocks.BooleanBlock(default=False, required=False)), ('cards', wagtail.blocks.StreamBlock([('simple_text_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('price_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('price', wagtail.blocks.CharBlock()), ('price_tier', wagtail.blocks.CharBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))]))]))])), ('sponsors_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('body', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))])), ('layout', wagtail.blocks.ChoiceBlock(choices=[('side-by-side', 'Side-by-side'), ('vertical', 'Vertical')], required=False))])), ('home_intro_section', wagtail.blocks.StructBlock([('pretitle', wagtail.blocks.CharBlock(required=False)), ('title', wagtail.blocks.CharBlock(required=False))])), ('keynoters_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('schedule_preview_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('primary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Primary CTA')), ('secondary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Secondary CTA'))])), ('socials_section', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=True)), ('hashtag', wagtail.blocks.CharBlock(required=True))])), ('special_guest_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('guest_name', wagtail.blocks.CharBlock(required=True)), ('guest_photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('guest_job_title', wagtail.blocks.CharBlock(required=True)), ('event_date', wagtail.blocks.DateBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('information_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], required=False)), ('background_color', wagtail.blocks.ChoiceBlock(choices=[('coral', 'coral'), ('caramel', 'caramel'), ('cream', 'cream'), ('yellow', 'yellow'), ('green', 'green'), ('purple', 'purple'), ('pink', 'pink'), ('blue', 'blue'), ('red', 'red'), ('success', 'success'), ('warning', 'warning'), ('neutral', 'neutral'), ('error', 'error'), ('black', 'black'), ('grey', 'grey'), ('white', 'white'), ('milk', 'milk')])), ('countdown_to_datetime', wagtail.blocks.DateTimeBlock(required=False)), ('countdown_to_deadline', wagtail.blocks.CharBlock(required=False)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('news_grid_section', wagtail.blocks.StructBlock([])), ('checkout_section', wagtail.blocks.StructBlock([('show_conference_tickets_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_social_events_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_tours_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_gadgets_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_membership_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_hotel_products', wagtail.blocks.BooleanBlock(default=False, required=False))]))], use_json_field=True), - ), - ] diff --git a/cms/page/migrations/0012_live_streaming_section.py b/cms/page/migrations/0012_live_streaming_section.py deleted file mode 100644 index 0207d65fb0..0000000000 --- a/cms/page/migrations/0012_live_streaming_section.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.1.7 on 2023-05-14 20:00 - -import base.blocks.accordion -from django.db import migrations -import wagtail.blocks -import wagtail.fields -import wagtail.images.blocks - - -class Migration(migrations.Migration): - - dependencies = [ - ('page', '0011_alter_genericpage_body'), - ] - - operations = [ - migrations.AlterField( - model_name='genericpage', - name='body', - field=wagtail.fields.StreamField([('text_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('is_main_title', wagtail.blocks.BooleanBlock(default=False, required=False)), ('subtitle', wagtail.blocks.CharBlock(required=False)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('body_text_size', wagtail.blocks.ChoiceBlock(choices=[('text-1', 'Text-1'), ('text-2', 'Text-2')], required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], required=False)), ('accordions', wagtail.blocks.ListBlock(base.blocks.accordion.Accordion)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('map', wagtail.blocks.StructBlock([('longitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('latitude', wagtail.blocks.DecimalBlock(decimal_places=6, max_digits=9)), ('zoom', wagtail.blocks.IntegerBlock(default=15)), ('link', wagtail.blocks.URLBlock())])), ('slider_cards_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=False)), ('spacing', wagtail.blocks.ChoiceBlock(choices=[('xl', 'Extra Large'), ('3xl', '3 Extra Large')])), ('snake_background', wagtail.blocks.BooleanBlock(default=False, required=False)), ('cards', wagtail.blocks.StreamBlock([('simple_text_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('price_card', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('body', wagtail.blocks.RichTextBlock()), ('price', wagtail.blocks.CharBlock()), ('price_tier', wagtail.blocks.CharBlock()), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))]))]))])), ('sponsors_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('body', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))])), ('layout', wagtail.blocks.ChoiceBlock(choices=[('side-by-side', 'Side-by-side'), ('vertical', 'Vertical')], required=False))])), ('home_intro_section', wagtail.blocks.StructBlock([('pretitle', wagtail.blocks.CharBlock(required=False)), ('title', wagtail.blocks.CharBlock(required=False))])), ('keynoters_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('schedule_preview_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('primary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Primary CTA')), ('secondary_cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))], label='Secondary CTA'))])), ('socials_section', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=True)), ('hashtag', wagtail.blocks.CharBlock(required=True))])), ('special_guest_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('guest_name', wagtail.blocks.CharBlock(required=True)), ('guest_photo', wagtail.images.blocks.ImageChooserBlock(required=True)), ('guest_job_title', wagtail.blocks.CharBlock(required=True)), ('event_date', wagtail.blocks.DateBlock(required=True)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('information_section', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(required=True)), ('body', wagtail.blocks.RichTextBlock(required=False)), ('illustration', wagtail.blocks.ChoiceBlock(choices=[('cathedral', 'Cathedral'), ('florence', 'Florence'), ('florence2', 'Florence2'), ('handWithSnakeInside', 'Hand With Snake Inside'), ('snake1', 'Snake1'), ('snake2', 'Snake2'), ('snake4', 'Snake4'), ('snake5', 'Snake5'), ('snakeBody', 'Snake Body'), ('snakeCouple', 'Snake Couple'), ('snakeDNA', 'Snake D N A'), ('snakeHead', 'Snake Head'), ('snakeInDragon', 'Snake In Dragon'), ('snakeInDragonInverted', 'Snake In Dragon Inverted'), ('snakeLetter', 'Snake Letter'), ('snakeLongNeck', 'Snake Long Neck'), ('snakePencil', 'Snake Pencil'), ('snakeTail', 'Snake Tail'), ('snakeWithBalloon', 'Snake With Balloon'), ('snakeWithContacts', 'Snake With Contacts'), ('snakesWithBanner', 'Snakes With Banner'), ('snakesWithCocktail', 'Snakes With Cocktail'), ('snakesWithDirections', 'Snakes With Directions'), ('snakesWithOutlines', 'Snakes With Outlines'), ('tripleSnakes', 'Triple Snakes')], required=False)), ('background_color', wagtail.blocks.ChoiceBlock(choices=[('coral', 'coral'), ('caramel', 'caramel'), ('cream', 'cream'), ('yellow', 'yellow'), ('green', 'green'), ('purple', 'purple'), ('pink', 'pink'), ('blue', 'blue'), ('red', 'red'), ('success', 'success'), ('warning', 'warning'), ('neutral', 'neutral'), ('error', 'error'), ('black', 'black'), ('grey', 'grey'), ('white', 'white'), ('milk', 'milk')])), ('countdown_to_datetime', wagtail.blocks.DateTimeBlock(required=False)), ('countdown_to_deadline', wagtail.blocks.CharBlock(required=False)), ('cta', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(required=False)), ('link', wagtail.blocks.CharBlock(required=False))]))])), ('news_grid_section', wagtail.blocks.StructBlock([])), ('checkout_section', wagtail.blocks.StructBlock([('show_conference_tickets_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_social_events_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_tours_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_gadgets_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_membership_products', wagtail.blocks.BooleanBlock(default=False, required=False)), ('show_hotel_products', wagtail.blocks.BooleanBlock(default=False, required=False))])), ('live_streaming_section', wagtail.blocks.StructBlock([]))], use_json_field=True), - ), - ] diff --git a/cms/page/migrations/__init__.py b/cms/page/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/page/models.py b/cms/page/models.py deleted file mode 100644 index c7d201d563..0000000000 --- a/cms/page/models.py +++ /dev/null @@ -1,47 +0,0 @@ -from page.blocks.sponsors_section import SponsorsSection -from home.blocks.home_intro_section import HomeIntroSection -from page.blocks.keynoters_section import KeynotersSection -from page.blocks.schedule_preview_section import SchedulePreviewSection -from page.blocks.socials_section import SocialsSection -from page.blocks.special_guest_section import SpecialGuestSection -from page.blocks.information_section import InformationSection -from news.blocks.news_grid_section import NewsGridSection -from page.blocks.live_streaming_section import LiveStreamingSection -from wagtail.models import Page -from wagtail.admin.panels import FieldPanel - -from wagtail.fields import StreamField - -from page.blocks.text_section import TextSection -from page.blocks.slider_cards_section import SliderCardsSection -from page.blocks.checkout_section import CheckoutSection -from base.blocks.map import Map -from wagtail import blocks - - -class BodyBlock(blocks.StreamBlock): - text_section = TextSection() - map = Map() - slider_cards_section = SliderCardsSection() - sponsors_section = SponsorsSection() - - home_intro_section = HomeIntroSection() - keynoters_section = KeynotersSection() - schedule_preview_section = SchedulePreviewSection() - socials_section = SocialsSection() - special_guest_section = SpecialGuestSection() - information_section = InformationSection() - news_grid_section = NewsGridSection() - checkout_section = CheckoutSection() - live_streaming_section = LiveStreamingSection() - - -class GenericPage(Page): - body = StreamField( - BodyBlock(), - use_json_field=True, - ) - - content_panels = Page.content_panels + [ - FieldPanel("body"), - ] diff --git a/cms/page/signals.py b/cms/page/signals.py deleted file mode 100644 index 2f758bd40a..0000000000 --- a/cms/page/signals.py +++ /dev/null @@ -1,66 +0,0 @@ -import httpx -import logging - -from sites.models import VercelFrontendSettings - -logger = logging.getLogger(__name__) - - -def revalidate_vercel_frontend(sender, **kwargs): - instance = kwargs["instance"] - - site = kwargs["instance"].get_site() - site_name = site.site_name - hostname = site.hostname - settings = VercelFrontendSettings.for_site(site) - - if not settings: - # not configured for this site - return - - url = settings.revalidate_url - secret = settings.revalidate_secret - - if not url or not secret: - # not configured for this site - return - - language_code = instance.locale.language_code - - if language_code != "en": - # we need to get the original slug - # as we use the english slugs for the frontend - english_page = ( - instance.get_translations(inclusive=True) - .filter(locale__language_code="en") - .first() - ) - - slug = english_page.slug - _, _, page_path = english_page.get_url_parts() - else: - slug = instance.slug - _, _, page_path = instance.get_url_parts() - - page_path = page_path[:-1] - - if slug == hostname: - path = f"/{language_code}" - else: - path = f"/{language_code}{page_path}" - - try: - response = httpx.post( - url, - timeout=None, - json={ - "secret": secret, - "path": path, - }, - ) - response.raise_for_status() - except httpx.HTTPError as e: - logger.error(f"Error while revalidating {path} on {site_name}: {e}") - return - - logger.info(f"Revalidated {path} on {site_name}") diff --git a/cms/page/tests/__init__.py b/cms/page/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/page/tests/test_signals.py b/cms/page/tests/test_signals.py deleted file mode 100644 index 91a8ccc0e4..0000000000 --- a/cms/page/tests/test_signals.py +++ /dev/null @@ -1,124 +0,0 @@ -import json -import pytest -from page.signals import revalidate_vercel_frontend - - -pytestmark = pytest.mark.django_db - - -def test_revalidate_vercel_frontend_disabled_if_not_configured( - page_factory, site_factory, respx_mock -): - mock_call = respx_mock.post("https://test.com").respond(status_code=200) - - site = site_factory() - page = page_factory() - site.root_page = page - site.save() - revalidate_vercel_frontend("test_revalidate_vercel_frontend", instance=page) - - assert not mock_call.called - - -def test_revalidate_vercel_frontend( - page_factory, site_factory, respx_mock, vercel_frontend_settings_factory -): - parent = page_factory() - page = page_factory(slug="test-page123") - page.set_url_path(parent) - - site = site_factory(root_page=parent) - - settings = vercel_frontend_settings_factory( - revalidate_url="https://test.com", revalidate_secret="test", site=site - ) - mock_call = respx_mock.post(settings.revalidate_url).respond(status_code=200) - - revalidate_vercel_frontend("test_revalidate_vercel_frontend", instance=page) - - assert mock_call.called - - body = json.loads(mock_call.calls[0].request.content) - assert body["secret"] == "test" - assert body["path"] == "/en/test-page123" - - -def test_revalidate_vercel_frontend_special_case_for_homepage( - page_factory, site_factory, respx_mock, vercel_frontend_settings_factory -): - parent = page_factory() - page = page_factory(slug="homepage") - page.set_url_path(parent) - - site = site_factory(root_page=parent) - - settings = vercel_frontend_settings_factory( - revalidate_url="https://test.com", revalidate_secret="test", site=site - ) - mock_call = respx_mock.post(settings.revalidate_url).respond(status_code=200) - - revalidate_vercel_frontend("test_revalidate_vercel_frontend", instance=page) - - assert mock_call.called - - body = json.loads(mock_call.calls[0].request.content) - assert body["secret"] == "test" - assert body["path"] == "/en" - - -def test_revalidate_vercel_frontend_for_different_language( - page_factory, site_factory, respx_mock, vercel_frontend_settings_factory, locale -): - parent = page_factory() - site = site_factory(hostname="pycon", root_page=parent) - - page = page_factory(slug="test123", locale=locale("en"), parent=parent) - page.set_url_path(parent) - - italian_page = page.copy_for_translation(locale=locale("it")) - italian_page.slug = "something-else" - italian_page.save() - italian_page.set_url_path(parent) - - settings = vercel_frontend_settings_factory( - revalidate_url="https://test.com", revalidate_secret="test", site=site - ) - mock_call = respx_mock.post(settings.revalidate_url).respond(status_code=200) - - revalidate_vercel_frontend("test_revalidate_vercel_frontend", instance=italian_page) - - assert mock_call.called - - body = json.loads(mock_call.calls[0].request.content) - assert body["secret"] == "test" - assert body["path"] == "/it/test123" - - -def test_revalidate_vercel_frontend_when_vercel_is_down_doesnt_crash( - caplog, - page_factory, - site_factory, - respx_mock, - vercel_frontend_settings_factory, - locale, -): - parent = page_factory() - - page = page_factory(slug="test123", locale=locale("en"), parent=parent) - site = site_factory(hostname="pycon", root_page=page) - - italian_page = page.copy_for_translation(locale=locale("it")) - italian_page.slug = "something-else" - italian_page.save() - - settings = vercel_frontend_settings_factory( - revalidate_url="https://test.com", revalidate_secret="test", site=site - ) - mock_call = respx_mock.post(settings.revalidate_url).respond(status_code=500) - - revalidate_vercel_frontend("test_revalidate_vercel_frontend", instance=italian_page) - - assert mock_call.called - - json.loads(mock_call.calls[0].request.content) - assert "Error while revalidating" in caplog.records[0].message diff --git a/cms/pdm.lock b/cms/pdm.lock deleted file mode 100644 index e3494e185b..0000000000 --- a/cms/pdm.lock +++ /dev/null @@ -1,1242 +0,0 @@ -# This file is @generated by PDM. -# It is not intended for manual editing. - -[metadata] -groups = ["default", "dev"] -cross_platform = true -static_urls = false -lock_version = "4.3" -content_hash = "sha256:7e23524ec87eaa40de219ad94c8203855927d946dd88183ef7ea3e34d427a014" - -[[package]] -name = "annotated-types" -version = "0.5.0" -requires_python = ">=3.7" -summary = "Reusable constraint types to use with typing.Annotated" -files = [ - {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"}, - {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"}, -] - -[[package]] -name = "anyascii" -version = "0.3.2" -requires_python = ">=3.3" -summary = "Unicode to ASCII transliteration" -files = [ - {file = "anyascii-0.3.2-py3-none-any.whl", hash = "sha256:3b3beef6fc43d9036d3b0529050b0c48bfad8bc960e9e562d7223cfb94fe45d4"}, - {file = "anyascii-0.3.2.tar.gz", hash = "sha256:9d5d32ef844fe225b8bc7cba7f950534fae4da27a9bf3a6bea2cb0ea46ce4730"}, -] - -[[package]] -name = "anyio" -version = "4.0.0" -requires_python = ">=3.8" -summary = "High level compatibility layer for multiple asynchronous event loop implementations" -dependencies = [ - "idna>=2.8", - "sniffio>=1.1", -] -files = [ - {file = "anyio-4.0.0-py3-none-any.whl", hash = "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f"}, - {file = "anyio-4.0.0.tar.gz", hash = "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"}, -] - -[[package]] -name = "asgiref" -version = "3.7.2" -requires_python = ">=3.7" -summary = "ASGI specs, helper code, and adapters" -files = [ - {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, - {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, -] - -[[package]] -name = "azure-core" -version = "1.29.4" -requires_python = ">=3.7" -summary = "Microsoft Azure Core Library for Python" -dependencies = [ - "requests>=2.18.4", - "six>=1.11.0", - "typing-extensions>=4.6.0", -] -files = [ - {file = "azure-core-1.29.4.tar.gz", hash = "sha256:500b3aa9bf2e90c5ccc88bb105d056114ca0ce7d0ce73afb8bc4d714b2fc7568"}, - {file = "azure_core-1.29.4-py3-none-any.whl", hash = "sha256:b03261bcba22c0b9290faf9999cedd23e849ed2577feee90515694cea6bc74bf"}, -] - -[[package]] -name = "azure-storage-blob" -version = "12.18.1" -requires_python = ">=3.7" -summary = "Microsoft Azure Blob Storage Client Library for Python" -dependencies = [ - "azure-core<2.0.0,>=1.28.0", - "cryptography>=2.1.4", - "isodate>=0.6.1", - "typing-extensions>=4.3.0", -] -files = [ - {file = "azure-storage-blob-12.18.1.tar.gz", hash = "sha256:d3265c2403c28d8881326c365e9cf7ed2ad55fdac98404eae753548702b31ba2"}, - {file = "azure_storage_blob-12.18.1-py3-none-any.whl", hash = "sha256:00b92568e91d608c04dfd4814c3b180818e690023493bb984c22dfc1a8a96e55"}, -] - -[[package]] -name = "beautifulsoup4" -version = "4.11.2" -requires_python = ">=3.6.0" -summary = "Screen-scraping library" -dependencies = [ - "soupsieve>1.2", -] -files = [ - {file = "beautifulsoup4-4.11.2-py3-none-any.whl", hash = "sha256:0e79446b10b3ecb499c1556f7e228a53e64a2bfcebd455f370d8927cb5b59e39"}, - {file = "beautifulsoup4-4.11.2.tar.gz", hash = "sha256:bc4bdda6717de5a2987436fb8d72f45dc90dd856bdfd512a1314ce90349a0106"}, -] - -[[package]] -name = "boto3" -version = "1.28.53" -requires_python = ">= 3.7" -summary = "The AWS SDK for Python" -dependencies = [ - "botocore<1.32.0,>=1.31.53", - "jmespath<2.0.0,>=0.7.1", - "s3transfer<0.7.0,>=0.6.0", -] -files = [ - {file = "boto3-1.28.53-py3-none-any.whl", hash = "sha256:dc2da9aff7de359774030a243a09b74568664117e2afb77c6e4b90572ae3a6c3"}, - {file = "boto3-1.28.53.tar.gz", hash = "sha256:b95b0cc39f08402029c3a2bb141e1775cfa46576ebe9f9916f79bde90e27f53f"}, -] - -[[package]] -name = "botocore" -version = "1.31.53" -requires_python = ">= 3.7" -summary = "Low-level, data-driven core of boto 3." -dependencies = [ - "jmespath<2.0.0,>=0.7.1", - "python-dateutil<3.0.0,>=2.1", - "urllib3<1.27,>=1.25.4", -] -files = [ - {file = "botocore-1.31.53-py3-none-any.whl", hash = "sha256:aa647f94039d21de97c969df21ce8c5186b68234eb5c53148f0d8bbd708e375d"}, - {file = "botocore-1.31.53.tar.gz", hash = "sha256:905580ea724d74f11652bab63fcec6bf0d32f1cf8b2963f7388efc0ea406b69b"}, -] - -[[package]] -name = "certifi" -version = "2023.7.22" -requires_python = ">=3.6" -summary = "Python package for providing Mozilla's CA Bundle." -files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, -] - -[[package]] -name = "cffi" -version = "1.15.1" -summary = "Foreign Function Interface for Python calling C code." -dependencies = [ - "pycparser", -] -files = [ - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.2.0" -requires_python = ">=3.7.0" -summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Cross-platform colored terminal text." -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.3.1" -requires_python = ">=3.8" -summary = "Code coverage measurement for Python" -files = [ - {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, - {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, - {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, - {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, - {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, - {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, - {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, -] - -[[package]] -name = "coverage" -version = "7.3.1" -extras = ["toml"] -requires_python = ">=3.8" -summary = "Code coverage measurement for Python" -dependencies = [ - "coverage==7.3.1", -] -files = [ - {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, - {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, - {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, - {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, - {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, - {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, - {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, -] - -[[package]] -name = "cryptography" -version = "41.0.4" -requires_python = ">=3.7" -summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -dependencies = [ - "cffi>=1.12", -] -files = [ - {file = "cryptography-41.0.4-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839"}, - {file = "cryptography-41.0.4-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f"}, - {file = "cryptography-41.0.4-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714"}, - {file = "cryptography-41.0.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb"}, - {file = "cryptography-41.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13"}, - {file = "cryptography-41.0.4-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143"}, - {file = "cryptography-41.0.4-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397"}, - {file = "cryptography-41.0.4-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860"}, - {file = "cryptography-41.0.4-cp37-abi3-win32.whl", hash = "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd"}, - {file = "cryptography-41.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d"}, - {file = "cryptography-41.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67"}, - {file = "cryptography-41.0.4-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e"}, - {file = "cryptography-41.0.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829"}, - {file = "cryptography-41.0.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca"}, - {file = "cryptography-41.0.4-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d"}, - {file = "cryptography-41.0.4-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac"}, - {file = "cryptography-41.0.4-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9"}, - {file = "cryptography-41.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f"}, - {file = "cryptography-41.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91"}, - {file = "cryptography-41.0.4-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8"}, - {file = "cryptography-41.0.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6"}, - {file = "cryptography-41.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311"}, - {file = "cryptography-41.0.4.tar.gz", hash = "sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a"}, -] - -[[package]] -name = "defusedxml" -version = "0.7.1" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -summary = "XML bomb protection for Python stdlib modules" -files = [ - {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, - {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, -] - -[[package]] -name = "django" -version = "4.2.5" -requires_python = ">=3.8" -summary = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." -dependencies = [ - "asgiref<4,>=3.6.0", - "sqlparse>=0.3.1", - "tzdata; sys_platform == \"win32\"", -] -files = [ - {file = "Django-4.2.5-py3-none-any.whl", hash = "sha256:b6b2b5cae821077f137dc4dade696a1c2aa292f892eca28fa8d7bfdf2608ddd4"}, - {file = "Django-4.2.5.tar.gz", hash = "sha256:5e5c1c9548ffb7796b4a8a4782e9a2e5a3df3615259fc1bfd3ebc73b646146c1"}, -] - -[[package]] -name = "django-environ" -version = "0.11.2" -requires_python = ">=3.6,<4" -summary = "A package that allows you to utilize 12factor inspired environment variables to configure your Django application." -files = [ - {file = "django-environ-0.11.2.tar.gz", hash = "sha256:f32a87aa0899894c27d4e1776fa6b477e8164ed7f6b3e410a62a6d72caaf64be"}, - {file = "django_environ-0.11.2-py2.py3-none-any.whl", hash = "sha256:0ff95ab4344bfeff693836aa978e6840abef2e2f1145adff7735892711590c05"}, -] - -[[package]] -name = "django-filter" -version = "23.3" -requires_python = ">=3.7" -summary = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." -dependencies = [ - "Django>=3.2", -] -files = [ - {file = "django-filter-23.3.tar.gz", hash = "sha256:015fe155582e1805b40629344e4a6cf3cc40450827d294d040b4b8c1749a9fa6"}, - {file = "django_filter-23.3-py3-none-any.whl", hash = "sha256:65bc5d1d8f4fff3aaf74cb5da537b6620e9214fb4b3180f6c560776b1b6dccd0"}, -] - -[[package]] -name = "django-modelcluster" -version = "6.0" -requires_python = ">=3.7" -summary = "Django extension to allow working with 'clusters' of models as a single unit, independently of the database" -dependencies = [ - "django>=2.2", - "pytz>=2015.2", -] -files = [ - {file = "django-modelcluster-6.0.tar.gz", hash = "sha256:cdcffef5baf5d3759ee04c5b60ffaf1a0c95fc0f265e762f3ddfadde3394e5db"}, - {file = "django_modelcluster-6.0-py2.py3-none-any.whl", hash = "sha256:4ae46f86c43702020f24f212222eef0a2588df937bbb523a5447da247b5fb130"}, -] - -[[package]] -name = "django-permissionedforms" -version = "0.1" -requires_python = ">=3.7" -summary = "Django extension for creating forms that vary according to user permissions" -dependencies = [ - "Django", -] -files = [ - {file = "django-permissionedforms-0.1.tar.gz", hash = "sha256:4340bb20c4477fffb13b4cc5cccf9f1b1010b64f79956c291c72d2ad2ed243f8"}, - {file = "django_permissionedforms-0.1-py2.py3-none-any.whl", hash = "sha256:d341a961a27cc77fde8cc42141c6ab55cc1f0cb886963cc2d6967b9674fa47d6"}, -] - -[[package]] -name = "django-storages" -version = "1.14" -requires_python = ">=3.7" -summary = "Support for many storage backends in Django" -dependencies = [ - "Django>=3.2", -] -files = [ - {file = "django-storages-1.14.tar.gz", hash = "sha256:6c97e5faad829c923a1262206281742c484d76d43b332a196ddcc242b909c551"}, - {file = "django_storages-1.14-py3-none-any.whl", hash = "sha256:11280a883b13812df548f3cfe9c10280afc0d4727c8babdee369a75e71158f16"}, -] - -[[package]] -name = "django-storages" -version = "1.14" -extras = ["azure"] -requires_python = ">=3.7" -summary = "Support for many storage backends in Django" -dependencies = [ - "azure-storage-blob>=12.0.0", - "django-storages==1.14", -] -files = [ - {file = "django-storages-1.14.tar.gz", hash = "sha256:6c97e5faad829c923a1262206281742c484d76d43b332a196ddcc242b909c551"}, - {file = "django_storages-1.14-py3-none-any.whl", hash = "sha256:11280a883b13812df548f3cfe9c10280afc0d4727c8babdee369a75e71158f16"}, -] - -[[package]] -name = "django-taggit" -version = "4.0.0" -requires_python = ">=3.6" -summary = "django-taggit is a reusable Django application for simple tagging." -dependencies = [ - "Django>=3.2", -] -files = [ - {file = "django-taggit-4.0.0.tar.gz", hash = "sha256:4d52de9d37245a9b9f98c0ec71fdccf1d2283e38e8866d40a7ae6a3b6787a161"}, - {file = "django_taggit-4.0.0-py3-none-any.whl", hash = "sha256:eb800dabef5f0a4e047ab0751f82cf805bc4a9e972037ef12bf519f52cd92480"}, -] - -[[package]] -name = "django-treebeard" -version = "4.7" -requires_python = ">=3.8" -summary = "Efficient tree implementations for Django" -dependencies = [ - "Django>=3.2", -] -files = [ - {file = "django-treebeard-4.7.tar.gz", hash = "sha256:c751a3f924158c288fea89afc25a7151979faf01bf11fdc7be3b858099dfa56d"}, - {file = "django_treebeard-4.7-py3-none-any.whl", hash = "sha256:787117995ff985d98e6c2b241ef6b9d37fe8ff7051cd7535c283616a0b5b2645"}, -] - -[[package]] -name = "djangorestframework" -version = "3.14.0" -requires_python = ">=3.6" -summary = "Web APIs for Django, made easy." -dependencies = [ - "django>=3.0", - "pytz", -] -files = [ - {file = "djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"}, - {file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"}, -] - -[[package]] -name = "draftjs-exporter" -version = "2.1.7" -summary = "Library to convert rich text from Draft.js raw ContentState to HTML" -files = [ - {file = "draftjs_exporter-2.1.7-py3-none-any.whl", hash = "sha256:d415a9964690a2cddb66a31ef32dd46c277e9b80434b94e39e3043188ed83e33"}, - {file = "draftjs_exporter-2.1.7.tar.gz", hash = "sha256:5839cbc29d7bce2fb99837a404ca40c3a07313f2a20e2700de7ad6aa9a9a18fb"}, -] - -[[package]] -name = "et-xmlfile" -version = "1.1.0" -requires_python = ">=3.6" -summary = "An implementation of lxml.xmlfile for the standard library" -files = [ - {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, - {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, -] - -[[package]] -name = "factory-boy" -version = "3.3.0" -requires_python = ">=3.7" -summary = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." -dependencies = [ - "Faker>=0.7.0", -] -files = [ - {file = "factory_boy-3.3.0-py2.py3-none-any.whl", hash = "sha256:a2cdbdb63228177aa4f1c52f4b6d83fab2b8623bf602c7dedd7eb83c0f69c04c"}, - {file = "factory_boy-3.3.0.tar.gz", hash = "sha256:bc76d97d1a65bbd9842a6d722882098eb549ec8ee1081f9fb2e8ff29f0c300f1"}, -] - -[[package]] -name = "faker" -version = "19.6.2" -requires_python = ">=3.8" -summary = "Faker is a Python package that generates fake data for you." -dependencies = [ - "python-dateutil>=2.4", -] -files = [ - {file = "Faker-19.6.2-py3-none-any.whl", hash = "sha256:8fba91068dc26e3159c1ac9f22444a2338704b0991d86605322e454bda420092"}, - {file = "Faker-19.6.2.tar.gz", hash = "sha256:d5d5953556b0fb428a46019e03fc2d40eab2980135ddef5a9eb3d054947fdf83"}, -] - -[[package]] -name = "filetype" -version = "1.2.0" -summary = "Infer file type and MIME type of any file/buffer. No external dependencies." -files = [ - {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, - {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, -] - -[[package]] -name = "graphql-core" -version = "3.2.3" -requires_python = ">=3.6,<4" -summary = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." -files = [ - {file = "graphql-core-3.2.3.tar.gz", hash = "sha256:06d2aad0ac723e35b1cb47885d3e5c45e956a53bc1b209a9fc5369007fe46676"}, - {file = "graphql_core-3.2.3-py3-none-any.whl", hash = "sha256:5766780452bd5ec8ba133f8bf287dc92713e3868ddd83aee4faab9fc3e303dc3"}, -] - -[[package]] -name = "gunicorn" -version = "21.2.0" -requires_python = ">=3.5" -summary = "WSGI HTTP Server for UNIX" -dependencies = [ - "packaging", -] -files = [ - {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, - {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, -] - -[[package]] -name = "h11" -version = "0.14.0" -requires_python = ">=3.7" -summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "html5lib" -version = "1.1" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -summary = "HTML parser based on the WHATWG HTML specification" -dependencies = [ - "six>=1.9", - "webencodings", -] -files = [ - {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, - {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, -] - -[[package]] -name = "httpcore" -version = "0.17.3" -requires_python = ">=3.7" -summary = "A minimal low-level HTTP client." -dependencies = [ - "anyio<5.0,>=3.0", - "certifi", - "h11<0.15,>=0.13", - "sniffio==1.*", -] -files = [ - {file = "httpcore-0.17.3-py3-none-any.whl", hash = "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87"}, - {file = "httpcore-0.17.3.tar.gz", hash = "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888"}, -] - -[[package]] -name = "httpx" -version = "0.24.1" -requires_python = ">=3.7" -summary = "The next generation HTTP client." -dependencies = [ - "certifi", - "httpcore<0.18.0,>=0.15.0", - "idna", - "sniffio", -] -files = [ - {file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"}, - {file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"}, -] - -[[package]] -name = "idna" -version = "3.4" -requires_python = ">=3.5" -summary = "Internationalized Domain Names in Applications (IDNA)" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "inflection" -version = "0.5.1" -requires_python = ">=3.5" -summary = "A port of Ruby on Rails inflector to Python" -files = [ - {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, - {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -requires_python = ">=3.7" -summary = "brain-dead simple config-ini parsing" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "isodate" -version = "0.6.1" -summary = "An ISO 8601 date/time/duration parser and formatter" -dependencies = [ - "six", -] -files = [ - {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, - {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, -] - -[[package]] -name = "jmespath" -version = "1.0.1" -requires_python = ">=3.7" -summary = "JSON Matching Expressions" -files = [ - {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, - {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, -] - -[[package]] -name = "l18n" -version = "2021.3" -summary = "Internationalization for pytz timezones and territories" -dependencies = [ - "pytz>=2020.1", - "six", -] -files = [ - {file = "l18n-2021.3-py3-none-any.whl", hash = "sha256:78495d1df95b6f7dcc694d1ba8994df709c463a1cbac1bf016e1b9a5ce7280b9"}, - {file = "l18n-2021.3.tar.gz", hash = "sha256:1956e890d673d17135cc20913253c154f6bc1c00266c22b7d503cc1a5a42d848"}, -] - -[[package]] -name = "openpyxl" -version = "3.1.2" -requires_python = ">=3.6" -summary = "A Python library to read/write Excel 2010 xlsx/xlsm files" -dependencies = [ - "et-xmlfile", -] -files = [ - {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"}, - {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"}, -] - -[[package]] -name = "packaging" -version = "23.1" -requires_python = ">=3.7" -summary = "Core utilities for Python packages" -files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, -] - -[[package]] -name = "pillow" -version = "10.0.1" -requires_python = ">=3.8" -summary = "Python Imaging Library (Fork)" -files = [ - {file = "Pillow-10.0.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fd2a5403a75b54661182b75ec6132437a181209b901446ee5724b589af8edef1"}, - {file = "Pillow-10.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d7e91b4379f7a76b31c2dda84ab9e20c6220488e50f7822e59dac36b0cd92b1"}, - {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e9adb3f22d4c416e7cd79b01375b17159d6990003633ff1d8377e21b7f1b21"}, - {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93139acd8109edcdeffd85e3af8ae7d88b258b3a1e13a038f542b79b6d255c54"}, - {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:92a23b0431941a33242b1f0ce6c88a952e09feeea9af4e8be48236a68ffe2205"}, - {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cbe68deb8580462ca0d9eb56a81912f59eb4542e1ef8f987405e35a0179f4ea2"}, - {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:522ff4ac3aaf839242c6f4e5b406634bfea002469656ae8358644fc6c4856a3b"}, - {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:84efb46e8d881bb06b35d1d541aa87f574b58e87f781cbba8d200daa835b42e1"}, - {file = "Pillow-10.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:898f1d306298ff40dc1b9ca24824f0488f6f039bc0e25cfb549d3195ffa17088"}, - {file = "Pillow-10.0.1-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:bcf1207e2f2385a576832af02702de104be71301c2696d0012b1b93fe34aaa5b"}, - {file = "Pillow-10.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d6c9049c6274c1bb565021367431ad04481ebb54872edecfcd6088d27edd6ed"}, - {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28444cb6ad49726127d6b340217f0627abc8732f1194fd5352dec5e6a0105635"}, - {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de596695a75496deb3b499c8c4f8e60376e0516e1a774e7bc046f0f48cd620ad"}, - {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:2872f2d7846cf39b3dbff64bc1104cc48c76145854256451d33c5faa55c04d1a"}, - {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4ce90f8a24e1c15465048959f1e94309dfef93af272633e8f37361b824532e91"}, - {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ee7810cf7c83fa227ba9125de6084e5e8b08c59038a7b2c9045ef4dde61663b4"}, - {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1be1c872b9b5fcc229adeadbeb51422a9633abd847c0ff87dc4ef9bb184ae08"}, - {file = "Pillow-10.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:98533fd7fa764e5f85eebe56c8e4094db912ccbe6fbf3a58778d543cadd0db08"}, - {file = "Pillow-10.0.1-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:32bec7423cdf25c9038fef614a853c9d25c07590e1a870ed471f47fb80b244db"}, - {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cf63d2c6928b51d35dfdbda6f2c1fddbe51a6bc4a9d4ee6ea0e11670dd981e"}, - {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f6d3d4c905e26354e8f9d82548475c46d8e0889538cb0657aa9c6f0872a37aa4"}, - {file = "Pillow-10.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:847e8d1017c741c735d3cd1883fa7b03ded4f825a6e5fcb9378fd813edee995f"}, - {file = "Pillow-10.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7f771e7219ff04b79e231d099c0a28ed83aa82af91fd5fa9fdb28f5b8d5addaf"}, - {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459307cacdd4138edee3875bbe22a2492519e060660eaf378ba3b405d1c66317"}, - {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b059ac2c4c7a97daafa7dc850b43b2d3667def858a4f112d1aa082e5c3d6cf7d"}, - {file = "Pillow-10.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6caf3cd38449ec3cd8a68b375e0c6fe4b6fd04edb6c9766b55ef84a6e8ddf2d"}, - {file = "Pillow-10.0.1.tar.gz", hash = "sha256:d72967b06be9300fed5cfbc8b5bafceec48bf7cdc7dab66b1d2549035287191d"}, -] - -[[package]] -name = "pillow-heif" -version = "0.13.0" -requires_python = ">=3.8" -summary = "Python interface for libheif library" -dependencies = [ - "pillow>=9.1.1", -] -files = [ - {file = "pillow_heif-0.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f91f43c7fe09903cb75b9ce15d5a9e69dad370d2081810fc4b2953a616dc8c6f"}, - {file = "pillow_heif-0.13.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f4daa03c473eb4467f93a57ff2f5eb4fc66f649fdffc5fd3c8106603de7b7b66"}, - {file = "pillow_heif-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62758bf775e0c720dc159057136c685d21f20ed71d83117771cf863c6f675ff0"}, - {file = "pillow_heif-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:292c9bba20aa366732f0e6f35ad462aafa89385c09cbd4a4cdcd8f309c9296dd"}, - {file = "pillow_heif-0.13.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2bf43a48ee4b723be9c0366a316c4c56fbc9c583f99730babb60d265372eeea7"}, - {file = "pillow_heif-0.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:88c07d147158aa12f09636fe252d3550907600aeec57974ada57c84bd4d08efd"}, - {file = "pillow_heif-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:efa72a8fc136a324b59b3a3a0bac15803054d33a1738728c720c37ed47eddab3"}, - {file = "pillow_heif-0.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a9e966a0fd2a2d1ae2e56b2010a240ae968de88145b133a17fc1606ff2f8d838"}, - {file = "pillow_heif-0.13.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ee6055fef87de9ae24a483221ca68a8d6d6cb295cb78730a9a23a4b05cdf4908"}, - {file = "pillow_heif-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9be78d1d11c1d3971e69efcaf67bfabbc6f6ee72889015c79e6160f3e2a3d42"}, - {file = "pillow_heif-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5580d2a211da6e14445f7a5e95caa62f0d4d279ad4070382efaee59a0c0e58e5"}, - {file = "pillow_heif-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:101bc0506b91aa22a91676d62c808c4f005e6ab095e241bb828aa752418cdf88"}, - {file = "pillow_heif-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6c705770da571dcef7756f85b8d12c230d2064c886f12b929c462bec607313b2"}, - {file = "pillow_heif-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:2abcefcce289ecda74b845342dcd0e3a29930e77ac5520e45347808b3b0bf2cb"}, - {file = "pillow_heif-0.13.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:457ed3c2bc0376aebb80453fd69e9259fcb727e45ce737185016b9df1e063986"}, - {file = "pillow_heif-0.13.0-pp310-pypy310_pp73-macosx_12_0_arm64.whl", hash = "sha256:6bc46ffc5d9f7a396fb1493b40f834bde96975a0371c38ebae2f4fe4398103d8"}, - {file = "pillow_heif-0.13.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c78f930ae35a24ff95d3c525b1d9794288dbed5b0ff66a3bdfff71a0a33cf02"}, - {file = "pillow_heif-0.13.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9611dab5575f8c4851f154c088e572d264c5587590892920c0e33ad874cc608"}, - {file = "pillow_heif-0.13.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:80b65c84216e23b2a36bc0799f82e68b8881cde60b3af2a68d0e23edb9f9faae"}, - {file = "pillow_heif-0.13.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2f4fb8647fc3ffafe34aed111770c2126c8fd2e04ab9c124fe16b74752aa6c0d"}, - {file = "pillow_heif-0.13.0-pp39-pypy39_pp73-macosx_12_0_arm64.whl", hash = "sha256:c204ca4eb0ee59ab883f6b8475e889663c7230eba87197e024559af4808508af"}, - {file = "pillow_heif-0.13.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fbf127bca5e24edd074acd7e13f40af08acf1006d54e63108415c7521eddb2c"}, - {file = "pillow_heif-0.13.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fe2d04f77e5901acc393ecdb99e00d9fc1409d39b7f8a2f9c85c0f37ae11a1f"}, - {file = "pillow_heif-0.13.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c110efb24c719a045c8304dbb83031a4156431ad761306020916eeab3855c80e"}, - {file = "pillow_heif-0.13.0.tar.gz", hash = "sha256:be337495d0be5accf4d6ea6614f61293c40c86dc8d3e5e6eb63661f90f472362"}, -] - -[[package]] -name = "pluggy" -version = "1.3.0" -requires_python = ">=3.8" -summary = "plugin and hook calling mechanisms for python" -files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, -] - -[[package]] -name = "polib" -version = "1.2.0" -summary = "A library to manipulate gettext files (po and mo files)." -files = [ - {file = "polib-1.2.0-py2.py3-none-any.whl", hash = "sha256:1c77ee1b81feb31df9bca258cbc58db1bbb32d10214b173882452c73af06d62d"}, - {file = "polib-1.2.0.tar.gz", hash = "sha256:f3ef94aefed6e183e342a8a269ae1fc4742ba193186ad76f175938621dbfc26b"}, -] - -[[package]] -name = "psycopg2" -version = "2.9.7" -requires_python = ">=3.6" -summary = "psycopg2 - Python-PostgreSQL Database Adapter" -files = [ - {file = "psycopg2-2.9.7-cp311-cp311-win32.whl", hash = "sha256:44d93a0109dfdf22fe399b419bcd7fa589d86895d3931b01fb321d74dadc68f1"}, - {file = "psycopg2-2.9.7-cp311-cp311-win_amd64.whl", hash = "sha256:91e81a8333a0037babfc9fe6d11e997a9d4dac0f38c43074886b0d9dead94fe9"}, - {file = "psycopg2-2.9.7.tar.gz", hash = "sha256:f00cc35bd7119f1fed17b85bd1007855194dde2cbd8de01ab8ebb17487440ad8"}, -] - -[[package]] -name = "pycparser" -version = "2.21" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -summary = "C parser in Python" -files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] - -[[package]] -name = "pydantic" -version = "2.3.0" -requires_python = ">=3.7" -summary = "Data validation using Python type hints" -dependencies = [ - "annotated-types>=0.4.0", - "pydantic-core==2.6.3", - "typing-extensions>=4.6.1", -] -files = [ - {file = "pydantic-2.3.0-py3-none-any.whl", hash = "sha256:45b5e446c6dfaad9444819a293b921a40e1db1aa61ea08aede0522529ce90e81"}, - {file = "pydantic-2.3.0.tar.gz", hash = "sha256:1607cc106602284cd4a00882986570472f193fde9cb1259bceeaedb26aa79a6d"}, -] - -[[package]] -name = "pydantic-core" -version = "2.6.3" -requires_python = ">=3.7" -summary = "" -dependencies = [ - "typing-extensions!=4.7.0,>=4.6.0", -] -files = [ - {file = "pydantic_core-2.6.3-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:6bcc1ad776fffe25ea5c187a028991c031a00ff92d012ca1cc4714087e575973"}, - {file = "pydantic_core-2.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df14f6332834444b4a37685810216cc8fe1fe91f447332cd56294c984ecbff1c"}, - {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b7486d85293f7f0bbc39b34e1d8aa26210b450bbd3d245ec3d732864009819"}, - {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a892b5b1871b301ce20d40b037ffbe33d1407a39639c2b05356acfef5536d26a"}, - {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:883daa467865e5766931e07eb20f3e8152324f0adf52658f4d302242c12e2c32"}, - {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4eb77df2964b64ba190eee00b2312a1fd7a862af8918ec70fc2d6308f76ac64"}, - {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce8c84051fa292a5dc54018a40e2a1926fd17980a9422c973e3ebea017aa8da"}, - {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22134a4453bd59b7d1e895c455fe277af9d9d9fbbcb9dc3f4a97b8693e7e2c9b"}, - {file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:02e1c385095efbd997311d85c6021d32369675c09bcbfff3b69d84e59dc103f6"}, - {file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d79f1f2f7ebdb9b741296b69049ff44aedd95976bfee38eb4848820628a99b50"}, - {file = "pydantic_core-2.6.3-cp311-none-win32.whl", hash = "sha256:430ddd965ffd068dd70ef4e4d74f2c489c3a313adc28e829dd7262cc0d2dd1e8"}, - {file = "pydantic_core-2.6.3-cp311-none-win_amd64.whl", hash = "sha256:84f8bb34fe76c68c9d96b77c60cef093f5e660ef8e43a6cbfcd991017d375950"}, - {file = "pydantic_core-2.6.3-cp311-none-win_arm64.whl", hash = "sha256:5a2a3c9ef904dcdadb550eedf3291ec3f229431b0084666e2c2aa8ff99a103a2"}, - {file = "pydantic_core-2.6.3-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:8421cf496e746cf8d6b677502ed9a0d1e4e956586cd8b221e1312e0841c002d5"}, - {file = "pydantic_core-2.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bb128c30cf1df0ab78166ded1ecf876620fb9aac84d2413e8ea1594b588c735d"}, - {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37a822f630712817b6ecc09ccc378192ef5ff12e2c9bae97eb5968a6cdf3b862"}, - {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:240a015102a0c0cc8114f1cba6444499a8a4d0333e178bc504a5c2196defd456"}, - {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f90e5e3afb11268628c89f378f7a1ea3f2fe502a28af4192e30a6cdea1e7d5e"}, - {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:340e96c08de1069f3d022a85c2a8c63529fd88709468373b418f4cf2c949fb0e"}, - {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1480fa4682e8202b560dcdc9eeec1005f62a15742b813c88cdc01d44e85308e5"}, - {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f14546403c2a1d11a130b537dda28f07eb6c1805a43dae4617448074fd49c282"}, - {file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a87c54e72aa2ef30189dc74427421e074ab4561cf2bf314589f6af5b37f45e6d"}, - {file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f93255b3e4d64785554e544c1c76cd32f4a354fa79e2eeca5d16ac2e7fdd57aa"}, - {file = "pydantic_core-2.6.3-cp312-none-win32.whl", hash = "sha256:f70dc00a91311a1aea124e5f64569ea44c011b58433981313202c46bccbec0e1"}, - {file = "pydantic_core-2.6.3-cp312-none-win_amd64.whl", hash = "sha256:23470a23614c701b37252618e7851e595060a96a23016f9a084f3f92f5ed5881"}, - {file = "pydantic_core-2.6.3-cp312-none-win_arm64.whl", hash = "sha256:1ac1750df1b4339b543531ce793b8fd5c16660a95d13aecaab26b44ce11775e9"}, - {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d7050899026e708fb185e174c63ebc2c4ee7a0c17b0a96ebc50e1f76a231c057"}, - {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99faba727727b2e59129c59542284efebbddade4f0ae6a29c8b8d3e1f437beb7"}, - {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fa159b902d22b283b680ef52b532b29554ea2a7fc39bf354064751369e9dbd7"}, - {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:046af9cfb5384f3684eeb3f58a48698ddab8dd870b4b3f67f825353a14441418"}, - {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:930bfe73e665ebce3f0da2c6d64455098aaa67e1a00323c74dc752627879fc67"}, - {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:85cc4d105747d2aa3c5cf3e37dac50141bff779545ba59a095f4a96b0a460e70"}, - {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b25afe9d5c4f60dcbbe2b277a79be114e2e65a16598db8abee2a2dcde24f162b"}, - {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e49ce7dc9f925e1fb010fc3d555250139df61fa6e5a0a95ce356329602c11ea9"}, - {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:2dd50d6a1aef0426a1d0199190c6c43ec89812b1f409e7fe44cb0fbf6dfa733c"}, - {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6595b0d8c8711e8e1dc389d52648b923b809f68ac1c6f0baa525c6440aa0daa"}, - {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ef724a059396751aef71e847178d66ad7fc3fc969a1a40c29f5aac1aa5f8784"}, - {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3c8945a105f1589ce8a693753b908815e0748f6279959a4530f6742e1994dcb6"}, - {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c8c6660089a25d45333cb9db56bb9e347241a6d7509838dbbd1931d0e19dbc7f"}, - {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:692b4ff5c4e828a38716cfa92667661a39886e71136c97b7dac26edef18767f7"}, - {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f1a5d8f18877474c80b7711d870db0eeef9442691fcdb00adabfc97e183ee0b0"}, - {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:3796a6152c545339d3b1652183e786df648ecdf7c4f9347e1d30e6750907f5bb"}, - {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b962700962f6e7a6bd77e5f37320cabac24b4c0f76afeac05e9f93cf0c620014"}, - {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56ea80269077003eaa59723bac1d8bacd2cd15ae30456f2890811efc1e3d4413"}, - {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c0ebbebae71ed1e385f7dfd9b74c1cff09fed24a6df43d326dd7f12339ec34"}, - {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:252851b38bad3bfda47b104ffd077d4f9604a10cb06fe09d020016a25107bf98"}, - {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6656a0ae383d8cd7cc94e91de4e526407b3726049ce8d7939049cbfa426518c8"}, - {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9140ded382a5b04a1c030b593ed9bf3088243a0a8b7fa9f071a5736498c5483"}, - {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d38bbcef58220f9c81e42c255ef0bf99735d8f11edef69ab0b499da77105158a"}, - {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c9d469204abcca28926cbc28ce98f28e50e488767b084fb3fbdf21af11d3de26"}, - {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48c1ed8b02ffea4d5c9c220eda27af02b8149fe58526359b3c07eb391cb353a2"}, - {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b2b1bfed698fa410ab81982f681f5b1996d3d994ae8073286515ac4d165c2e7"}, - {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf9d42a71a4d7a7c1f14f629e5c30eac451a6fc81827d2beefd57d014c006c4a"}, - {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4292ca56751aebbe63a84bbfc3b5717abb09b14d4b4442cc43fd7c49a1529efd"}, - {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7dc2ce039c7290b4ef64334ec7e6ca6494de6eecc81e21cb4f73b9b39991408c"}, - {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:615a31b1629e12445c0e9fc8339b41aaa6cc60bd53bf802d5fe3d2c0cda2ae8d"}, - {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1fa1f6312fb84e8c281f32b39affe81984ccd484da6e9d65b3d18c202c666149"}, - {file = "pydantic_core-2.6.3.tar.gz", hash = "sha256:1508f37ba9e3ddc0189e6ff4e2228bd2d3c3a4641cbe8c07177162f76ed696c7"}, -] - -[[package]] -name = "pyjwt" -version = "2.8.0" -requires_python = ">=3.7" -summary = "JSON Web Token implementation in Python" -files = [ - {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, - {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, -] - -[[package]] -name = "pytest" -version = "7.4.2" -requires_python = ">=3.7" -summary = "pytest: simple powerful testing with Python" -dependencies = [ - "colorama; sys_platform == \"win32\"", - "iniconfig", - "packaging", - "pluggy<2.0,>=0.12", -] -files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, -] - -[[package]] -name = "pytest-cov" -version = "4.1.0" -requires_python = ">=3.7" -summary = "Pytest plugin for measuring coverage." -dependencies = [ - "coverage[toml]>=5.2.1", - "pytest>=4.6", -] -files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, -] - -[[package]] -name = "pytest-django" -version = "4.5.2" -requires_python = ">=3.5" -summary = "A Django plugin for pytest." -dependencies = [ - "pytest>=5.4.0", -] -files = [ - {file = "pytest-django-4.5.2.tar.gz", hash = "sha256:d9076f759bb7c36939dbdd5ae6633c18edfc2902d1a69fdbefd2426b970ce6c2"}, - {file = "pytest_django-4.5.2-py3-none-any.whl", hash = "sha256:c60834861933773109334fe5a53e83d1ef4828f2203a1d6a0fa9972f4f75ab3e"}, -] - -[[package]] -name = "pytest-factoryboy" -version = "2.5.1" -requires_python = ">=3.7" -summary = "Factory Boy support for pytest." -dependencies = [ - "factory-boy>=2.10.0", - "inflection", - "pytest>=5.0.0", - "typing-extensions", -] -files = [ - {file = "pytest_factoryboy-2.5.1-py3-none-any.whl", hash = "sha256:41e3465935322188daefc8720f83cebb16bf3d3a430356dc91151c55f31d99c7"}, - {file = "pytest_factoryboy-2.5.1.tar.gz", hash = "sha256:7275a52299b20c0f58b63fdf7326b3fd2b7cbefbdaa90fdcfc776bbe92197484"}, -] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -summary = "Extensions to the standard Python datetime module" -dependencies = [ - "six>=1.5", -] -files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[[package]] -name = "pythonit-toolkit" -version = "0.1.88" -requires_python = ">=3.11,<4.0" -summary = "" -dependencies = [ - "PyJWT<3.0.0,>=2.8.0", - "boto3<2.0.0,>=1.28.9", - "httpx<0.25.0,>=0.24.1", - "pydantic", - "sentry-sdk<2.0.0,>=1.28.1", - "starlette<0.31.0,>=0.30.0", - "strawberry-graphql", -] -files = [ - {file = "pythonit_toolkit-0.1.88-py3-none-any.whl", hash = "sha256:8664494eec5749b3ac01b8708054fbadf645b31b5c7ff0e86da076b91a0b5a92"}, - {file = "pythonit_toolkit-0.1.88.tar.gz", hash = "sha256:2f20ff67860a98f5f1920e4db688ac748d2b8408c7b91f80861b8423ee34c4d5"}, -] - -[[package]] -name = "pytz" -version = "2023.3.post1" -summary = "World timezone definitions, modern and historical" -files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, -] - -[[package]] -name = "requests" -version = "2.31.0" -requires_python = ">=3.7" -summary = "Python HTTP for Humans." -dependencies = [ - "certifi>=2017.4.17", - "charset-normalizer<4,>=2", - "idna<4,>=2.5", - "urllib3<3,>=1.21.1", -] -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[[package]] -name = "respx" -version = "0.20.2" -requires_python = ">=3.7" -summary = "A utility for mocking out the Python HTTPX and HTTP Core libraries." -dependencies = [ - "httpx>=0.21.0", -] -files = [ - {file = "respx-0.20.2-py2.py3-none-any.whl", hash = "sha256:ab8e1cf6da28a5b2dd883ea617f8130f77f676736e6e9e4a25817ad116a172c9"}, - {file = "respx-0.20.2.tar.gz", hash = "sha256:07cf4108b1c88b82010f67d3c831dae33a375c7b436e54d87737c7f9f99be643"}, -] - -[[package]] -name = "s3transfer" -version = "0.6.2" -requires_python = ">= 3.7" -summary = "An Amazon S3 Transfer Manager" -dependencies = [ - "botocore<2.0a.0,>=1.12.36", -] -files = [ - {file = "s3transfer-0.6.2-py3-none-any.whl", hash = "sha256:b014be3a8a2aab98cfe1abc7229cc5a9a0cf05eb9c1f2b86b230fd8df3f78084"}, - {file = "s3transfer-0.6.2.tar.gz", hash = "sha256:cab66d3380cca3e70939ef2255d01cd8aece6a4907a9528740f668c4b0611861"}, -] - -[[package]] -name = "sentry-sdk" -version = "1.31.0" -summary = "Python client for Sentry (https://sentry.io)" -dependencies = [ - "certifi", - "urllib3>=1.26.11; python_version >= \"3.6\"", -] -files = [ - {file = "sentry-sdk-1.31.0.tar.gz", hash = "sha256:6de2e88304873484207fed836388e422aeff000609b104c802749fd89d56ba5b"}, - {file = "sentry_sdk-1.31.0-py2.py3-none-any.whl", hash = "sha256:64a7141005fb775b9db298a30de93e3b83e0ddd1232dc6f36eb38aebc1553291"}, -] - -[[package]] -name = "sentry-sdk" -version = "1.31.0" -extras = ["django"] -summary = "Python client for Sentry (https://sentry.io)" -dependencies = [ - "django>=1.8", - "sentry-sdk==1.31.0", -] -files = [ - {file = "sentry-sdk-1.31.0.tar.gz", hash = "sha256:6de2e88304873484207fed836388e422aeff000609b104c802749fd89d56ba5b"}, - {file = "sentry_sdk-1.31.0-py2.py3-none-any.whl", hash = "sha256:64a7141005fb775b9db298a30de93e3b83e0ddd1232dc6f36eb38aebc1553291"}, -] - -[[package]] -name = "six" -version = "1.16.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -summary = "Python 2 and 3 compatibility utilities" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "sniffio" -version = "1.3.0" -requires_python = ">=3.7" -summary = "Sniff out which async library your code is running under" -files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, -] - -[[package]] -name = "soupsieve" -version = "2.5" -requires_python = ">=3.8" -summary = "A modern CSS selector implementation for Beautiful Soup." -files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, -] - -[[package]] -name = "sqlparse" -version = "0.4.4" -requires_python = ">=3.5" -summary = "A non-validating SQL parser." -files = [ - {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, - {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, -] - -[[package]] -name = "starlette" -version = "0.30.0" -requires_python = ">=3.8" -summary = "The little ASGI library that shines." -dependencies = [ - "anyio<5,>=3.4.0", -] -files = [ - {file = "starlette-0.30.0-py3-none-any.whl", hash = "sha256:cb15a5dfbd8de70c999bd1ae4b7e1ba625d74520bc57b28cc4086c7969431f2d"}, - {file = "starlette-0.30.0.tar.gz", hash = "sha256:9cf6bd5f2fbc091c2f22701f9b7f7dfcbd304a567845cffbf89d706543fd2a03"}, -] - -[[package]] -name = "strawberry-graphql" -version = "0.209.1" -requires_python = ">=3.8,<4.0" -summary = "A library for creating GraphQL APIs" -dependencies = [ - "graphql-core<3.3.0,>=3.2.0", - "python-dateutil<3.0.0,>=2.7.0", - "typing-extensions>=4.5.0", -] -files = [ - {file = "strawberry_graphql-0.209.1-py3-none-any.whl", hash = "sha256:c2ffa1c343c06e3b2b407b52e75bd5179704702dd5d2d1b0265cd46be440a703"}, - {file = "strawberry_graphql-0.209.1.tar.gz", hash = "sha256:c9b6a541a0a1fef07cbf2242e439aaebeb5a20c426cf2b0ba6b605b00d86352f"}, -] - -[[package]] -name = "telepath" -version = "0.3.1" -requires_python = ">=3.8" -summary = "A library for exchanging data between Python and JavaScript" -files = [ - {file = "telepath-0.3.1.tar.gz", hash = "sha256:925c0609e0a8a6488ec4a55b19d485882cf72223b2b19fe2359a50fddd813c9c"}, -] - -[[package]] -name = "typing-extensions" -version = "4.8.0" -requires_python = ">=3.8" -summary = "Backported and Experimental Type Hints for Python 3.8+" -files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, -] - -[[package]] -name = "tzdata" -version = "2023.3" -requires_python = ">=2" -summary = "Provider of IANA time zone data" -files = [ - {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, - {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, -] - -[[package]] -name = "urllib3" -version = "1.26.16" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -summary = "HTTP library with thread-safe connection pooling, file post, and more." -files = [ - {file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"}, - {file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"}, -] - -[[package]] -name = "wagtail" -version = "5.1.1" -requires_python = ">=3.8" -summary = "A Django content management system." -dependencies = [ - "Django<4.3,>=3.2", - "Pillow<11.0.0,>=9.1.0", - "Willow[heif]<1.7,>=1.6", - "anyascii>=0.1.5", - "beautifulsoup4<4.12,>=4.8", - "django-filter<24,>=2.2", - "django-modelcluster<7.0,>=6.0", - "django-permissionedforms<1.0,>=0.1", - "django-taggit<5.0,>=2.0", - "django-treebeard<5.0,>=4.5.1", - "djangorestframework<4.0,>=3.11.1", - "draftjs-exporter<3.0,>=2.1.5", - "html5lib<2,>=0.999", - "l18n>=2018.5", - "openpyxl<4.0,>=3.0.10", - "requests<3.0,>=2.11.1", - "telepath<1,>=0.1.1", -] -files = [ - {file = "wagtail-5.1.1-py3-none-any.whl", hash = "sha256:e1f1d920bb294c81eafdb415d2bd150d5637e2323bf9df5a34ef53cb86274121"}, - {file = "wagtail-5.1.1.tar.gz", hash = "sha256:211ff0cd7059f968a7fc4385503b20d801c1e68b56f5831e0a6a4fc600a5943f"}, -] - -[[package]] -name = "wagtail-factories" -version = "4.1.0" -summary = "Factory boy classes for wagtail" -dependencies = [ - "factory-boy>=3.2", - "wagtail>=4.1", -] -files = [ - {file = "wagtail_factories-4.1.0-py2.py3-none-any.whl", hash = "sha256:6abd435518fdf0cdd3d9920acb2abbd9005936d6fecfdf6fdde3e404834d4b0c"}, - {file = "wagtail_factories-4.1.0.tar.gz", hash = "sha256:067e3e1a30721a90eaa1514c8214aae7a171490bbedf026eeaa904a2ed9abcd0"}, -] - -[[package]] -name = "wagtail-localize" -version = "1.5.2" -requires_python = ">=3.8" -summary = "Translation plugin for Wagtail CMS" -dependencies = [ - "Django<5.0,>=3.2", - "Wagtail>=4.1", - "polib<2.0,>=1.1", - "typing-extensions>=4.0", -] -files = [ - {file = "wagtail_localize-1.5.2-py3-none-any.whl", hash = "sha256:88e0d282fe823fcd6f5f30da2c0460b5a8fc56799b00b3612945aeb163e67ea7"}, - {file = "wagtail_localize-1.5.2.tar.gz", hash = "sha256:52b293228c25518d1a793a8a4406a7ad779be35c7f0ede2932cb0eb35eb8747a"}, -] - -[[package]] -name = "webencodings" -version = "0.5.1" -summary = "Character encoding aliases for legacy web content" -files = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] - -[[package]] -name = "whitenoise" -version = "6.5.0" -requires_python = ">=3.7" -summary = "Radically simplified static file serving for WSGI applications" -files = [ - {file = "whitenoise-6.5.0-py3-none-any.whl", hash = "sha256:16468e9ad2189f09f4a8c635a9031cc9bb2cdbc8e5e53365407acf99f7ade9ec"}, - {file = "whitenoise-6.5.0.tar.gz", hash = "sha256:15fe60546ac975b58e357ccaeb165a4ca2d0ab697e48450b8f0307ca368195a8"}, -] - -[[package]] -name = "willow" -version = "1.6.2" -requires_python = ">=3.8" -summary = "A Python image library that sits on top of Pillow, Wand and OpenCV" -dependencies = [ - "defusedxml<1.0,>=0.7", - "filetype!=1.1.0,>=1.0.10", -] -files = [ - {file = "willow-1.6.2-py3-none-any.whl", hash = "sha256:957a4af8a7733e116a65eca34da11afe3fd52ffdb397494c8823901c25863787"}, - {file = "willow-1.6.2.tar.gz", hash = "sha256:e2d0450fd78ab19052d0478b888ef163e3264e8dcd1af002dd691458db98056f"}, -] - -[[package]] -name = "willow" -version = "1.6.2" -extras = ["heif"] -requires_python = ">=3.8" -summary = "A Python image library that sits on top of Pillow, Wand and OpenCV" -dependencies = [ - "Willow==1.6.2", - "pillow-heif<1.0.0,>=0.10.0", -] -files = [ - {file = "willow-1.6.2-py3-none-any.whl", hash = "sha256:957a4af8a7733e116a65eca34da11afe3fd52ffdb397494c8823901c25863787"}, - {file = "willow-1.6.2.tar.gz", hash = "sha256:e2d0450fd78ab19052d0478b888ef163e3264e8dcd1af002dd691458db98056f"}, -] diff --git a/cms/pyproject.toml b/cms/pyproject.toml deleted file mode 100644 index 664aca8229..0000000000 --- a/cms/pyproject.toml +++ /dev/null @@ -1,41 +0,0 @@ -[tool] -[tool.pdm] -[tool.pdm.scripts] -migrate = "python manage.py migrate" -[tool.pdm.dev-dependencies] -dev = [ - "wagtail-factories>=4.0.0", - "pytest>=7.2.2", - "factory-boy>=3.2.1", - "pytest-factoryboy>=2.5.1", - "pytest-django>=4.5.2", - "pytest-cov>=4.0.0", - "respx>=0.18.2", -] - -[project] -name = "" -version = "" -description = "" -authors = [{ name = "Python Italia", email = "help@pycon.it" }] -dependencies = [ - "wagtail==5.1.1", - "django==4.2.5", - "wagtail-localize==1.5.2", - "strawberry-graphql==0.209.1", - "django-environ==0.11.2", - "django-storages[azure]==1.14", - "gunicorn==21.2.0", - "psycopg2==2.9.7", - "pythonit-toolkit>=0.1.85", - "whitenoise==6.5.0", - "sentry-sdk[django]==1.31.0", - "httpx>=0.20.0", -] -requires-python = ">=3.11" -license = { text = "MIT" } - -[tool.pytest.ini_options] -DJANGO_SETTINGS_MODULE = "cms.settings.dev" -addopts = "--failed-first -vv -s -l --cov-report term-missing --cov=. --reuse-db --no-migrations" -norecursedirs = ".git htmlcov tmp*" diff --git a/cms/sites/__init__.py b/cms/sites/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/sites/apps.py b/cms/sites/apps.py deleted file mode 100644 index f00f335d22..0000000000 --- a/cms/sites/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class SitesConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "sites" diff --git a/cms/sites/migrations/0001_vercel_frontend_settings.py b/cms/sites/migrations/0001_vercel_frontend_settings.py deleted file mode 100644 index f485816f26..0000000000 --- a/cms/sites/migrations/0001_vercel_frontend_settings.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 4.1.7 on 2023-04-10 11:40 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('wagtailcore', '0083_workflowcontenttype'), - ] - - operations = [ - migrations.CreateModel( - name='VercelFrontendSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('revalidate_url', models.CharField(max_length=3000)), - ('revalidate_secret', models.TextField()), - ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.site')), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/cms/sites/migrations/__init__.py b/cms/sites/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/sites/models.py b/cms/sites/models.py deleted file mode 100644 index 9163535595..0000000000 --- a/cms/sites/models.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.db import models -from wagtail.contrib.settings.models import ( - BaseSiteSetting, - register_setting, -) - - -@register_setting -class VercelFrontendSettings(BaseSiteSetting): - revalidate_url = models.CharField(max_length=3000) - revalidate_secret = models.TextField() diff --git a/cms/sites/tests/__init__.py b/cms/sites/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cms/sites/tests/factories.py b/cms/sites/tests/factories.py deleted file mode 100644 index 4f934159ef..0000000000 --- a/cms/sites/tests/factories.py +++ /dev/null @@ -1,10 +0,0 @@ -from factory.django import DjangoModelFactory -from pytest_factoryboy import register - -from sites.models import VercelFrontendSettings - - -@register -class VercelFrontendSettingsFactory(DjangoModelFactory): - class Meta: - model = VercelFrontendSettings diff --git a/gateway/.dockerignore b/gateway/.dockerignore deleted file mode 100644 index c777f224cd..0000000000 --- a/gateway/.dockerignore +++ /dev/null @@ -1,9 +0,0 @@ -**/node_modules/** -.prettierrc -.editorconfig -Dockerfile -dist -.env -**/__tests__/** -dev/** -dev-types.d.ts diff --git a/gateway/.editorconfig b/gateway/.editorconfig deleted file mode 120000 index 38d9a0ce1c..0000000000 --- a/gateway/.editorconfig +++ /dev/null @@ -1 +0,0 @@ -../.editorconfig \ No newline at end of file diff --git a/gateway/.eslintrc b/gateway/.eslintrc deleted file mode 100644 index 3a43ec3c30..0000000000 --- a/gateway/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["@python-italia"] -} diff --git a/gateway/.npmrc b/gateway/.npmrc deleted file mode 100644 index 319e41e69d..0000000000 --- a/gateway/.npmrc +++ /dev/null @@ -1 +0,0 @@ -strict-peer-dependencies=false diff --git a/gateway/.prettierignore b/gateway/.prettierignore deleted file mode 100644 index e6a03bba79..0000000000 --- a/gateway/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -coverage/** diff --git a/gateway/.prettierrc b/gateway/.prettierrc deleted file mode 100755 index 1e4637a6ae..0000000000 --- a/gateway/.prettierrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "semi": true, - "singleQuote": false, - "trailingComma": "all", - "tabWidth": 2, - "useTabs": false, - "importOrder": ["^..?/init$", "^next", "^~", "^[./]", ".css$"], - "importOrderSeparation": true -} diff --git a/gateway/Dockerfile b/gateway/Dockerfile deleted file mode 100644 index 3beeeaf0a0..0000000000 --- a/gateway/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM amazon/aws-lambda-nodejs:14 as build - -RUN npm install -g pnpm@7.3.0; \ - pnpm --version; \ - pnpm setup; \ - mkdir -p /usr/local/share/pnpm &&\ - export PNPM_HOME="/usr/local/share/pnpm" &&\ - export PATH="$PNPM_HOME:$PATH"; \ - pnpm bin -g - -WORKDIR /app - -ENV NODE_ENV=production - -COPY package.json pnpm-lock.yaml .npmrc ./ - -RUN pnpm install - -COPY . . - -RUN pnpm run tsc - -FROM amazon/aws-lambda-nodejs:14 - -WORKDIR ${LAMBDA_TASK_ROOT} - -COPY --from=build /app/node_modules ./node_modules -COPY --from=build /app/dist ./ - -ENV NODE_ENV=production - -CMD [ "handler.graphqlHandler" ] diff --git a/gateway/__mocks__/config.ts b/gateway/__mocks__/config.ts deleted file mode 100644 index fd9f1667ea..0000000000 --- a/gateway/__mocks__/config.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const IDENTITY_SECRET = "abc"; -export const PASTAPORTO_SECRET = "abc"; -export const IS_DEV = true; diff --git a/gateway/config.ts b/gateway/config.ts deleted file mode 100644 index 2baf971d81..0000000000 --- a/gateway/config.ts +++ /dev/null @@ -1,21 +0,0 @@ -import dotenv from "dotenv"; - -dotenv.config(); - -export const { - // Secrets - PASTAPORTO_SECRET, - IDENTITY_SECRET, - SERVICE_TO_SERVICE_SECRET, - - // Services - USERS_SERVICE, - ASSOCIATION_BACKEND_SERVICE, - PYCON_BACKEND_SERVICE, - CMS_SERVICE, - - // Other configuration - IS_DEV = process.env.NODE_ENV === "development", - SENTRY_DSN = "", - ENV = "local", -} = process.env; diff --git a/gateway/context.ts b/gateway/context.ts deleted file mode 100644 index 26abf8e1f8..0000000000 --- a/gateway/context.ts +++ /dev/null @@ -1,54 +0,0 @@ -import cookie from "cookie"; - -import { IS_DEV } from "./config"; -import { createPastaporto } from "./pastaporto/create-pastaporto"; - -export type ApolloContext = { - setCookies?: any[]; - setHeaders?: any[]; - allHeaders?: any; - pastaporto: string | null; - res?: any; -}; - -export const createContext = async ({ - allHeaders, - cookiesHeader, - res, -}: { - allHeaders: any; - cookiesHeader?: string; - res: any; -}) => { - let cookies = null; - if (cookiesHeader) { - cookies = cookie.parse(cookiesHeader); - } - - const identity = cookies?.["identity_v2"] ?? null; - let pastaporto = null; - try { - pastaporto = await createPastaporto(identity); - } catch (e) { - console.error("Error creating pastaporto.", e); - res.cookie("identity_v2", "invalid", { - httpOnly: true, - maxAge: -1, - path: "/", - sameSite: "strict", - secure: !IS_DEV, - }); - } - - const context: ApolloContext = { - setCookies: [], - setHeaders: [], - pastaporto, - allHeaders, - res, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - cookies: cookiesHeader, - }; - return context; -}; diff --git a/gateway/dev-types.d.ts b/gateway/dev-types.d.ts deleted file mode 100644 index 3b47093f48..0000000000 --- a/gateway/dev-types.d.ts +++ /dev/null @@ -1 +0,0 @@ -import "jest-extended"; diff --git a/gateway/dev/dev-server.ts b/gateway/dev/dev-server.ts deleted file mode 100644 index 21b6cb77f5..0000000000 --- a/gateway/dev/dev-server.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { ApolloServer } from "apollo-server"; - -import "../init"; - -import { createContext } from "../context"; -import { createGateway } from "../gateway"; -import { initSentry, SentryPlugin } from "../plugins/sentry"; -import { getPort } from "./utils"; - -initSentry(false); - -const server = new ApolloServer({ - gateway: createGateway(), - plugins: [SentryPlugin(false)], - cors: { - origin: [ - "http://localhost:3020", - "http://localhost:3010", - "http://localhost:3000", - "https://studio.apollographql.com", - ], - credentials: true, - }, - context: async ({ req, res }) => { - return createContext({ - allHeaders: req.headers, - cookiesHeader: req.headers.cookie, - res, - }); - }, -}); - -server.listen({ port: getPort() }).then(({ url }) => { - console.log(`🚀 Server ready at ${url}`); -}); diff --git a/gateway/dev/utils.ts b/gateway/dev/utils.ts deleted file mode 100644 index 3545b77b7e..0000000000 --- a/gateway/dev/utils.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const getPort = () => { - return 4000; -}; diff --git a/gateway/gateway.ts b/gateway/gateway.ts deleted file mode 100644 index d189b9fd47..0000000000 --- a/gateway/gateway.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { - ApolloGateway, - IntrospectAndCompose, - RemoteGraphQLDataSource, -} from "@apollo/gateway"; - -import { IS_DEV } from "./config"; -import { getServices } from "./services"; - -const PASTAPORTO_X_HEADER = "x-pastaporto"; -const BACKEND_TOKEN_X_HEADER = "x-backend-token"; - -class ServiceRemoteGraphQLDataSource extends RemoteGraphQLDataSource { - async willSendRequest({ - request, - context, - }: Parameters>[0]) { - if (context.pastaporto) { - request!.http!.headers.set(PASTAPORTO_X_HEADER, context.pastaporto); - } - request!.http!.headers.set("cookie", context.cookies); - - const gatewayRequestHeaders = context.allHeaders; - if ( - gatewayRequestHeaders && - gatewayRequestHeaders[BACKEND_TOKEN_X_HEADER] - ) { - request!.http!.headers.set( - BACKEND_TOKEN_X_HEADER, - gatewayRequestHeaders[BACKEND_TOKEN_X_HEADER], - ); - } - } - - didReceiveResponse({ - response, - context, - }: Parameters< - NonNullable - >[0]) { - const headers = response.http!.headers; - const setCookie = headers.get("set-cookie"); - - if (setCookie) { - context.res.set("set-cookie", setCookie); - } - - return response; - } -} - -export const createGateway = () => { - const options: any = {}; - - if (IS_DEV) { - options.supergraphSdl = new IntrospectAndCompose({ - subgraphs: getServices(), - pollIntervalInMs: 5000, - }); - } - - return new ApolloGateway({ - ...options, - buildService({ url }) { - return new ServiceRemoteGraphQLDataSource({ - url, - }); - }, - }); -}; diff --git a/gateway/handler.ts b/gateway/handler.ts deleted file mode 100644 index 275aeafacf..0000000000 --- a/gateway/handler.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as ServerlessSentry from "@sentry/serverless"; -import { ApolloServer } from "apollo-server-lambda"; - -import "./init"; - -import { createContext } from "./context"; -import { createGateway } from "./gateway"; -import { initSentry, SentryPlugin } from "./plugins/sentry"; - -initSentry(true); - -const server = new ApolloServer({ - gateway: createGateway(), - introspection: true, - plugins: [SentryPlugin(true)], - context: async ({ event, express }) => { - return createContext({ - allHeaders: event.headers, - cookiesHeader: event.headers["Cookie"], - res: express.res, - }); - }, -}); - -let serverHandler: ReturnType | null = null; - -exports.graphqlHandler = ServerlessSentry.AWSLambda.wrapHandler( - async (event: any, context: any) => { - if (!serverHandler) { - serverHandler = server.createHandler({ - expressGetMiddlewareOptions: { - cors: { - credentials: true, - methods: ["GET", "POST", "OPTIONS", "HEAD"], - origin: [ - /python-italia\.vercel\.app$/, - "http://localhost:3000", - "https://associazione.python.it", - "https://pycon.it", - "https://studio.apollographql.com", - ], - }, - }, - }); - } - - try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return await serverHandler!(event, context, null); - } catch (e) { - console.error("server handler error:", e); - } - }, -); diff --git a/gateway/init.ts b/gateway/init.ts deleted file mode 100644 index eb1e60e19b..0000000000 --- a/gateway/init.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { gql } from "apollo-server"; -import { readFileSync } from "fs"; - -require.extensions[".graphql"] = function (module, filename) { - module.exports = gql(readFileSync(filename, "utf8")); -}; diff --git a/gateway/jest.config.js b/gateway/jest.config.js deleted file mode 100644 index 8002b31fa3..0000000000 --- a/gateway/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - preset: "ts-jest", - testEnvironment: "node", - setupFilesAfterEnv: ["jest-extended"], -}; diff --git a/gateway/nodemon.json b/gateway/nodemon.json deleted file mode 100644 index fa99c00caf..0000000000 --- a/gateway/nodemon.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "watch": ["..", "."], - "exec": "ts-node --project tsconfig.json dev/dev-server.ts", - "ext": "js ts" -} diff --git a/gateway/package.json b/gateway/package.json deleted file mode 100644 index 3c5fae2059..0000000000 --- a/gateway/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "gateway", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "dependencies": { - "@apollo/federation": "^0.38.1", - "@apollo/gateway": "2.5.6", - "@sentry/integrations": "^7.74.0", - "@sentry/node": "^7.74.0", - "@sentry/serverless": "^7.74.0", - "@sentry/tracing": "^7.74.0", - "apollo-server": "3.12.1", - "apollo-server-errors": "^3.3.1", - "apollo-server-lambda": "3.12.1", - "apollo-server-plugin-base": "^3.7.2", - "apollo-server-types": "^3.8.0", - "cookie": "^0.5.0", - "cors": "^2.8.5", - "dotenv": "^16.3.1", - "graphql": "^16.8.1", - "graphql-request": "^6.1.0", - "jsonwebtoken": "^9.0.2", - "typescript": "5.2.2" - }, - "scripts": { - "dev": "NODE_ENV=development nodemon", - "tsc": "tsc", - "lint": "eslint . --ext .js,.ts", - "lint:ts": "tsc --noemit", - "lint:prettier": "prettier --check ./**/*" - }, - "devDependencies": { - "@python-italia/eslint-config": "^1.0.13", - "@trivago/prettier-plugin-sort-imports": "^4.2.0", - "@types/cookie": "^0.5.2", - "@types/jest": "^29.5.5", - "@types/jsonwebtoken": "^9.0.3", - "@types/node": "^20.8.6", - "jest": "^29.7.0", - "jest-extended": "^4.0.2", - "nodemon": "3.0.1", - "prettier": "^3.0.3", - "ts-jest": "^29.1.1", - "ts-node": "10.9.1" - }, - "resolutions": { - "typescript": "4.6.3" - } -} diff --git a/gateway/pastaporto/create-pastaporto.ts b/gateway/pastaporto/create-pastaporto.ts deleted file mode 100644 index fa99726e2b..0000000000 --- a/gateway/pastaporto/create-pastaporto.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { gql, GraphQLClient } from "graphql-request"; -import jwt from "jsonwebtoken"; - -import { SERVICE_TO_SERVICE_SECRET, USERS_SERVICE } from "../config"; - -const USERS_SERVICE_INTERNAL_API_ENDPOINT = `${USERS_SERVICE}/internal-api`; - -export const createPastaporto = async ( - identityToken: string | null, -): Promise => { - if (!identityToken) { - return null; - } - - const token: string = jwt.sign({}, SERVICE_TO_SERVICE_SECRET!, { - issuer: "gateway", - audience: "users-backend", - expiresIn: "1m", - }); - - const client = new GraphQLClient(USERS_SERVICE_INTERNAL_API_ENDPOINT, { - headers: { - "x-service-token": token, - }, - }); - - const query = gql` - query ($identityToken: String) { - createPastaporto(identityToken: $identityToken) { - pastaportoToken - } - } - `; - - const data = await client.request(query, { identityToken }); - // @ts-ignore - return data.createPastaporto.pastaportoToken; -}; diff --git a/gateway/plugins/apollo-headers.ts b/gateway/plugins/apollo-headers.ts deleted file mode 100644 index 662aab4a54..0000000000 --- a/gateway/plugins/apollo-headers.ts +++ /dev/null @@ -1,47 +0,0 @@ -import cookie from "cookie"; - -import { ApolloContext } from "../context"; - -export type RequestContext = { - context: ApolloContext; - response: any; - [key: string]: any; -}; - -export const apolloHeadersPlugin = (applyCookies = false) => { - return { - async requestDidStart() { - return { - async willSendResponse(requestContext: RequestContext) { - const { setHeaders = [], setCookies = [] } = requestContext.context; - - // inform user about wrong usage - if (!Array.isArray(requestContext.context.setHeaders)) { - console.warn("setHeaders is not in context or is not an array"); - } - if (!Array.isArray(requestContext.context.setCookies)) { - console.warn("setCookies is not in context or is not an array"); - } - - // set headers - setHeaders.forEach(({ key, value }) => { - requestContext.response.http.headers.append(key, value); - }); - - if (applyCookies) { - // set cookies - const serializedCookieArray = setCookies.map( - ({ name, value, options }) => - cookie.serialize(name, value, options), - ); - - requestContext.response.http.headers.set( - "Set-Cookie", - serializedCookieArray, - ); - } - }, - }; - }, - }; -}; diff --git a/gateway/plugins/format-cookies-express.ts b/gateway/plugins/format-cookies-express.ts deleted file mode 100644 index fb8d7a7fb6..0000000000 --- a/gateway/plugins/format-cookies-express.ts +++ /dev/null @@ -1,20 +0,0 @@ -import cookie from "cookie"; - -import { RequestContext } from "./apollo-headers"; - -export const formatCookiesForExpressPlugin = { - async requestDidStart() { - return { - async willSendResponse(requestContext: RequestContext) { - // Under express we can set multiple cookies - // by just calling response.append - // this plugin assumes we set the express response in the context - // and can be used only under express, so in the local env only - const { setCookies = [], res } = requestContext.context; - setCookies.forEach(({ name, value, options }) => - res.append("Set-Cookie", cookie.serialize(name, value, options)), - ); - }, - }; - }, -}; diff --git a/gateway/plugins/sentry.ts b/gateway/plugins/sentry.ts deleted file mode 100644 index d8302ba21b..0000000000 --- a/gateway/plugins/sentry.ts +++ /dev/null @@ -1,123 +0,0 @@ -import * as NodeSentry from "@sentry/node"; -import * as ServerlessSentry from "@sentry/serverless"; -import { CaptureConsole } from "@sentry/integrations"; -import { ApolloError } from "apollo-server"; -import { ApolloServerPlugin } from "apollo-server-plugin-base"; -import { GraphQLRequestContextDidEncounterErrors } from "apollo-server-types"; -import { GraphQLError } from "graphql"; - -import { ENV, SENTRY_DSN } from "../config"; - -type ApolloContext = {}; - -export const initSentry = (isServerless: boolean) => { - if (!SENTRY_DSN) { - return; - } - - const wrapper = isServerless ? ServerlessSentry.AWSLambda : NodeSentry; - - wrapper.init({ - dsn: SENTRY_DSN, - environment: ENV, - tracesSampleRate: 0.1, - integrations: [ - new CaptureConsole({ - levels: ["error"], - }), - ], - }); -}; - -const shouldSkipError = (error: GraphQLError) => { - if (error instanceof ApolloError) { - return true; - } - - return false; -}; - -const PPI_FIELDS = [ - "password", - "addressLine1", - "firstName", - "lastName", - "phoneNumber", - "postcode", - "country", - "city", - "email", -]; - -export const removePIIs = ( - data: { [name: string]: any } | undefined | null, -) => { - /* This function removes personal information from an object. - If the value is not empty and it is a personal information field - it will be replace with [REDACTED]. - We don't replace falsy (null, empty strings) values, as it might be useful - to know that they were falsy for debugging. - */ - if (!data) { - return data; - } - - for (const [key, value] of Object.entries(data)) { - if (typeof value == "object") { - data[key] = removePIIs(value); - } else { - if (PPI_FIELDS.includes(key) && value) { - data[key] = "[REDACTED]"; - } - } - } - - return data; -}; - -const configureScope = ( - scope: NodeSentry.Scope, - context: GraphQLRequestContextDidEncounterErrors, -) => { - scope.setTag("kind", context.operation!.operation); - scope.setExtra("query", context.request.query); - scope.setExtra("variables", removePIIs(context.request.variables)); -}; - -export const SentryPlugin = (isServerless: boolean): ApolloServerPlugin => { - const Sentry = isServerless ? ServerlessSentry : NodeSentry; - - return { - async requestDidStart() { - return { - async didEncounterErrors( - context: GraphQLRequestContextDidEncounterErrors, - ) { - if (!context.operation) { - return; - } - - for (const err of context.errors) { - if (shouldSkipError(err)) { - continue; - } - - Sentry.configureScope((scope) => { - configureScope(scope, context); - if (err.path) { - scope.addBreadcrumb({ - category: "query-path", - message: err.path.join(" > "), - }); - } - - // this allows to group errors more granularly based on the error message - scope.setFingerprint(["{{ default }}", err.message]); - Sentry.captureException(err); - }); - } - }, - }; - }, - }; -}; diff --git a/gateway/pnpm-lock.yaml b/gateway/pnpm-lock.yaml deleted file mode 100644 index 8ada4a608c..0000000000 --- a/gateway/pnpm-lock.yaml +++ /dev/null @@ -1,6599 +0,0 @@ -lockfileVersion: '6.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -overrides: - typescript: 4.6.3 - -dependencies: - '@apollo/federation': - specifier: ^0.38.1 - version: 0.38.1(graphql@16.8.1) - '@apollo/gateway': - specifier: 2.5.6 - version: 2.5.6(graphql@16.8.1) - '@sentry/integrations': - specifier: ^7.74.0 - version: 7.74.0 - '@sentry/node': - specifier: ^7.74.0 - version: 7.74.0 - '@sentry/serverless': - specifier: ^7.74.0 - version: 7.74.0 - '@sentry/tracing': - specifier: ^7.74.0 - version: 7.74.0 - apollo-server: - specifier: 3.12.1 - version: 3.12.1(graphql@16.8.1) - apollo-server-errors: - specifier: ^3.3.1 - version: 3.3.1(graphql@16.8.1) - apollo-server-lambda: - specifier: 3.12.1 - version: 3.12.1(graphql@16.8.1) - apollo-server-plugin-base: - specifier: ^3.7.2 - version: 3.7.2(graphql@16.8.1) - apollo-server-types: - specifier: ^3.8.0 - version: 3.8.0(graphql@16.8.1) - cookie: - specifier: ^0.5.0 - version: 0.5.0 - cors: - specifier: ^2.8.5 - version: 2.8.5 - dotenv: - specifier: ^16.3.1 - version: 16.3.1 - graphql: - specifier: ^16.8.1 - version: 16.8.1 - graphql-request: - specifier: ^6.1.0 - version: 6.1.0(graphql@16.8.1) - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 - typescript: - specifier: 4.6.3 - version: 4.6.3 - -devDependencies: - '@python-italia/eslint-config': - specifier: ^1.0.13 - version: 1.0.13(next@13.5.4) - '@trivago/prettier-plugin-sort-imports': - specifier: ^4.2.0 - version: 4.2.0(prettier@3.0.3) - '@types/cookie': - specifier: ^0.5.2 - version: 0.5.2 - '@types/jest': - specifier: ^29.5.5 - version: 29.5.5 - '@types/jsonwebtoken': - specifier: ^9.0.3 - version: 9.0.3 - '@types/node': - specifier: ^20.8.6 - version: 20.8.6 - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@20.8.6)(ts-node@10.9.1) - jest-extended: - specifier: ^4.0.2 - version: 4.0.2(jest@29.7.0) - nodemon: - specifier: 3.0.1 - version: 3.0.1 - prettier: - specifier: ^3.0.3 - version: 3.0.3 - ts-jest: - specifier: ^29.1.1 - version: 29.1.1(@babel/core@7.23.2)(jest@29.7.0)(typescript@4.6.3) - ts-node: - specifier: 10.9.1 - version: 10.9.1(@types/node@20.8.6)(typescript@4.6.3) - -packages: - - /@aashutoshrathi/word-wrap@1.2.6: - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} - dev: true - - /@ampproject/remapping@2.2.1: - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 - dev: true - - /@apollo/cache-control-types@1.0.3(graphql@16.8.1): - resolution: {integrity: sha512-F17/vCp7QVwom9eG7ToauIKdAxpSoadsJnqIfyryLFSkLSOEqu+eC5Z3N8OXcUVStuOMcNHlyraRsA6rRICu4g==} - peerDependencies: - graphql: 14.x || 15.x || 16.x - dependencies: - graphql: 16.8.1 - dev: false - - /@apollo/composition@2.5.6(graphql@16.8.1): - resolution: {integrity: sha512-px3xvqgb3tyh7PtRNSBm0I5U2wmI+KcJBoM4xIVMEA+9vk3Mc46U3yXc/fTSGd6EPQD90jXVC56fwdWw/+f7oA==} - engines: {node: '>=14.15.0'} - peerDependencies: - graphql: ^16.5.0 - dependencies: - '@apollo/federation-internals': 2.5.6(graphql@16.8.1) - '@apollo/query-graphs': 2.5.6(graphql@16.8.1) - graphql: 16.8.1 - dev: false - - /@apollo/federation-internals@2.5.6(graphql@16.8.1): - resolution: {integrity: sha512-ZNJdEcWfKXlFDRYtoCftApQBJl1/aDUBnBfFJeXeTXW4FVhyGotVkWbJUIBlCHMISGmQQDNfRay9Dcp7uIda9A==} - engines: {node: '>=14.15.0'} - peerDependencies: - graphql: ^16.5.0 - dependencies: - '@types/uuid': 9.0.5 - chalk: 4.1.2 - graphql: 16.8.1 - js-levenshtein: 1.1.6 - uuid: 9.0.1 - dev: false - - /@apollo/federation@0.38.1(graphql@16.8.1): - resolution: {integrity: sha512-miifyAEsFgiYKeM3lUHFH6+vKa2vm9dXKSyWVpX6oeJiPblFLe2/iByN3psZQO2sRdVqO1OKYrGXdgKc74XDKw==} - engines: {node: '>=12.13.0'} - deprecated: The @apollo/federation package is deprecated and will reach end-of-life September 22, 2023. It contains outdated utilities for both running subgraphs and composing supergraph schemas. Please migrate to the appropriate package for your use case (@apollo/subgraph or @apollo/composition). For more details, see our announcement blog post (https://www.apollographql.com/blog/announcement/backend/announcing-the-end-of-life-schedule-for-apollo-gateway-v0-x/) and documentation (https://www.apollographql.com/docs/federation/federation-2/backward-compatibility/#is-official-support-ending-for-apollogateway-v0x). - peerDependencies: - graphql: ^15.8.0 || ^16.0.0 - dependencies: - '@apollo/subgraph': 0.6.1(graphql@16.8.1) - apollo-server-types: 3.8.0(graphql@16.8.1) - graphql: 16.8.1 - lodash.xorby: 4.7.0 - transitivePeerDependencies: - - encoding - dev: false - - /@apollo/gateway@2.5.6(graphql@16.8.1): - resolution: {integrity: sha512-zec9agl5UOjVM+bSGvldHlyx0OjFu6Mt8i/IfPi4fkH+8LJYzbshDTOQK2rDtPfbJoJpmgpCSO8XPzWZVOGvog==} - engines: {node: '>=14.15.0'} - peerDependencies: - graphql: ^16.5.0 - dependencies: - '@apollo/composition': 2.5.6(graphql@16.8.1) - '@apollo/federation-internals': 2.5.6(graphql@16.8.1) - '@apollo/query-planner': 2.5.6(graphql@16.8.1) - '@apollo/server-gateway-interface': 1.1.1(graphql@16.8.1) - '@apollo/usage-reporting-protobuf': 4.1.1 - '@apollo/utils.createhash': 2.0.1 - '@apollo/utils.fetcher': 2.0.1 - '@apollo/utils.isnodelike': 2.0.1 - '@apollo/utils.keyvaluecache': 2.1.1 - '@apollo/utils.logger': 2.0.1 - '@josephg/resolvable': 1.0.1 - '@opentelemetry/api': 1.6.0 - '@types/node-fetch': 2.6.6 - async-retry: 1.3.3 - graphql: 16.8.1 - loglevel: 1.8.1 - make-fetch-happen: 11.1.1 - node-abort-controller: 3.1.1 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /@apollo/protobufjs@1.2.6: - resolution: {integrity: sha512-Wqo1oSHNUj/jxmsVp4iR3I480p6qdqHikn38lKrFhfzcDJ7lwd7Ck7cHRl4JE81tWNArl77xhnG/OkZhxKBYOw==} - hasBin: true - requiresBuild: true - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 - '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/long': 4.0.2 - '@types/node': 10.17.60 - long: 4.0.0 - dev: false - - /@apollo/protobufjs@1.2.7: - resolution: {integrity: sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==} - hasBin: true - requiresBuild: true - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 - '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/long': 4.0.2 - long: 4.0.0 - dev: false - - /@apollo/query-graphs@2.5.6(graphql@16.8.1): - resolution: {integrity: sha512-W0Raf+hL93XgF3gNXE0eij1m7DGv1zfxsGEUG/GAuBtasOH3YMN5b4HZZm11IDCrQ45OltrnZtAZX8c6UqskrA==} - engines: {node: '>=14.15.0'} - peerDependencies: - graphql: ^16.5.0 - dependencies: - '@apollo/federation-internals': 2.5.6(graphql@16.8.1) - deep-equal: 2.2.2 - graphql: 16.8.1 - ts-graphviz: 1.8.1 - uuid: 9.0.1 - dev: false - - /@apollo/query-planner@2.5.6(graphql@16.8.1): - resolution: {integrity: sha512-7qrQl4/s1FOK0TZaa+0WwL17Us9Oy1I8bfpfjf+3xp8NtgOnFijXzJ32IOdjhW+mkt5EC4LrkBUWvygCULuMyQ==} - engines: {node: '>=14.15.0'} - peerDependencies: - graphql: ^16.5.0 - dependencies: - '@apollo/federation-internals': 2.5.6(graphql@16.8.1) - '@apollo/query-graphs': 2.5.6(graphql@16.8.1) - '@apollo/utils.keyvaluecache': 2.1.1 - chalk: 4.1.2 - deep-equal: 2.2.2 - graphql: 16.8.1 - pretty-format: 29.7.0 - dev: false - - /@apollo/server-gateway-interface@1.1.1(graphql@16.8.1): - resolution: {integrity: sha512-pGwCl/po6+rxRmDMFgozKQo2pbsSwE91TpsDBAOgf74CRDPXHHtM88wbwjab0wMMZh95QfR45GGyDIdhY24bkQ==} - peerDependencies: - graphql: 14.x || 15.x || 16.x - dependencies: - '@apollo/usage-reporting-protobuf': 4.1.1 - '@apollo/utils.fetcher': 2.0.1 - '@apollo/utils.keyvaluecache': 2.1.1 - '@apollo/utils.logger': 2.0.1 - graphql: 16.8.1 - dev: false - - /@apollo/subgraph@0.6.1(graphql@16.8.1): - resolution: {integrity: sha512-w/6FoubSxuzXSx8uvLE1wEuHZVHRXFyfHPKdM76wX5U/xw82zlUKseVO7wTuVODTcnUzEA30udYeCApUoC3/Xw==} - engines: {node: '>=12.13.0'} - deprecated: All v0.x versions of @apollo/subgraph are now deprecated with an end-of-life date of September 22, 2023. Apollo recommends upgrading to v2.x as soon as possible. For more details, see our announcement blog post (https://www.apollographql.com/blog/announcement/backend/announcing-the-end-of-life-schedule-for-apollo-gateway-v0-x/) and documentation (https://www.apollographql.com/docs/federation/federation-2/backward-compatibility/#is-official-support-ending-for-apollogateway-v0x). - peerDependencies: - graphql: ^15.8.0 || ^16.0.0 - dependencies: - '@apollo/cache-control-types': 1.0.3(graphql@16.8.1) - graphql: 16.8.1 - dev: false - - /@apollo/usage-reporting-protobuf@4.1.1: - resolution: {integrity: sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==} - dependencies: - '@apollo/protobufjs': 1.2.7 - dev: false - - /@apollo/utils.createhash@2.0.1: - resolution: {integrity: sha512-fQO4/ZOP8LcXWvMNhKiee+2KuKyqIcfHrICA+M4lj/h/Lh1H10ICcUtk6N/chnEo5HXu0yejg64wshdaiFitJg==} - engines: {node: '>=14'} - dependencies: - '@apollo/utils.isnodelike': 2.0.1 - sha.js: 2.4.11 - dev: false - - /@apollo/utils.dropunuseddefinitions@1.1.0(graphql@16.8.1): - resolution: {integrity: sha512-jU1XjMr6ec9pPoL+BFWzEPW7VHHulVdGKMkPAMiCigpVIT11VmCbnij0bWob8uS3ODJ65tZLYKAh/55vLw2rbg==} - engines: {node: '>=12.13.0'} - peerDependencies: - graphql: 14.x || 15.x || 16.x - dependencies: - graphql: 16.8.1 - dev: false - - /@apollo/utils.fetcher@2.0.1: - resolution: {integrity: sha512-jvvon885hEyWXd4H6zpWeN3tl88QcWnHp5gWF5OPF34uhvoR+DFqcNxs9vrRaBBSY3qda3Qe0bdud7tz2zGx1A==} - engines: {node: '>=14'} - dev: false - - /@apollo/utils.isnodelike@2.0.1: - resolution: {integrity: sha512-w41XyepR+jBEuVpoRM715N2ZD0xMD413UiJx8w5xnAZD2ZkSJnMJBoIzauK83kJpSgNuR6ywbV29jG9NmxjK0Q==} - engines: {node: '>=14'} - dev: false - - /@apollo/utils.keyvaluecache@1.0.2: - resolution: {integrity: sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==} - dependencies: - '@apollo/utils.logger': 1.0.1 - lru-cache: 7.13.1 - dev: false - - /@apollo/utils.keyvaluecache@2.1.1: - resolution: {integrity: sha512-qVo5PvUUMD8oB9oYvq4ViCjYAMWnZ5zZwEjNF37L2m1u528x5mueMlU+Cr1UinupCgdB78g+egA1G98rbJ03Vw==} - engines: {node: '>=14'} - dependencies: - '@apollo/utils.logger': 2.0.1 - lru-cache: 7.18.3 - dev: false - - /@apollo/utils.logger@1.0.1: - resolution: {integrity: sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==} - dev: false - - /@apollo/utils.logger@2.0.1: - resolution: {integrity: sha512-YuplwLHaHf1oviidB7MxnCXAdHp3IqYV8n0momZ3JfLniae92eYqMIx+j5qJFX6WKJPs6q7bczmV4lXIsTu5Pg==} - engines: {node: '>=14'} - dev: false - - /@apollo/utils.printwithreducedwhitespace@1.1.0(graphql@16.8.1): - resolution: {integrity: sha512-GfFSkAv3n1toDZ4V6u2d7L4xMwLA+lv+6hqXicMN9KELSJ9yy9RzuEXaX73c/Ry+GzRsBy/fdSUGayGqdHfT2Q==} - engines: {node: '>=12.13.0'} - peerDependencies: - graphql: 14.x || 15.x || 16.x - dependencies: - graphql: 16.8.1 - dev: false - - /@apollo/utils.removealiases@1.0.0(graphql@16.8.1): - resolution: {integrity: sha512-6cM8sEOJW2LaGjL/0vHV0GtRaSekrPQR4DiywaApQlL9EdROASZU5PsQibe2MWeZCOhNrPRuHh4wDMwPsWTn8A==} - engines: {node: '>=12.13.0'} - peerDependencies: - graphql: 14.x || 15.x || 16.x - dependencies: - graphql: 16.8.1 - dev: false - - /@apollo/utils.sortast@1.1.0(graphql@16.8.1): - resolution: {integrity: sha512-VPlTsmUnOwzPK5yGZENN069y6uUHgeiSlpEhRnLFYwYNoJHsuJq2vXVwIaSmts015WTPa2fpz1inkLYByeuRQA==} - engines: {node: '>=12.13.0'} - peerDependencies: - graphql: 14.x || 15.x || 16.x - dependencies: - graphql: 16.8.1 - lodash.sortby: 4.7.0 - dev: false - - /@apollo/utils.stripsensitiveliterals@1.2.0(graphql@16.8.1): - resolution: {integrity: sha512-E41rDUzkz/cdikM5147d8nfCFVKovXxKBcjvLEQ7bjZm/cg9zEcXvS6vFY8ugTubI3fn6zoqo0CyU8zT+BGP9w==} - engines: {node: '>=12.13.0'} - peerDependencies: - graphql: 14.x || 15.x || 16.x - dependencies: - graphql: 16.8.1 - dev: false - - /@apollo/utils.usagereporting@1.0.1(graphql@16.8.1): - resolution: {integrity: sha512-6dk+0hZlnDbahDBB2mP/PZ5ybrtCJdLMbeNJD+TJpKyZmSY6bA3SjI8Cr2EM9QA+AdziywuWg+SgbWUF3/zQqQ==} - engines: {node: '>=12.13.0'} - peerDependencies: - graphql: 14.x || 15.x || 16.x - dependencies: - '@apollo/usage-reporting-protobuf': 4.1.1 - '@apollo/utils.dropunuseddefinitions': 1.1.0(graphql@16.8.1) - '@apollo/utils.printwithreducedwhitespace': 1.1.0(graphql@16.8.1) - '@apollo/utils.removealiases': 1.0.0(graphql@16.8.1) - '@apollo/utils.sortast': 1.1.0(graphql@16.8.1) - '@apollo/utils.stripsensitiveliterals': 1.2.0(graphql@16.8.1) - graphql: 16.8.1 - dev: false - - /@apollographql/apollo-tools@0.5.4(graphql@16.8.1): - resolution: {integrity: sha512-shM3q7rUbNyXVVRkQJQseXv6bnYM3BUma/eZhwXR4xsuM+bqWnJKvW7SAfRjP7LuSCocrexa5AXhjjawNHrIlw==} - engines: {node: '>=8', npm: '>=6'} - peerDependencies: - graphql: ^14.2.1 || ^15.0.0 || ^16.0.0 - dependencies: - graphql: 16.8.1 - dev: false - - /@apollographql/graphql-playground-html@1.6.29: - resolution: {integrity: sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==} - dependencies: - xss: 1.0.14 - dev: false - - /@babel/code-frame@7.12.11: - resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} - dependencies: - '@babel/highlight': 7.22.20 - dev: true - - /@babel/code-frame@7.22.13: - resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.22.20 - chalk: 2.4.2 - dev: true - - /@babel/compat-data@7.23.2: - resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/core@7.23.2: - resolution: {integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.22.13 - '@babel/generator': 7.23.0 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) - '@babel/helpers': 7.23.2 - '@babel/parser': 7.23.0 - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 - convert-source-map: 2.0.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/generator@7.17.7: - resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.17.0 - jsesc: 2.5.2 - source-map: 0.5.7 - dev: true - - /@babel/generator@7.23.0: - resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.0 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 - jsesc: 2.5.2 - dev: true - - /@babel/helper-compilation-targets@7.22.15: - resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/compat-data': 7.23.2 - '@babel/helper-validator-option': 7.22.15 - browserslist: 4.22.1 - lru-cache: 5.1.1 - semver: 6.3.1 - dev: true - - /@babel/helper-environment-visitor@7.22.20: - resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-function-name@7.23.0: - resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.0 - dev: true - - /@babel/helper-hoist-variables@7.22.5: - resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.0 - dev: true - - /@babel/helper-module-imports@7.22.15: - resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.0 - dev: true - - /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.2): - resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - dev: true - - /@babel/helper-plugin-utils@7.22.5: - resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-simple-access@7.22.5: - resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.0 - dev: true - - /@babel/helper-split-export-declaration@7.22.6: - resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.0 - dev: true - - /@babel/helper-string-parser@7.22.5: - resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-identifier@7.22.20: - resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-option@7.22.15: - resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helpers@7.23.2: - resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/highlight@7.22.20: - resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: true - - /@babel/parser@7.23.0: - resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.17.0 - dev: true - - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.2): - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.2): - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.2): - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.2): - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.2): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.2): - resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.2): - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.2): - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.2): - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.2): - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.2): - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.2): - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.2): - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.2): - resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/runtime-corejs3@7.23.2: - resolution: {integrity: sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==} - engines: {node: '>=6.9.0'} - dependencies: - core-js-pure: 3.33.0 - regenerator-runtime: 0.14.0 - dev: true - - /@babel/runtime@7.23.2: - resolution: {integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.0 - dev: true - - /@babel/template@7.22.15: - resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.22.13 - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 - dev: true - - /@babel/traverse@7.17.3: - resolution: {integrity: sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.22.13 - '@babel/generator': 7.17.7 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.0 - '@babel/types': 7.17.0 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/traverse@7.23.2: - resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.22.13 - '@babel/generator': 7.23.0 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/types@7.17.0: - resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 - dev: true - - /@babel/types@7.23.0: - resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 - dev: true - - /@bcoe/v8-coverage@0.2.3: - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - dev: true - - /@cspotcode/source-map-support@0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - dev: true - - /@eslint/eslintrc@0.4.3: - resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 7.3.1 - globals: 13.23.0 - ignore: 4.0.6 - import-fresh: 3.3.0 - js-yaml: 3.14.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@graphql-tools/merge@8.3.1(graphql@16.8.1): - resolution: {integrity: sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - '@graphql-tools/utils': 8.9.0(graphql@16.8.1) - graphql: 16.8.1 - tslib: 2.6.2 - dev: false - - /@graphql-tools/merge@8.4.2(graphql@16.8.1): - resolution: {integrity: sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - '@graphql-tools/utils': 9.2.1(graphql@16.8.1) - graphql: 16.8.1 - tslib: 2.6.2 - dev: false - - /@graphql-tools/mock@8.7.20(graphql@16.8.1): - resolution: {integrity: sha512-ljcHSJWjC/ZyzpXd5cfNhPI7YljRVvabKHPzKjEs5ElxWu2cdlLGvyNYepApXDsM/OJG/2xuhGM+9GWu5gEAPQ==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - '@graphql-tools/schema': 9.0.19(graphql@16.8.1) - '@graphql-tools/utils': 9.2.1(graphql@16.8.1) - fast-json-stable-stringify: 2.1.0 - graphql: 16.8.1 - tslib: 2.6.2 - dev: false - - /@graphql-tools/schema@8.5.1(graphql@16.8.1): - resolution: {integrity: sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - '@graphql-tools/merge': 8.3.1(graphql@16.8.1) - '@graphql-tools/utils': 8.9.0(graphql@16.8.1) - graphql: 16.8.1 - tslib: 2.6.2 - value-or-promise: 1.0.11 - dev: false - - /@graphql-tools/schema@9.0.19(graphql@16.8.1): - resolution: {integrity: sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - '@graphql-tools/merge': 8.4.2(graphql@16.8.1) - '@graphql-tools/utils': 9.2.1(graphql@16.8.1) - graphql: 16.8.1 - tslib: 2.6.2 - value-or-promise: 1.0.12 - dev: false - - /@graphql-tools/utils@8.9.0(graphql@16.8.1): - resolution: {integrity: sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - graphql: 16.8.1 - tslib: 2.6.2 - dev: false - - /@graphql-tools/utils@9.2.1(graphql@16.8.1): - resolution: {integrity: sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) - graphql: 16.8.1 - tslib: 2.6.2 - dev: false - - /@graphql-typed-document-node/core@3.2.0(graphql@16.8.1): - resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - graphql: 16.8.1 - dev: false - - /@humanwhocodes/config-array@0.5.0: - resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - dev: true - - /@isaacs/cliui@8.0.2: - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - dependencies: - string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: false - - /@istanbuljs/load-nyc-config@1.1.0: - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - dev: true - - /@istanbuljs/schema@0.1.3: - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - dev: true - - /@jest/console@29.7.0: - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - dev: true - - /@jest/core@29.7.0(ts-node@10.9.1): - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.8.6)(ts-node@10.9.1) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /@jest/environment@29.7.0: - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - jest-mock: 29.7.0 - dev: true - - /@jest/expect-utils@29.7.0: - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.6.3 - dev: true - - /@jest/expect@29.7.0: - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/fake-timers@29.7.0: - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.8.6 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - dev: true - - /@jest/globals@29.7.0: - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/reporters@29.7.0: - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.19 - '@types/node': 20.8.6 - chalk: 4.1.2 - collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 6.0.1 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.6 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.1.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.27.8 - - /@jest/source-map@29.6.3: - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.19 - callsites: 3.1.0 - graceful-fs: 4.2.11 - dev: true - - /@jest/test-result@29.7.0: - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.2 - dev: true - - /@jest/test-sequencer@29.7.0: - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - dev: true - - /@jest/transform@29.7.0: - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.23.2 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.19 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.5 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/types@26.6.2: - resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} - engines: {node: '>= 10.14.2'} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.2 - '@types/node': 20.8.6 - '@types/yargs': 15.0.16 - chalk: 4.1.2 - dev: true - - /@jest/types@29.6.3: - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.2 - '@types/node': 20.8.6 - '@types/yargs': 17.0.28 - chalk: 4.1.2 - dev: true - - /@josephg/resolvable@1.0.1: - resolution: {integrity: sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==} - dev: false - - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.19 - dev: true - - /@jridgewell/resolve-uri@3.1.1: - resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} - engines: {node: '>=6.0.0'} - dev: true - - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} - dev: true - - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true - - /@jridgewell/trace-mapping@0.3.19: - resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} - dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /@next/env@13.5.4: - resolution: {integrity: sha512-LGegJkMvRNw90WWphGJ3RMHMVplYcOfRWf2Be3td3sUa+1AaxmsYyANsA+znrGCBjXJNi4XAQlSoEfUxs/4kIQ==} - dev: true - - /@next/eslint-plugin-next@11.1.4: - resolution: {integrity: sha512-E0iM++lWF2uOEBSRWSIg9sl3xXWvYSGP6tvUVKdeNNIiEAWwcZzs0OqxeO7Xq3BZ5XkQREEGiAUkzfCqDze5TQ==} - dependencies: - glob: 7.1.7 - dev: true - - /@next/swc-darwin-arm64@13.5.4: - resolution: {integrity: sha512-Df8SHuXgF1p+aonBMcDPEsaahNo2TCwuie7VXED4FVyECvdXfRT9unapm54NssV9tF3OQFKBFOdlje4T43VO0w==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@next/swc-darwin-x64@13.5.4: - resolution: {integrity: sha512-siPuUwO45PnNRMeZnSa8n/Lye5ZX93IJom9wQRB5DEOdFrw0JjOMu1GINB8jAEdwa7Vdyn1oJ2xGNaQpdQQ9Pw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@next/swc-linux-arm64-gnu@13.5.4: - resolution: {integrity: sha512-l/k/fvRP/zmB2jkFMfefmFkyZbDkYW0mRM/LB+tH5u9pB98WsHXC0WvDHlGCYp3CH/jlkJPL7gN8nkTQVrQ/2w==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@next/swc-linux-arm64-musl@13.5.4: - resolution: {integrity: sha512-YYGb7SlLkI+XqfQa8VPErljb7k9nUnhhRrVaOdfJNCaQnHBcvbT7cx/UjDQLdleJcfyg1Hkn5YSSIeVfjgmkTg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@next/swc-linux-x64-gnu@13.5.4: - resolution: {integrity: sha512-uE61vyUSClnCH18YHjA8tE1prr/PBFlBFhxBZis4XBRJoR+txAky5d7gGNUIbQ8sZZ7LVkSVgm/5Fc7mwXmRAg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@next/swc-linux-x64-musl@13.5.4: - resolution: {integrity: sha512-qVEKFYML/GvJSy9CfYqAdUexA6M5AklYcQCW+8JECmkQHGoPxCf04iMh7CPR7wkHyWWK+XLt4Ja7hhsPJtSnhg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@next/swc-win32-arm64-msvc@13.5.4: - resolution: {integrity: sha512-mDSQfqxAlfpeZOLPxLymZkX0hYF3juN57W6vFHTvwKlnHfmh12Pt7hPIRLYIShk8uYRsKPtMTth/EzpwRI+u8w==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@next/swc-win32-ia32-msvc@13.5.4: - resolution: {integrity: sha512-aoqAT2XIekIWoriwzOmGFAvTtVY5O7JjV21giozBTP5c6uZhpvTWRbmHXbmsjZqY4HnEZQRXWkSAppsIBweKqw==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@next/swc-win32-x64-msvc@13.5.4: - resolution: {integrity: sha512-cyRvlAxwlddlqeB9xtPSfNSCRy8BOa4wtMo0IuI9P7Y0XT2qpDrpFKRyZ7kUngZis59mPVla5k8X1oOJ8RxDYg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - dev: true - - /@nodelib/fs.stat@2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - dev: true - - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.15.0 - dev: true - - /@npmcli/fs@3.1.0: - resolution: {integrity: sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - semver: 7.5.4 - dev: false - - /@opentelemetry/api@1.6.0: - resolution: {integrity: sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g==} - engines: {node: '>=8.0.0'} - dev: false - - /@pkgjs/parseargs@0.11.0: - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - requiresBuild: true - dev: false - optional: true - - /@protobufjs/aspromise@1.1.2: - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - dev: false - - /@protobufjs/base64@1.1.2: - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - dev: false - - /@protobufjs/codegen@2.0.4: - resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} - dev: false - - /@protobufjs/eventemitter@1.1.0: - resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} - dev: false - - /@protobufjs/fetch@1.1.0: - resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 - dev: false - - /@protobufjs/float@1.0.2: - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - dev: false - - /@protobufjs/inquire@1.1.0: - resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - dev: false - - /@protobufjs/path@1.1.2: - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - dev: false - - /@protobufjs/pool@1.1.0: - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - dev: false - - /@protobufjs/utf8@1.1.0: - resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - dev: false - - /@python-italia/eslint-config@1.0.13(next@13.5.4): - resolution: {integrity: sha512-5H+vykXms8OnVBs4nn6Yu5n1+/EfmJObIhVneMLbIwBPvzsGs6rtnSw3Fnu0e+rZip8BYj2l7QUBn82Ohvxo2Q==} - dependencies: - '@typescript-eslint/eslint-plugin': 4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@4.6.3) - '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@4.6.3) - eslint: 7.32.0 - eslint-config-next: 11.1.4(eslint@7.32.0)(next@13.5.4)(typescript@4.6.3) - eslint-config-prettier: 8.10.0(eslint@7.32.0) - eslint-plugin-jest-dom: 3.9.4(eslint@7.32.0) - eslint-plugin-simple-import-sort: 7.0.0(eslint@7.32.0) - eslint-plugin-testing-library: 4.12.4(eslint@7.32.0)(typescript@4.6.3) - typescript: 4.6.3 - transitivePeerDependencies: - - eslint-import-resolver-webpack - - next - - supports-color - dev: true - - /@rushstack/eslint-patch@1.5.1: - resolution: {integrity: sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==} - dev: true - - /@sentry-internal/tracing@7.74.0: - resolution: {integrity: sha512-JK6IRGgdtZjswGfaGIHNWIThffhOHzVIIaGmglui+VFIzOsOqePjoxaDV0MEvzafxXZD7eWqGE5RGuZ0n6HFVg==} - engines: {node: '>=8'} - dependencies: - '@sentry/core': 7.74.0 - '@sentry/types': 7.74.0 - '@sentry/utils': 7.74.0 - tslib: 2.6.2 - dev: false - - /@sentry/core@7.74.0: - resolution: {integrity: sha512-83NRuqn7nDZkSVBN5yJQqcpXDG4yMYiB7TkYUKrGTzBpRy6KUOrkCdybuKk0oraTIGiGSe5WEwCFySiNgR9FzA==} - engines: {node: '>=8'} - dependencies: - '@sentry/types': 7.74.0 - '@sentry/utils': 7.74.0 - tslib: 2.6.2 - dev: false - - /@sentry/integrations@7.74.0: - resolution: {integrity: sha512-O4UyxiV5wzXSDnEd9Z/SIt/5M12URWNtIJPPJjowlllzw8X9e3zBcnXmjMOLZ+mZWjQmRDjOoz3lPPQ17f7fvw==} - engines: {node: '>=8'} - dependencies: - '@sentry/core': 7.74.0 - '@sentry/types': 7.74.0 - '@sentry/utils': 7.74.0 - localforage: 1.10.0 - tslib: 2.6.2 - dev: false - - /@sentry/node@7.74.0: - resolution: {integrity: sha512-uBmW2/z0cz/WFIG74ZF7lSipO0XNzMf9yrdqnZXnGDYsUZE4I4QiqDN0hNi6fkTgf9MYRC8uFem2OkAvyPJ74Q==} - engines: {node: '>=8'} - dependencies: - '@sentry-internal/tracing': 7.74.0 - '@sentry/core': 7.74.0 - '@sentry/types': 7.74.0 - '@sentry/utils': 7.74.0 - cookie: 0.5.0 - https-proxy-agent: 5.0.1 - lru_map: 0.3.3 - tslib: 2.6.2 - transitivePeerDependencies: - - supports-color - dev: false - - /@sentry/serverless@7.74.0: - resolution: {integrity: sha512-qYCOpCgCTbLzffFsn8jBmqBV4SUhbMotIWxgsnsiyga/kNvwR8dXf5chYjJ4Z3/HrSwTLeCIqOmuXGt9WOwgxg==} - engines: {node: '>=10'} - dependencies: - '@sentry/core': 7.74.0 - '@sentry/node': 7.74.0 - '@sentry/types': 7.74.0 - '@sentry/utils': 7.74.0 - '@types/aws-lambda': 8.10.124 - '@types/express': 4.17.19 - tslib: 2.6.2 - transitivePeerDependencies: - - supports-color - dev: false - - /@sentry/tracing@7.74.0: - resolution: {integrity: sha512-rSFJADhh3J3zmkzJ1EXCOwS3h7F6o/lSKu7CWZSZ6k5kBvbCJ5AXvGQadhPdWPJMMcPFzCJaOyTKEPcwL4tbCw==} - engines: {node: '>=8'} - dependencies: - '@sentry-internal/tracing': 7.74.0 - dev: false - - /@sentry/types@7.74.0: - resolution: {integrity: sha512-rI5eIRbUycWjn6s6o3yAjjWtIvYSxZDdnKv5je2EZINfLKcMPj1dkl6wQd2F4y7gLfD/N6Y0wZYIXC3DUdJQQg==} - engines: {node: '>=8'} - dev: false - - /@sentry/utils@7.74.0: - resolution: {integrity: sha512-k3np8nuTPtx5KDODPtULfFln4UXdE56MZCcF19Jv6Ljxf+YN/Ady1+0Oi3e0XoSvFpWNyWnglauT7M65qCE6kg==} - engines: {node: '>=8'} - dependencies: - '@sentry/types': 7.74.0 - tslib: 2.6.2 - dev: false - - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - - /@sinonjs/commons@3.0.0: - resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} - dependencies: - type-detect: 4.0.8 - dev: true - - /@sinonjs/fake-timers@10.3.0: - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - dependencies: - '@sinonjs/commons': 3.0.0 - dev: true - - /@swc/helpers@0.5.2: - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} - dependencies: - tslib: 2.6.2 - dev: true - - /@testing-library/dom@7.31.2: - resolution: {integrity: sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==} - engines: {node: '>=10'} - dependencies: - '@babel/code-frame': 7.22.13 - '@babel/runtime': 7.23.2 - '@types/aria-query': 4.2.2 - aria-query: 4.2.2 - chalk: 4.1.2 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - pretty-format: 26.6.2 - dev: true - - /@tootallnate/once@2.0.0: - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - dev: false - - /@trivago/prettier-plugin-sort-imports@4.2.0(prettier@3.0.3): - resolution: {integrity: sha512-YBepjbt+ZNBVmN3ev1amQH3lWCmHyt5qTbLCp/syXJRu/Kw2koXh44qayB1gMRxcL/gV8egmjN5xWSrYyfUtyw==} - peerDependencies: - '@vue/compiler-sfc': 3.x - prettier: 2.x - 3.x - peerDependenciesMeta: - '@vue/compiler-sfc': - optional: true - dependencies: - '@babel/generator': 7.17.7 - '@babel/parser': 7.23.0 - '@babel/traverse': 7.17.3 - '@babel/types': 7.17.0 - javascript-natural-sort: 0.7.1 - lodash: 4.17.21 - prettier: 3.0.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@tsconfig/node10@1.0.9: - resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} - dev: true - - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true - - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true - - /@tsconfig/node16@1.0.4: - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true - - /@types/accepts@1.3.5: - resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==} - dependencies: - '@types/node': 20.8.6 - dev: false - - /@types/aria-query@4.2.2: - resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} - dev: true - - /@types/aws-lambda@8.10.124: - resolution: {integrity: sha512-PHqK0SuAkFS3tZjceqRXecxxrWIN3VqTicuialtK2wZmvBy7H9WGc3u3+wOgaZB7N8SpSXDpWk9qa7eorpTStg==} - dev: false - - /@types/babel__core@7.20.2: - resolution: {integrity: sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==} - dependencies: - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 - '@types/babel__generator': 7.6.5 - '@types/babel__template': 7.4.2 - '@types/babel__traverse': 7.20.2 - dev: true - - /@types/babel__generator@7.6.5: - resolution: {integrity: sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==} - dependencies: - '@babel/types': 7.23.0 - dev: true - - /@types/babel__template@7.4.2: - resolution: {integrity: sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==} - dependencies: - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 - dev: true - - /@types/babel__traverse@7.20.2: - resolution: {integrity: sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==} - dependencies: - '@babel/types': 7.23.0 - dev: true - - /@types/body-parser@1.19.2: - resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} - dependencies: - '@types/connect': 3.4.36 - '@types/node': 20.8.6 - dev: false - - /@types/body-parser@1.19.3: - resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==} - dependencies: - '@types/connect': 3.4.36 - '@types/node': 20.8.6 - dev: false - - /@types/connect@3.4.36: - resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} - dependencies: - '@types/node': 20.8.6 - dev: false - - /@types/cookie@0.5.2: - resolution: {integrity: sha512-DBpRoJGKJZn7RY92dPrgoMew8xCWc2P71beqsjyhEI/Ds9mOyVmBwtekyfhpwFIVt1WrxTonFifiOZ62V8CnNA==} - dev: true - - /@types/cors@2.8.12: - resolution: {integrity: sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==} - dev: false - - /@types/express-serve-static-core@4.17.31: - resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} - dependencies: - '@types/node': 20.8.6 - '@types/qs': 6.9.8 - '@types/range-parser': 1.2.5 - dev: false - - /@types/express-serve-static-core@4.17.37: - resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} - dependencies: - '@types/node': 20.8.6 - '@types/qs': 6.9.8 - '@types/range-parser': 1.2.5 - '@types/send': 0.17.2 - dev: false - - /@types/express@4.17.14: - resolution: {integrity: sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==} - dependencies: - '@types/body-parser': 1.19.3 - '@types/express-serve-static-core': 4.17.37 - '@types/qs': 6.9.8 - '@types/serve-static': 1.15.3 - dev: false - - /@types/express@4.17.19: - resolution: {integrity: sha512-UtOfBtzN9OvpZPPbnnYunfjM7XCI4jyk1NvnFhTVz5krYAnW4o5DCoIekvms+8ApqhB4+9wSge1kBijdfTSmfg==} - dependencies: - '@types/body-parser': 1.19.3 - '@types/express-serve-static-core': 4.17.37 - '@types/qs': 6.9.8 - '@types/serve-static': 1.15.3 - dev: false - - /@types/graceful-fs@4.1.7: - resolution: {integrity: sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==} - dependencies: - '@types/node': 20.8.6 - dev: true - - /@types/http-errors@2.0.2: - resolution: {integrity: sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==} - dev: false - - /@types/istanbul-lib-coverage@2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} - dev: true - - /@types/istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - dev: true - - /@types/istanbul-reports@3.0.2: - resolution: {integrity: sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==} - dependencies: - '@types/istanbul-lib-report': 3.0.1 - dev: true - - /@types/jest@29.5.5: - resolution: {integrity: sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==} - dependencies: - expect: 29.7.0 - pretty-format: 29.7.0 - dev: true - - /@types/json-schema@7.0.13: - resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==} - dev: true - - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: true - - /@types/jsonwebtoken@9.0.3: - resolution: {integrity: sha512-b0jGiOgHtZ2jqdPgPnP6WLCXZk1T8p06A/vPGzUvxpFGgKMbjXJDjC5m52ErqBnIuWZFgGoIJyRdeG5AyreJjA==} - dependencies: - '@types/node': 20.8.6 - dev: true - - /@types/long@4.0.2: - resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} - dev: false - - /@types/mime@1.3.3: - resolution: {integrity: sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==} - dev: false - - /@types/mime@3.0.2: - resolution: {integrity: sha512-Wj+fqpTLtTbG7c0tH47dkahefpLKEbB+xAZuLq7b4/IDHPl/n6VoXcyUQ2bypFlbSwvCr0y+bD4euTTqTJsPxQ==} - dev: false - - /@types/node-fetch@2.6.6: - resolution: {integrity: sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw==} - dependencies: - '@types/node': 20.8.6 - form-data: 4.0.0 - dev: false - - /@types/node@10.17.60: - resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} - dev: false - - /@types/node@20.8.6: - resolution: {integrity: sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==} - dependencies: - undici-types: 5.25.3 - - /@types/qs@6.9.8: - resolution: {integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==} - dev: false - - /@types/range-parser@1.2.5: - resolution: {integrity: sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==} - dev: false - - /@types/send@0.17.2: - resolution: {integrity: sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==} - dependencies: - '@types/mime': 1.3.3 - '@types/node': 20.8.6 - dev: false - - /@types/serve-static@1.15.3: - resolution: {integrity: sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==} - dependencies: - '@types/http-errors': 2.0.2 - '@types/mime': 3.0.2 - '@types/node': 20.8.6 - dev: false - - /@types/stack-utils@2.0.1: - resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} - dev: true - - /@types/uuid@9.0.5: - resolution: {integrity: sha512-xfHdwa1FMJ082prjSJpoEI57GZITiQz10r3vEJCHa2khEFQjKy91aWKz6+zybzssCvXUwE1LQWgWVwZ4nYUvHQ==} - dev: false - - /@types/yargs-parser@21.0.1: - resolution: {integrity: sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==} - dev: true - - /@types/yargs@15.0.16: - resolution: {integrity: sha512-2FeD5qezW3FvLpZ0JpfuaEWepgNLl9b2gQYiz/ce0NhoB1W/D+VZu98phITXkADYerfr/jb7JcDcVhITsc9bwg==} - dependencies: - '@types/yargs-parser': 21.0.1 - dev: true - - /@types/yargs@17.0.28: - resolution: {integrity: sha512-N3e3fkS86hNhtk6BEnc0rj3zcehaxx8QWhCROJkqpl5Zaoi7nAic3jH8q94jVD3zu5LGk+PUB6KAiDmimYOEQw==} - dependencies: - '@types/yargs-parser': 21.0.1 - dev: true - - /@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@4.6.3): - resolution: {integrity: sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==} - engines: {node: ^10.12.0 || >=12.0.0} - peerDependencies: - '@typescript-eslint/parser': ^4.0.0 - eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/experimental-utils': 4.33.0(eslint@7.32.0)(typescript@4.6.3) - '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@4.6.3) - '@typescript-eslint/scope-manager': 4.33.0 - debug: 4.3.4 - eslint: 7.32.0 - functional-red-black-tree: 1.0.1 - ignore: 5.2.4 - regexpp: 3.2.0 - semver: 7.5.4 - tsutils: 3.21.0(typescript@4.6.3) - typescript: 4.6.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/experimental-utils@4.33.0(eslint@7.32.0)(typescript@4.6.3): - resolution: {integrity: sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==} - engines: {node: ^10.12.0 || >=12.0.0} - peerDependencies: - eslint: '*' - dependencies: - '@types/json-schema': 7.0.13 - '@typescript-eslint/scope-manager': 4.33.0 - '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/typescript-estree': 4.33.0(typescript@4.6.3) - eslint: 7.32.0 - eslint-scope: 5.1.1 - eslint-utils: 3.0.0(eslint@7.32.0) - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@4.6.3): - resolution: {integrity: sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==} - engines: {node: ^10.12.0 || >=12.0.0} - peerDependencies: - eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 4.33.0 - '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/typescript-estree': 4.33.0(typescript@4.6.3) - debug: 4.3.4 - eslint: 7.32.0 - typescript: 4.6.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/scope-manager@4.33.0: - resolution: {integrity: sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==} - engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} - dependencies: - '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/visitor-keys': 4.33.0 - dev: true - - /@typescript-eslint/types@4.33.0: - resolution: {integrity: sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==} - engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} - dev: true - - /@typescript-eslint/typescript-estree@4.33.0(typescript@4.6.3): - resolution: {integrity: sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==} - engines: {node: ^10.12.0 || >=12.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/visitor-keys': 4.33.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.4 - tsutils: 3.21.0(typescript@4.6.3) - typescript: 4.6.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/visitor-keys@4.33.0: - resolution: {integrity: sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==} - engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} - dependencies: - '@typescript-eslint/types': 4.33.0 - eslint-visitor-keys: 2.1.0 - dev: true - - /@vendia/serverless-express@4.10.4: - resolution: {integrity: sha512-OH2cX+LqtrayCIkHAkShiLnvrgqGDvwIQEex5dHc/uJitBQjIz3q7dZtfU7cZ5vcR9Vkide5xJQDBEMbXoWLeA==} - engines: {node: '>=12'} - dev: false - - /abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: true - - /accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - dev: false - - /acorn-jsx@5.3.2(acorn@7.4.1): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 7.4.1 - dev: true - - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} - engines: {node: '>=0.4.0'} - dev: true - - /acorn@7.4.1: - resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /acorn@8.10.0: - resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - - /agentkeepalive@4.5.0: - resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} - engines: {node: '>= 8.0.0'} - dependencies: - humanize-ms: 1.2.1 - dev: false - - /aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - dev: false - - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: true - - /ajv@8.12.0: - resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - dev: true - - /ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - dev: true - - /ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.21.3 - dev: true - - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: false - - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - dev: true - - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - /ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - dev: false - - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - dev: true - - /apollo-datasource@3.3.2: - resolution: {integrity: sha512-L5TiS8E2Hn/Yz7SSnWIVbZw0ZfEIXZCa5VUiVxD9P53JvSrf4aStvsFDlGWPvpIdCR+aly2CfoB79B9/JjKFqg==} - engines: {node: '>=12.0'} - deprecated: The `apollo-datasource` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - dependencies: - '@apollo/utils.keyvaluecache': 1.0.2 - apollo-server-env: 4.2.1 - transitivePeerDependencies: - - encoding - dev: false - - /apollo-reporting-protobuf@3.4.0: - resolution: {integrity: sha512-h0u3EbC/9RpihWOmcSsvTW2O6RXVaD/mPEjfrPkxRPTEPWqncsgOoRJw+wih4OqfH3PvTJvoEIf4LwKrUaqWog==} - deprecated: The `apollo-reporting-protobuf` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/usage-reporting-protobuf` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - dependencies: - '@apollo/protobufjs': 1.2.6 - dev: false - - /apollo-server-core@3.12.1(graphql@16.8.1): - resolution: {integrity: sha512-9SF5WAkkV0FZQ2HVUWI9Jada1U0jg7e8NCN9EklbtvaCeUlOLyXyM+KCWuZ7+dqHxjshbtcwylPHutt3uzoNkw==} - engines: {node: '>=12.0'} - deprecated: The `apollo-server-core` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - peerDependencies: - graphql: ^15.3.0 || ^16.0.0 - dependencies: - '@apollo/utils.keyvaluecache': 1.0.2 - '@apollo/utils.logger': 1.0.1 - '@apollo/utils.usagereporting': 1.0.1(graphql@16.8.1) - '@apollographql/apollo-tools': 0.5.4(graphql@16.8.1) - '@apollographql/graphql-playground-html': 1.6.29 - '@graphql-tools/mock': 8.7.20(graphql@16.8.1) - '@graphql-tools/schema': 8.5.1(graphql@16.8.1) - '@josephg/resolvable': 1.0.1 - apollo-datasource: 3.3.2 - apollo-reporting-protobuf: 3.4.0 - apollo-server-env: 4.2.1 - apollo-server-errors: 3.3.1(graphql@16.8.1) - apollo-server-plugin-base: 3.7.2(graphql@16.8.1) - apollo-server-types: 3.8.0(graphql@16.8.1) - async-retry: 1.3.3 - fast-json-stable-stringify: 2.1.0 - graphql: 16.8.1 - graphql-tag: 2.12.6(graphql@16.8.1) - loglevel: 1.8.1 - lru-cache: 6.0.0 - node-abort-controller: 3.1.1 - sha.js: 2.4.11 - uuid: 9.0.1 - whatwg-mimetype: 3.0.0 - transitivePeerDependencies: - - encoding - dev: false - - /apollo-server-env@4.2.1: - resolution: {integrity: sha512-vm/7c7ld+zFMxibzqZ7SSa5tBENc4B0uye9LTfjJwGoQFY5xsUPH5FpO5j0bMUDZ8YYNbrF9SNtzc5Cngcr90g==} - engines: {node: '>=12.0'} - deprecated: The `apollo-server-env` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/utils.fetcher` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - dev: false - - /apollo-server-errors@3.3.1(graphql@16.8.1): - resolution: {integrity: sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA==} - engines: {node: '>=12.0'} - deprecated: The `apollo-server-errors` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - peerDependencies: - graphql: ^15.3.0 || ^16.0.0 - dependencies: - graphql: 16.8.1 - dev: false - - /apollo-server-express@3.12.1(express@4.18.2)(graphql@16.8.1): - resolution: {integrity: sha512-5A9efrhEXqDx08BnORWf0zPYCABENqur47VZZW8osQpSSnMINqzJiV5RMrzz8wIznecRRhEcz+BqLdiexqZdgg==} - engines: {node: '>=12.0'} - deprecated: The `apollo-server-express` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - peerDependencies: - express: ^4.17.1 - graphql: ^15.3.0 || ^16.0.0 - dependencies: - '@types/accepts': 1.3.5 - '@types/body-parser': 1.19.2 - '@types/cors': 2.8.12 - '@types/express': 4.17.14 - '@types/express-serve-static-core': 4.17.31 - accepts: 1.3.8 - apollo-server-core: 3.12.1(graphql@16.8.1) - apollo-server-types: 3.8.0(graphql@16.8.1) - body-parser: 1.20.2 - cors: 2.8.5 - express: 4.18.2 - graphql: 16.8.1 - parseurl: 1.3.3 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /apollo-server-lambda@3.12.1(graphql@16.8.1): - resolution: {integrity: sha512-PlwLI+uT4EaQm5bSRRvXWEv/JwwMfIXCiceU0jXcV2QL6G1cQDnQjaDTwisQymweyXhfQ8NGOB+87JMu9QFDsw==} - engines: {node: '>=12.0'} - deprecated: The `apollo-server-lambda` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - peerDependencies: - graphql: ^15.3.0 || ^16.0.0 - dependencies: - '@types/aws-lambda': 8.10.124 - '@vendia/serverless-express': 4.10.4 - apollo-server-core: 3.12.1(graphql@16.8.1) - apollo-server-express: 3.12.1(express@4.18.2)(graphql@16.8.1) - express: 4.18.2 - graphql: 16.8.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /apollo-server-plugin-base@3.7.2(graphql@16.8.1): - resolution: {integrity: sha512-wE8dwGDvBOGehSsPTRZ8P/33Jan6/PmL0y0aN/1Z5a5GcbFhDaaJCjK5cav6npbbGL2DPKK0r6MPXi3k3N45aw==} - engines: {node: '>=12.0'} - deprecated: The `apollo-server-plugin-base` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - peerDependencies: - graphql: ^15.3.0 || ^16.0.0 - dependencies: - apollo-server-types: 3.8.0(graphql@16.8.1) - graphql: 16.8.1 - transitivePeerDependencies: - - encoding - dev: false - - /apollo-server-types@3.8.0(graphql@16.8.1): - resolution: {integrity: sha512-ZI/8rTE4ww8BHktsVpb91Sdq7Cb71rdSkXELSwdSR0eXu600/sY+1UXhTWdiJvk+Eq5ljqoHLwLbY2+Clq2b9A==} - engines: {node: '>=12.0'} - deprecated: The `apollo-server-types` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - peerDependencies: - graphql: ^15.3.0 || ^16.0.0 - dependencies: - '@apollo/utils.keyvaluecache': 1.0.2 - '@apollo/utils.logger': 1.0.1 - apollo-reporting-protobuf: 3.4.0 - apollo-server-env: 4.2.1 - graphql: 16.8.1 - transitivePeerDependencies: - - encoding - dev: false - - /apollo-server@3.12.1(graphql@16.8.1): - resolution: {integrity: sha512-wgxx+J8KPTkhepi4qI9qY1xNoYzJfmvRKVUdFmFCZ3lyPO2j/+oOnAnyEVruAIQU5gquK10B0jdwVyvese9J/g==} - deprecated: The `apollo-server` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details. - peerDependencies: - graphql: ^15.3.0 || ^16.0.0 - dependencies: - '@types/express': 4.17.14 - apollo-server-core: 3.12.1(graphql@16.8.1) - apollo-server-express: 3.12.1(express@4.18.2)(graphql@16.8.1) - express: 4.18.2 - graphql: 16.8.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true - - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - dependencies: - sprintf-js: 1.0.3 - dev: true - - /aria-query@4.2.2: - resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} - engines: {node: '>=6.0'} - dependencies: - '@babel/runtime': 7.23.2 - '@babel/runtime-corejs3': 7.23.2 - dev: true - - /aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - dependencies: - dequal: 2.0.3 - dev: true - - /array-buffer-byte-length@1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} - dependencies: - call-bind: 1.0.2 - is-array-buffer: 3.0.2 - - /array-flatten@1.1.1: - resolution: {integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=} - dev: false - - /array-includes@3.1.7: - resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - get-intrinsic: 1.2.1 - is-string: 1.0.7 - dev: true - - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - dev: true - - /array.prototype.findlastindex@1.2.3: - resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.1 - dev: true - - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - es-shim-unscopables: 1.0.0 - dev: true - - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - es-shim-unscopables: 1.0.0 - dev: true - - /array.prototype.tosorted@1.1.2: - resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.1 - dev: true - - /arraybuffer.prototype.slice@1.0.2: - resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.0 - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - get-intrinsic: 1.2.1 - is-array-buffer: 3.0.2 - is-shared-array-buffer: 1.0.2 - dev: true - - /ast-types-flow@0.0.7: - resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} - dev: true - - /astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - dev: true - - /async-retry@1.3.3: - resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} - dependencies: - retry: 0.13.1 - dev: false - - /asynciterator.prototype@1.0.0: - resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} - dependencies: - has-symbols: 1.0.3 - dev: true - - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false - - /available-typed-arrays@1.0.5: - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} - engines: {node: '>= 0.4'} - - /axe-core@4.8.2: - resolution: {integrity: sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==} - engines: {node: '>=4'} - dev: true - - /axobject-query@3.2.1: - resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} - dependencies: - dequal: 2.0.3 - dev: true - - /babel-jest@29.7.0(@babel/core@7.23.2): - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.23.2 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.2 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.23.2) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - dependencies: - '@babel/helper-plugin-utils': 7.22.5 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.0 - '@types/babel__core': 7.20.2 - '@types/babel__traverse': 7.20.2 - dev: true - - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.2): - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.2) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.2) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.2) - dev: true - - /babel-preset-jest@29.6.3(@babel/core@7.23.2): - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.2) - dev: true - - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - /binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} - dev: true - - /body-parser@1.20.1: - resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.11.0 - raw-body: 2.5.1 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - dev: false - - /body-parser@1.20.2: - resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.11.0 - raw-body: 2.5.2 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - dev: false - - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - dev: true - - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - dependencies: - balanced-match: 1.0.2 - dev: false - - /braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.0.1 - dev: true - - /browserslist@4.22.1: - resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001549 - electron-to-chromium: 1.4.554 - node-releases: 2.0.13 - update-browserslist-db: 1.0.13(browserslist@4.22.1) - dev: true - - /bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - dependencies: - fast-json-stable-stringify: 2.1.0 - dev: true - - /bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - dependencies: - node-int64: 0.4.0 - dev: true - - /buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - dev: false - - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true - - /busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} - dependencies: - streamsearch: 1.1.0 - dev: true - - /bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - dev: false - - /cacache@17.1.4: - resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - '@npmcli/fs': 3.1.0 - fs-minipass: 3.0.3 - glob: 10.3.10 - lru-cache: 7.18.3 - minipass: 7.0.4 - minipass-collect: 1.0.2 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - p-map: 4.0.0 - ssri: 10.0.5 - tar: 6.2.0 - unique-filename: 3.0.0 - dev: false - - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - dependencies: - function-bind: 1.1.2 - get-intrinsic: 1.2.1 - - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - dev: true - - /camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - dev: true - - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - dev: true - - /caniuse-lite@1.0.30001549: - resolution: {integrity: sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==} - dev: true - - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - dev: true - - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - /char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - dev: true - - /chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} - engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - dev: false - - /ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - dev: true - - /cjs-module-lexer@1.2.3: - resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} - dev: true - - /clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - dev: false - - /client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - dev: true - - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - - /co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - dev: true - - /collect-v8-coverage@1.0.2: - resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - dev: true - - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - dev: true - - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true - - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: false - - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: false - - /concat-map@0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} - dev: true - - /content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - dependencies: - safe-buffer: 5.2.1 - dev: false - - /content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - dev: false - - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - dev: true - - /cookie-signature@1.0.6: - resolution: {integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw=} - dev: false - - /cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} - dev: false - - /core-js-pure@3.33.0: - resolution: {integrity: sha512-FKSIDtJnds/YFIEaZ4HszRX7hkxGpNKM7FC9aJ9WLJbSd3lD4vOltFuVIBLR8asSx9frkTSqL0dw90SKQxgKrg==} - requiresBuild: true - dev: true - - /cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - dev: false - - /create-jest@29.7.0(@types/node@20.8.6)(ts-node@10.9.1): - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.8.6)(ts-node@10.9.1) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true - - /cross-fetch@3.1.8: - resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - dev: false - - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - /cssfilter@0.0.10: - resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==} - dev: false - - /damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - dev: true - - /debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.0.0 - dev: false - - /debug@3.2.7(supports-color@5.5.0): - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - supports-color: 5.5.0 - dev: true - - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - - /dedent@1.5.1: - resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - dev: true - - /deep-equal@2.2.2: - resolution: {integrity: sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==} - dependencies: - array-buffer-byte-length: 1.0.0 - call-bind: 1.0.2 - es-get-iterator: 1.1.3 - get-intrinsic: 1.2.1 - is-arguments: 1.1.1 - is-array-buffer: 3.0.2 - is-date-object: 1.0.5 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - isarray: 2.0.5 - object-is: 1.1.5 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.5.1 - side-channel: 1.0.4 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.11 - dev: false - - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true - - /deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - dev: true - - /define-data-property@1.1.1: - resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.1 - gopd: 1.0.1 - has-property-descriptors: 1.0.0 - - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - dependencies: - define-data-property: 1.1.1 - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: false - - /depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dev: false - - /dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - dev: true - - /destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dev: false - - /detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - dev: true - - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true - - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dependencies: - path-type: 4.0.0 - dev: true - - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dependencies: - esutils: 2.0.3 - dev: true - - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dependencies: - esutils: 2.0.3 - dev: true - - /dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - dev: true - - /dotenv@16.3.1: - resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} - engines: {node: '>=12'} - dev: false - - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: false - - /ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - dependencies: - safe-buffer: 5.2.1 - dev: false - - /ee-first@1.1.1: - resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} - dev: false - - /electron-to-chromium@1.4.554: - resolution: {integrity: sha512-Q0umzPJjfBrrj8unkONTgbKQXzXRrH7sVV7D9ea2yBV3Oaogz991yhbpfvo2LMNkJItmruXTEzVpP9cp7vaIiQ==} - dev: true - - /emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - dev: true - - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - /encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - dev: false - - /encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true - dependencies: - iconv-lite: 0.6.3 - dev: false - optional: true - - /enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - dev: true - - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: false - - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - dependencies: - is-arrayish: 0.2.1 - dev: true - - /es-abstract@1.22.2: - resolution: {integrity: sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.0 - arraybuffer.prototype.slice: 1.0.2 - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - es-set-tostringtag: 2.0.1 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.1 - get-symbol-description: 1.0.0 - globalthis: 1.0.3 - gopd: 1.0.1 - has: 1.0.4 - has-property-descriptors: 1.0.0 - has-proto: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - is-array-buffer: 3.0.2 - is-callable: 1.2.7 - is-negative-zero: 2.0.2 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - is-string: 1.0.7 - is-typed-array: 1.1.12 - is-weakref: 1.0.2 - object-inspect: 1.13.0 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.5.1 - safe-array-concat: 1.0.1 - safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.8 - string.prototype.trimend: 1.0.7 - string.prototype.trimstart: 1.0.7 - typed-array-buffer: 1.0.0 - typed-array-byte-length: 1.0.0 - typed-array-byte-offset: 1.0.0 - typed-array-length: 1.0.4 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.11 - dev: true - - /es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - has-symbols: 1.0.3 - is-arguments: 1.1.1 - is-map: 2.0.2 - is-set: 2.0.2 - is-string: 1.0.7 - isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 - dev: false - - /es-iterator-helpers@1.0.15: - resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} - dependencies: - asynciterator.prototype: 1.0.0 - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - es-set-tostringtag: 2.0.1 - function-bind: 1.1.2 - get-intrinsic: 1.2.1 - globalthis: 1.0.3 - has-property-descriptors: 1.0.0 - has-proto: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - iterator.prototype: 1.1.2 - safe-array-concat: 1.0.1 - dev: true - - /es-set-tostringtag@2.0.1: - resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.1 - has: 1.0.4 - has-tostringtag: 1.0.0 - dev: true - - /es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} - dependencies: - has: 1.0.4 - dev: true - - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - dev: true - - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - dev: true - - /escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - dev: false - - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - dev: true - - /escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - dev: true - - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true - - /eslint-config-next@11.1.4(eslint@7.32.0)(next@13.5.4)(typescript@4.6.3): - resolution: {integrity: sha512-PD2/sxnLcI1Zy/QwKSwugzgafwymNh70Y/nPB/v+i0GOTFIl2JpLRUg9m/bQFHzi6PDeDM81w89ayFvpa2/Nxg==} - peerDependencies: - eslint: ^7.23.0 - next: '>=10.2.0' - typescript: '>=3.3.1' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@next/eslint-plugin-next': 11.1.4 - '@rushstack/eslint-patch': 1.5.1 - '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@4.6.3) - eslint: 7.32.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.28.1)(eslint@7.32.0) - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@4.33.0)(eslint-import-resolver-typescript@2.7.1)(eslint@7.32.0) - eslint-plugin-jsx-a11y: 6.7.1(eslint@7.32.0) - eslint-plugin-react: 7.33.2(eslint@7.32.0) - eslint-plugin-react-hooks: 4.6.0(eslint@7.32.0) - next: 13.5.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0) - typescript: 4.6.3 - transitivePeerDependencies: - - eslint-import-resolver-webpack - - supports-color - dev: true - - /eslint-config-prettier@8.10.0(eslint@7.32.0): - resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - dependencies: - eslint: 7.32.0 - dev: true - - /eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - dependencies: - debug: 3.2.7(supports-color@5.5.0) - is-core-module: 2.13.0 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.28.1)(eslint@7.32.0): - resolution: {integrity: sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==} - engines: {node: '>=4'} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - dependencies: - debug: 4.3.4 - eslint: 7.32.0 - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@4.33.0)(eslint-import-resolver-typescript@2.7.1)(eslint@7.32.0) - glob: 7.2.3 - is-glob: 4.0.3 - resolve: 1.22.8 - tsconfig-paths: 3.14.2 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-module-utils@2.8.0(@typescript-eslint/parser@4.33.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@2.7.1)(eslint@7.32.0): - resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - dependencies: - '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@4.6.3) - debug: 3.2.7(supports-color@5.5.0) - eslint: 7.32.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.28.1)(eslint@7.32.0) - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@4.33.0)(eslint-import-resolver-typescript@2.7.1)(eslint@7.32.0): - resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - dependencies: - '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@4.6.3) - array-includes: 3.1.7 - array.prototype.findlastindex: 1.2.3 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@5.5.0) - doctrine: 2.1.0 - eslint: 7.32.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@4.33.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@2.7.1)(eslint@7.32.0) - has: 1.0.4 - is-core-module: 2.13.0 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.7 - object.groupby: 1.0.1 - object.values: 1.1.7 - semver: 6.3.1 - tsconfig-paths: 3.14.2 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - dev: true - - /eslint-plugin-jest-dom@3.9.4(eslint@7.32.0): - resolution: {integrity: sha512-VRkaALGIhyxinnewZFHe2WJsRWp3TONpXysVXK1IUNJHCpJAIM9yRrI7fQ8i5F6UYE7+DAnvNhSSJZesLTonug==} - engines: {node: ^10.12.0 || >=12.0.0, npm: '>=6', yarn: '>=1'} - peerDependencies: - eslint: '>=6.8' - dependencies: - '@babel/runtime': 7.23.2 - '@testing-library/dom': 7.31.2 - eslint: 7.32.0 - requireindex: 1.2.0 - dev: true - - /eslint-plugin-jsx-a11y@6.7.1(eslint@7.32.0): - resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - '@babel/runtime': 7.23.2 - aria-query: 5.3.0 - array-includes: 3.1.7 - array.prototype.flatmap: 1.3.2 - ast-types-flow: 0.0.7 - axe-core: 4.8.2 - axobject-query: 3.2.1 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 7.32.0 - has: 1.0.4 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.5 - minimatch: 3.1.2 - object.entries: 1.1.7 - object.fromentries: 2.0.7 - semver: 6.3.1 - dev: true - - /eslint-plugin-react-hooks@4.6.0(eslint@7.32.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - dependencies: - eslint: 7.32.0 - dev: true - - /eslint-plugin-react@7.33.2(eslint@7.32.0): - resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: 3.1.7 - array.prototype.flatmap: 1.3.2 - array.prototype.tosorted: 1.1.2 - doctrine: 2.1.0 - es-iterator-helpers: 1.0.15 - eslint: 7.32.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.7 - object.fromentries: 2.0.7 - object.hasown: 1.1.3 - object.values: 1.1.7 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.10 - dev: true - - /eslint-plugin-simple-import-sort@7.0.0(eslint@7.32.0): - resolution: {integrity: sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw==} - peerDependencies: - eslint: '>=5.0.0' - dependencies: - eslint: 7.32.0 - dev: true - - /eslint-plugin-testing-library@4.12.4(eslint@7.32.0)(typescript@4.6.3): - resolution: {integrity: sha512-XZtoeyIZKFTiH8vhwnCaTo/mNrLHoLyufY4kkNg+clzZFeThWPjp+0QfrLam1on1k3JGwiRvoLH/V4QdBaB2oA==} - engines: {node: ^10.12.0 || >=12.0.0, npm: '>=6'} - peerDependencies: - eslint: ^7.5.0 - dependencies: - '@typescript-eslint/experimental-utils': 4.33.0(eslint@7.32.0)(typescript@4.6.3) - eslint: 7.32.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - dev: true - - /eslint-utils@2.1.0: - resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} - engines: {node: '>=6'} - dependencies: - eslint-visitor-keys: 1.3.0 - dev: true - - /eslint-utils@3.0.0(eslint@7.32.0): - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 7.32.0 - eslint-visitor-keys: 2.1.0 - dev: true - - /eslint-visitor-keys@1.3.0: - resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} - engines: {node: '>=4'} - dev: true - - /eslint-visitor-keys@2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - dev: true - - /eslint@7.32.0: - resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==} - engines: {node: ^10.12.0 || >=12.0.0} - hasBin: true - dependencies: - '@babel/code-frame': 7.12.11 - '@eslint/eslintrc': 0.4.3 - '@humanwhocodes/config-array': 0.5.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - enquirer: 2.4.1 - escape-string-regexp: 4.0.0 - eslint-scope: 5.1.1 - eslint-utils: 2.1.0 - eslint-visitor-keys: 2.1.0 - espree: 7.3.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - functional-red-black-tree: 1.0.1 - glob-parent: 5.1.2 - globals: 13.23.0 - ignore: 4.0.6 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - js-yaml: 3.14.1 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.3 - progress: 2.0.3 - regexpp: 3.2.0 - semver: 7.5.4 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - table: 6.8.1 - text-table: 0.2.0 - v8-compile-cache: 2.4.0 - transitivePeerDependencies: - - supports-color - dev: true - - /espree@7.3.1: - resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - acorn: 7.4.1 - acorn-jsx: 5.3.2(acorn@7.4.1) - eslint-visitor-keys: 1.3.0 - dev: true - - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - dev: true - - /esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: true - - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - dev: true - - /estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - dev: true - - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - dev: true - - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - dev: true - - /etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - dev: false - - /execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - dev: true - - /exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - dev: true - - /expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - dev: true - - /express@4.18.2: - resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} - engines: {node: '>= 0.10.0'} - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.1 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.5.0 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.2.0 - fresh: 0.5.2 - http-errors: 2.0.0 - merge-descriptors: 1.0.1 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.7 - proxy-addr: 2.0.7 - qs: 6.11.0 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 - setprototypeof: 1.2.0 - statuses: 2.0.1 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - dev: false - - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true - - /fast-glob@3.3.1: - resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - dev: true - - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true - - /fastq@1.15.0: - resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} - dependencies: - reusify: 1.0.4 - dev: true - - /fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - dependencies: - bser: 2.1.1 - dev: true - - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flat-cache: 3.1.1 - dev: true - - /fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - dev: true - - /finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} - engines: {node: '>= 0.8'} - dependencies: - debug: 2.6.9 - encodeurl: 1.0.2 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.1 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - dev: false - - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - dev: true - - /flat-cache@3.1.1: - resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} - engines: {node: '>=12.0.0'} - dependencies: - flatted: 3.2.9 - keyv: 4.5.4 - rimraf: 3.0.2 - dev: true - - /flatted@3.2.9: - resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} - dev: true - - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - dependencies: - is-callable: 1.2.7 - - /foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} - engines: {node: '>=14'} - dependencies: - cross-spawn: 7.0.3 - signal-exit: 4.1.0 - dev: false - - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: false - - /forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - dev: false - - /fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - dev: false - - /fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: false - - /fs-minipass@3.0.3: - resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - minipass: 7.0.4 - dev: false - - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true - - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - functions-have-names: 1.2.3 - dev: true - - /functional-red-black-tree@1.0.1: - resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} - dev: true - - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - dev: true - - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true - - /get-intrinsic@1.2.1: - resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} - dependencies: - function-bind: 1.1.2 - has: 1.0.4 - has-proto: 1.0.1 - has-symbols: 1.0.3 - - /get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - dev: true - - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - dev: true - - /get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - dev: true - - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - dev: true - - /glob-to-regexp@0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - dev: true - - /glob@10.3.10: - resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - dependencies: - foreground-child: 3.1.1 - jackspeak: 2.3.6 - minimatch: 9.0.3 - minipass: 7.0.4 - path-scurry: 1.10.1 - dev: false - - /glob@7.1.7: - resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - dev: true - - /globals@13.23.0: - resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - dev: true - - /globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} - dependencies: - define-properties: 1.2.1 - dev: true - - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.1 - ignore: 5.2.4 - merge2: 1.4.1 - slash: 3.0.0 - dev: true - - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - dependencies: - get-intrinsic: 1.2.1 - - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true - - /graphql-request@6.1.0(graphql@16.8.1): - resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} - peerDependencies: - graphql: 14 - 16 - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) - cross-fetch: 3.1.8 - graphql: 16.8.1 - transitivePeerDependencies: - - encoding - dev: false - - /graphql-tag@2.12.6(graphql@16.8.1): - resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} - engines: {node: '>=10'} - peerDependencies: - graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - dependencies: - graphql: 16.8.1 - tslib: 2.6.2 - dev: false - - /graphql@16.8.1: - resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - dev: false - - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - dev: true - - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - /has-property-descriptors@1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} - dependencies: - get-intrinsic: 1.2.1 - - /has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} - engines: {node: '>= 0.4'} - - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - - /has-tostringtag@1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - - /has@1.0.4: - resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} - engines: {node: '>= 0.4.0'} - - /html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - dev: true - - /http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - dev: false - - /http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - dev: false - - /http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - - /https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - - /human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - dev: true - - /humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - dependencies: - ms: 2.1.3 - dev: false - - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - dev: false - - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - dev: false - optional: true - - /ignore-by-default@1.0.1: - resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - dev: true - - /ignore@4.0.6: - resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} - engines: {node: '>= 4'} - dev: true - - /ignore@5.2.4: - resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} - engines: {node: '>= 4'} - dev: true - - /immediate@3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - dev: false - - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - dev: true - - /import-local@3.1.0: - resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} - engines: {node: '>=8'} - hasBin: true - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - dev: true - - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - /indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - dev: false - - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: true - - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - /internal-slot@1.0.5: - resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.1 - has: 1.0.4 - side-channel: 1.0.4 - - /ip@2.0.0: - resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} - dev: false - - /ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - dev: false - - /is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - dev: false - - /is-array-buffer@3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - is-typed-array: 1.1.12 - - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - dev: true - - /is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - dependencies: - has-bigints: 1.0.2 - - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - dependencies: - binary-extensions: 2.2.0 - dev: true - - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - /is-core-module@2.13.0: - resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} - dependencies: - has: 1.0.4 - dev: true - - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true - - /is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} - dependencies: - call-bind: 1.0.2 - dev: true - - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - /is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - dev: true - - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - dev: true - - /is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - dev: false - - /is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - - /is-negative-zero@2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} - engines: {node: '>= 0.4'} - dev: true - - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true - - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - - /is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - - /is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} - dependencies: - call-bind: 1.0.2 - - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: true - - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - - /is-typed-array@1.1.12: - resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} - engines: {node: '>= 0.4'} - dependencies: - which-typed-array: 1.1.11 - - /is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} - - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - dependencies: - call-bind: 1.0.2 - dev: true - - /is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - /istanbul-lib-coverage@3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} - engines: {node: '>=8'} - dev: true - - /istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - dependencies: - '@babel/core': 7.23.2 - '@babel/parser': 7.23.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /istanbul-lib-instrument@6.0.1: - resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} - engines: {node: '>=10'} - dependencies: - '@babel/core': 7.23.2 - '@babel/parser': 7.23.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - dev: true - - /istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - dependencies: - istanbul-lib-coverage: 3.2.0 - make-dir: 4.0.0 - supports-color: 7.2.0 - dev: true - - /istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - dependencies: - debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - dev: true - - /istanbul-reports@3.1.6: - resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} - engines: {node: '>=8'} - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - dev: true - - /iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} - dependencies: - define-properties: 1.2.1 - get-intrinsic: 1.2.1 - has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.4 - set-function-name: 2.0.1 - dev: true - - /jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - dev: false - - /javascript-natural-sort@0.7.1: - resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} - dev: true - - /jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 - dev: true - - /jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.1 - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.0.4 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-cli@29.7.0(@types/node@20.8.6)(ts-node@10.9.1): - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.1) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.8.6)(ts-node@10.9.1) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.8.6)(ts-node@10.9.1) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /jest-config@29.7.0(@types/node@20.8.6)(ts-node@10.9.1): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.23.2 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - babel-jest: 29.7.0(@babel/core@7.23.2) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@20.8.6)(typescript@4.6.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - - /jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 - dev: true - - /jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - dev: true - - /jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - jest-mock: 29.7.0 - jest-util: 29.7.0 - dev: true - - /jest-extended@4.0.2(jest@29.7.0): - resolution: {integrity: sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - jest: '>=27.2.5' - peerDependenciesMeta: - jest: - optional: true - dependencies: - jest: 29.7.0(@types/node@20.8.6)(ts-node@10.9.1) - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - dev: true - - /jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.7 - '@types/node': 20.8.6 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - - /jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - - /jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.22.13 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 - dev: true - - /jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - jest-util: 29.7.0 - dev: true - - /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 29.7.0 - dev: true - - /jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.8 - resolve.exports: 2.0.2 - slash: 3.0.0 - dev: true - - /jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - chalk: 4.1.2 - cjs-module-lexer: 1.2.3 - collect-v8-coverage: 1.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.23.2 - '@babel/generator': 7.23.0 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.2) - '@babel/types': 7.23.0 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.2) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - chalk: 4.1.2 - ci-info: 3.9.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - dev: true - - /jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 - dev: true - - /jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.8.6 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 - dev: true - - /jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 20.8.6 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - dev: true - - /jest@29.7.0(@types/node@20.8.6)(ts-node@10.9.1): - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.1) - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.8.6)(ts-node@10.9.1) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /js-levenshtein@1.1.6: - resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} - engines: {node: '>=0.10.0'} - dev: false - - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true - - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - dev: true - - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - dev: true - - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true - - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - dev: true - - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true - - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - dev: true - - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true - - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - dependencies: - minimist: 1.2.8 - dev: true - - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true - - /jsonwebtoken@9.0.2: - resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} - engines: {node: '>=12', npm: '>=6'} - dependencies: - jws: 3.2.2 - 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.1.1 - ms: 2.1.3 - semver: 7.5.4 - dev: false - - /jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - dependencies: - array-includes: 3.1.7 - array.prototype.flat: 1.3.2 - object.assign: 4.1.4 - object.values: 1.1.7 - dev: true - - /jwa@1.4.1: - resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - dev: false - - /jws@3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} - dependencies: - jwa: 1.4.1 - safe-buffer: 5.2.1 - dev: false - - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - dependencies: - json-buffer: 3.0.1 - dev: true - - /kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - dev: true - - /language-subtag-registry@0.3.22: - resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} - dev: true - - /language-tags@1.0.5: - resolution: {integrity: sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==} - dependencies: - language-subtag-registry: 0.3.22 - dev: true - - /leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - dev: true - - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true - - /lie@3.1.1: - resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} - dependencies: - immediate: 3.0.6 - dev: false - - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true - - /localforage@1.10.0: - resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} - dependencies: - lie: 3.1.1 - dev: false - - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - dev: true - - /lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - dev: false - - /lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - dev: false - - /lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - dev: false - - /lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - dev: false - - /lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - dev: false - - /lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - dev: false - - /lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - dev: true - - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true - - /lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - dev: false - - /lodash.sortby@4.7.0: - resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - dev: false - - /lodash.truncate@4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - dev: true - - /lodash.xorby@4.7.0: - resolution: {integrity: sha512-gYiD6nvuQy0AEkMoUju+t4f4Rn18fjsLB/7x7YZFqtFT9kmegRLrj/uGEQVyVDy7otTmSrIMXNOk2wwuLcfHCQ==} - dev: false - - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true - - /loglevel@1.8.1: - resolution: {integrity: sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==} - engines: {node: '>= 0.6.0'} - dev: false - - /long@4.0.0: - resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} - dev: false - - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - dependencies: - js-tokens: 4.0.0 - dev: true - - /lru-cache@10.0.1: - resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} - engines: {node: 14 || >=16.14} - dev: false - - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - dev: true - - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - - /lru-cache@7.13.1: - resolution: {integrity: sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==} - engines: {node: '>=12'} - dev: false - - /lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - dev: false - - /lru_map@0.3.3: - resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} - dev: false - - /lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} - hasBin: true - dev: true - - /make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - dependencies: - semver: 7.5.4 - dev: true - - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true - - /make-fetch-happen@11.1.1: - resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - agentkeepalive: 4.5.0 - cacache: 17.1.4 - http-cache-semantics: 4.1.1 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - is-lambda: 1.0.1 - lru-cache: 7.18.3 - minipass: 5.0.0 - minipass-fetch: 3.0.4 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 0.6.3 - promise-retry: 2.0.1 - socks-proxy-agent: 7.0.0 - ssri: 10.0.5 - transitivePeerDependencies: - - supports-color - dev: false - - /makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - dependencies: - tmpl: 1.0.5 - dev: true - - /media-typer@0.3.0: - resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=} - engines: {node: '>= 0.6'} - dev: false - - /merge-descriptors@1.0.1: - resolution: {integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=} - dev: false - - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true - - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true - - /methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - dev: false - - /micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} - dependencies: - braces: 3.0.2 - picomatch: 2.3.1 - dev: true - - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: false - - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - dev: false - - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - dev: false - - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true - - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - dev: true - - /minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - brace-expansion: 2.0.1 - dev: false - - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true - - /minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass-fetch@3.0.4: - resolution: {integrity: sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - minipass: 7.0.4 - minipass-sized: 1.0.3 - minizlib: 2.1.2 - optionalDependencies: - encoding: 0.1.13 - dev: false - - /minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - dependencies: - yallist: 4.0.0 - dev: false - - /minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - dev: false - - /minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} - engines: {node: '>=16 || 14 >=14.17'} - dev: false - - /minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - dev: false - - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: false - - /ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - dev: false - - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - /nanoid@3.3.6: - resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true - - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: false - - /next@13.5.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-+93un5S779gho8y9ASQhb/bTkQF17FNQOtXLKAj3lsNgltEcF0C5PMLLncDmH+8X1EnJH1kbqAERa29nRXqhjA==} - engines: {node: '>=16.14.0'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - react: ^18.2.0 - react-dom: ^18.2.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - sass: - optional: true - dependencies: - '@next/env': 13.5.4 - '@swc/helpers': 0.5.2 - busboy: 1.6.0 - caniuse-lite: 1.0.30001549 - postcss: 8.4.31 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(@babel/core@7.23.2)(react@18.2.0) - watchpack: 2.4.0 - optionalDependencies: - '@next/swc-darwin-arm64': 13.5.4 - '@next/swc-darwin-x64': 13.5.4 - '@next/swc-linux-arm64-gnu': 13.5.4 - '@next/swc-linux-arm64-musl': 13.5.4 - '@next/swc-linux-x64-gnu': 13.5.4 - '@next/swc-linux-x64-musl': 13.5.4 - '@next/swc-win32-arm64-msvc': 13.5.4 - '@next/swc-win32-ia32-msvc': 13.5.4 - '@next/swc-win32-x64-msvc': 13.5.4 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - dev: true - - /node-abort-controller@3.1.1: - resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} - dev: false - - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - dependencies: - whatwg-url: 5.0.0 - dev: false - - /node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - dev: true - - /node-releases@2.0.13: - resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} - dev: true - - /nodemon@3.0.1: - resolution: {integrity: sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==} - engines: {node: '>=10'} - hasBin: true - dependencies: - chokidar: 3.5.3 - debug: 3.2.7(supports-color@5.5.0) - ignore-by-default: 1.0.1 - minimatch: 3.1.2 - pstree.remy: 1.1.8 - semver: 7.5.4 - simple-update-notifier: 2.0.0 - supports-color: 5.5.0 - touch: 3.1.0 - undefsafe: 2.0.5 - dev: true - - /nopt@1.0.10: - resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} - hasBin: true - dependencies: - abbrev: 1.1.1 - dev: true - - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true - - /npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - dev: true - - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - /object-inspect@1.13.0: - resolution: {integrity: sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==} - - /object-is@1.1.5: - resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - dev: false - - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - /object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - - /object.entries@1.1.7: - resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - dev: true - - /object.fromentries@2.0.7: - resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - dev: true - - /object.groupby@1.0.1: - resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - get-intrinsic: 1.2.1 - dev: true - - /object.hasown@1.1.3: - resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} - dependencies: - define-properties: 1.2.1 - es-abstract: 1.22.2 - dev: true - - /object.values@1.1.7: - resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - dev: true - - /on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - dependencies: - ee-first: 1.1.1 - dev: false - - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - dev: true - - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - dependencies: - mimic-fn: 2.1.0 - dev: true - - /optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} - engines: {node: '>= 0.8.0'} - dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true - - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - dependencies: - p-try: 2.2.0 - dev: true - - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - dev: true - - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 - dev: true - - /p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - dependencies: - aggregate-error: 3.1.0 - dev: false - - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true - - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - dev: true - - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - dependencies: - '@babel/code-frame': 7.22.13 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - dev: true - - /parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - dev: false - - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true - - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true - - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true - - /path-scurry@1.10.1: - resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - lru-cache: 10.0.1 - minipass: 7.0.4 - dev: false - - /path-to-regexp@0.1.7: - resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} - dev: false - - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: true - - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true - - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true - - /pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} - engines: {node: '>= 6'} - dev: true - - /pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - dependencies: - find-up: 4.1.0 - dev: true - - /postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.6 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: true - - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true - - /prettier@3.0.3: - resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} - engines: {node: '>=14'} - hasBin: true - dev: true - - /pretty-format@26.6.2: - resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} - engines: {node: '>= 10'} - dependencies: - '@jest/types': 26.6.2 - ansi-regex: 5.0.1 - ansi-styles: 4.3.0 - react-is: 17.0.2 - dev: true - - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.2.0 - - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - dev: true - - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - dev: false - - /prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - dev: true - - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - dev: true - - /proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - dev: false - - /pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - dev: true - - /punycode@2.3.0: - resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} - engines: {node: '>=6'} - dev: true - - /pure-rand@6.0.4: - resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} - dev: true - - /qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} - engines: {node: '>=0.6'} - dependencies: - side-channel: 1.0.4 - dev: false - - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true - - /range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - dev: false - - /raw-body@2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} - engines: {node: '>= 0.8'} - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - dev: false - - /raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - dev: false - - /react-dom@18.2.0(react@18.2.0): - resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} - peerDependencies: - react: ^18.2.0 - dependencies: - loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.0 - dev: true - - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true - - /react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - dev: true - - /react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - - /react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} - engines: {node: '>=0.10.0'} - dependencies: - loose-envify: 1.4.0 - dev: true - - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - dependencies: - picomatch: 2.3.1 - dev: true - - /reflect.getprototypeof@1.0.4: - resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - get-intrinsic: 1.2.1 - globalthis: 1.0.3 - which-builtin-type: 1.1.3 - dev: true - - /regenerator-runtime@0.14.0: - resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} - dev: true - - /regexp.prototype.flags@1.5.1: - resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - set-function-name: 2.0.1 - - /regexpp@3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} - dev: true - - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true - - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - dev: true - - /requireindex@1.2.0: - resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==} - engines: {node: '>=0.10.5'} - dev: true - - /resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - dependencies: - resolve-from: 5.0.0 - dev: true - - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - dev: true - - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - dev: true - - /resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - dev: true - - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - dependencies: - is-core-module: 2.13.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - - /resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - dependencies: - is-core-module: 2.13.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - dev: false - - /retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - dev: false - - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true - - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - dependencies: - glob: 7.2.3 - dev: true - - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - dependencies: - queue-microtask: 1.2.3 - dev: true - - /safe-array-concat@1.0.1: - resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} - engines: {node: '>=0.4'} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - has-symbols: 1.0.3 - isarray: 2.0.5 - dev: true - - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false - - /safe-regex-test@1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - is-regex: 1.1.4 - dev: true - - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: false - - /scheduler@0.23.0: - resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} - dependencies: - loose-envify: 1.4.0 - dev: true - - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - dev: true - - /semver@7.5.4: - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - - /send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} - dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.0 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color - dev: false - - /serve-static@1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} - engines: {node: '>= 0.8.0'} - dependencies: - encodeurl: 1.0.2 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.18.0 - transitivePeerDependencies: - - supports-color - dev: false - - /set-function-name@2.0.1: - resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} - engines: {node: '>= 0.4'} - dependencies: - define-data-property: 1.1.1 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.0 - - /setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - dev: false - - /sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: false - - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - dependencies: - shebang-regex: 3.0.0 - - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - /side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - object-inspect: 1.13.0 - - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true - - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - dev: false - - /simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} - dependencies: - semver: 7.5.4 - dev: true - - /sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true - - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true - - /slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - dev: true - - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - dev: false - - /socks-proxy-agent@7.0.0: - resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} - engines: {node: '>= 10'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - socks: 2.7.1 - transitivePeerDependencies: - - supports-color - dev: false - - /socks@2.7.1: - resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} - engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} - dependencies: - ip: 2.0.0 - smart-buffer: 4.2.0 - dev: false - - /source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - dev: true - - /source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - dev: true - - /source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - dev: true - - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: true - - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true - - /ssri@10.0.5: - resolution: {integrity: sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - minipass: 7.0.4 - dev: false - - /stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - dependencies: - escape-string-regexp: 2.0.0 - dev: true - - /statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - dev: false - - /stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} - dependencies: - internal-slot: 1.0.5 - dev: false - - /streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - dev: true - - /string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - dev: true - - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - dev: false - - /string.prototype.matchall@4.0.10: - resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - get-intrinsic: 1.2.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - regexp.prototype.flags: 1.5.1 - set-function-name: 2.0.1 - side-channel: 1.0.4 - dev: true - - /string.prototype.trim@1.2.8: - resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - dev: true - - /string.prototype.trimend@1.0.7: - resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - dev: true - - /string.prototype.trimstart@1.0.7: - resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - es-abstract: 1.22.2 - dev: true - - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - dependencies: - ansi-regex: 5.0.1 - - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - dependencies: - ansi-regex: 6.0.1 - dev: false - - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true - - /strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - dev: true - - /strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - dev: true - - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true - - /styled-jsx@5.1.1(@babel/core@7.23.2)(react@18.2.0): - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - dependencies: - '@babel/core': 7.23.2 - client-only: 0.0.1 - react: 18.2.0 - dev: true - - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - dependencies: - has-flag: 3.0.0 - dev: true - - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - dependencies: - has-flag: 4.0.0 - dev: true - - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - dev: true - - /table@6.8.1: - resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} - engines: {node: '>=10.0.0'} - dependencies: - ajv: 8.12.0 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - - /tar@6.2.0: - resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} - engines: {node: '>=10'} - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - dev: false - - /test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - dev: true - - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true - - /tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - dev: true - - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - dev: true - - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - dev: true - - /toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - dev: false - - /touch@3.1.0: - resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} - hasBin: true - dependencies: - nopt: 1.0.10 - dev: true - - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: false - - /ts-graphviz@1.8.1: - resolution: {integrity: sha512-54/fe5iu0Jb6X0pmDmzsA2UHLfyHjUEUwfHtZcEOR0fZ6Myf+dFoO6eNsyL8CBDMJ9u7WWEewduVaiaXlvjSVw==} - engines: {node: '>=14.16'} - dev: false - - /ts-jest@29.1.1(@babel/core@7.23.2)(jest@29.7.0)(typescript@4.6.3): - resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 - esbuild: '*' - jest: ^29.0.0 - typescript: '>=4.3 <6' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - dependencies: - '@babel/core': 7.23.2 - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.8.6)(ts-node@10.9.1) - jest-util: 29.7.0 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.5.4 - typescript: 4.6.3 - yargs-parser: 21.1.1 - dev: true - - /ts-node@10.9.1(@types/node@20.8.6)(typescript@4.6.3): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.8.6 - acorn: 8.10.0 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 4.6.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true - - /tsconfig-paths@3.14.2: - resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 - dev: true - - /tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: true - - /tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - - /tsutils@3.21.0(typescript@4.6.3): - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - typescript: 4.6.3 - dev: true - - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - dev: true - - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true - - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - dev: true - - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - dev: true - - /type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - dev: false - - /typed-array-buffer@1.0.0: - resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - is-typed-array: 1.1.12 - dev: true - - /typed-array-byte-length@1.0.0: - resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 - dev: true - - /typed-array-byte-offset@1.0.0: - resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 - dev: true - - /typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} - dependencies: - call-bind: 1.0.2 - for-each: 0.3.3 - is-typed-array: 1.1.12 - dev: true - - /typescript@4.6.3: - resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==} - engines: {node: '>=4.2.0'} - hasBin: true - - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - dependencies: - call-bind: 1.0.2 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - dev: true - - /undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - dev: true - - /undici-types@5.25.3: - resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} - - /unique-filename@3.0.0: - resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - unique-slug: 4.0.0 - dev: false - - /unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - imurmurhash: 0.1.4 - dev: false - - /unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - dev: false - - /update-browserslist-db@1.0.13(browserslist@4.22.1): - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.22.1 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - dependencies: - punycode: 2.3.0 - dev: true - - /utils-merge@1.0.1: - resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} - engines: {node: '>= 0.4.0'} - dev: false - - /uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - dev: false - - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true - - /v8-compile-cache@2.4.0: - resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==} - dev: true - - /v8-to-istanbul@9.1.3: - resolution: {integrity: sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==} - engines: {node: '>=10.12.0'} - dependencies: - '@jridgewell/trace-mapping': 0.3.19 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 2.0.0 - dev: true - - /value-or-promise@1.0.11: - resolution: {integrity: sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==} - engines: {node: '>=12'} - dev: false - - /value-or-promise@1.0.12: - resolution: {integrity: sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==} - engines: {node: '>=12'} - dev: false - - /vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - dev: false - - /walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - dependencies: - makeerror: 1.0.12 - dev: true - - /watchpack@2.4.0: - resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} - engines: {node: '>=10.13.0'} - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - dev: true - - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: false - - /whatwg-mimetype@3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} - engines: {node: '>=12'} - dev: false - - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - dev: false - - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - - /which-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} - engines: {node: '>= 0.4'} - dependencies: - function.prototype.name: 1.1.6 - has-tostringtag: 1.0.0 - is-async-function: 2.0.0 - is-date-object: 1.0.5 - is-finalizationregistry: 1.0.2 - is-generator-function: 1.0.10 - is-regex: 1.1.4 - is-weakref: 1.0.2 - isarray: 2.0.5 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.11 - dev: true - - /which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} - dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 - - /which-typed-array@1.1.11: - resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 - - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - dependencies: - isexe: 2.0.0 - - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - dev: false - - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true - - /write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - dev: true - - /xss@1.0.14: - resolution: {integrity: sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==} - engines: {node: '>= 0.10.0'} - hasBin: true - dependencies: - commander: 2.20.3 - cssfilter: 0.0.10 - dev: false - - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true - - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: true - - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true - - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - dependencies: - cliui: 8.0.1 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - dev: true - - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true - - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true diff --git a/gateway/services.ts b/gateway/services.ts deleted file mode 100644 index d5e44fb928..0000000000 --- a/gateway/services.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { PYCON_BACKEND_SERVICE } from "./config"; - -const DEFAULT_SERVICES = [ - { - name: "pycon-backend", - url: `${PYCON_BACKEND_SERVICE}/graphql`, - }, -]; - -export const getServices = () => { - return DEFAULT_SERVICES; -}; diff --git a/gateway/tsconfig.json b/gateway/tsconfig.json deleted file mode 100644 index e17a90fbfb..0000000000 --- a/gateway/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "es2016", - "moduleResolution": "node", - "module": "commonjs", - "outDir": "dist", - "strict": true, - "allowJs": false, - "noEmit": false, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "skipLibCheck": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "isolatedModules": false, - "removeComments": false, - "preserveConstEnums": true, - "sourceMap": true, - "forceConsistentCasingInFileNames": true, - "typeRoots": ["./node_modules/@types", "./types"], - "resolveJsonModule": true, - "baseUrl": "." - }, - "exclude": ["node_modules"], - "include": ["*.ts", "**/*.ts"] -} diff --git a/gateway/types/global.ts b/gateway/types/global.ts deleted file mode 100644 index 178bab984d..0000000000 --- a/gateway/types/global.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare module "jsonwebtoken"; -declare module "cookie"; diff --git a/users-backend/.dockerignore b/users-backend/.dockerignore deleted file mode 100644 index b24421389c..0000000000 --- a/users-backend/.dockerignore +++ /dev/null @@ -1,8 +0,0 @@ -.vscode/ -tests/ -.coverage -.env -.flake8 -.isort.cfg -coverage.xml -Dockerfile diff --git a/users-backend/.editorconfig b/users-backend/.editorconfig deleted file mode 120000 index 38d9a0ce1c..0000000000 --- a/users-backend/.editorconfig +++ /dev/null @@ -1 +0,0 @@ -../.editorconfig \ No newline at end of file diff --git a/users-backend/.flake8 b/users-backend/.flake8 deleted file mode 120000 index cb0568d647..0000000000 --- a/users-backend/.flake8 +++ /dev/null @@ -1 +0,0 @@ -../.flake8 \ No newline at end of file diff --git a/users-backend/.isort.cfg b/users-backend/.isort.cfg deleted file mode 120000 index 62a72913ba..0000000000 --- a/users-backend/.isort.cfg +++ /dev/null @@ -1 +0,0 @@ -../.isort.cfg \ No newline at end of file diff --git a/users-backend/Dockerfile b/users-backend/Dockerfile deleted file mode 100644 index b278072ac7..0000000000 --- a/users-backend/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -ARG FUNCTION_DIR="/home/app/" - -FROM python:3.9-slim as build-stage - -ARG FUNCTION_DIR - -RUN mkdir -p ${FUNCTION_DIR} -WORKDIR ${FUNCTION_DIR} - -RUN apt-get update -y && apt-get install -y gcc libpq-dev - -ENV LIBRARY_PATH=/lib:/usr/lib - -COPY poetry.lock ${FUNCTION_DIR} -COPY pyproject.toml ${FUNCTION_DIR} - -RUN pip3 install poetry==1.3.2 - -RUN poetry config virtualenvs.in-project true -RUN poetry install --no-dev -E lambda - -FROM python:3.9-slim - -ARG FUNCTION_DIR - -WORKDIR ${FUNCTION_DIR} - -COPY --from=build-stage ${FUNCTION_DIR}/.venv ${FUNCTION_DIR}/.venv -RUN /home/app/.venv/bin/python -m pip install setuptools - -RUN mkdir -p ${FUNCTION_DIR}/assets - -COPY . ${FUNCTION_DIR} - -ENTRYPOINT ["/home/app/.venv/bin/python", "-m", "awslambdaric"] -CMD [ "main.handler" ] diff --git a/users-backend/alembic.ini b/users-backend/alembic.ini deleted file mode 100644 index 0d78e89942..0000000000 --- a/users-backend/alembic.ini +++ /dev/null @@ -1,83 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# path to migration scripts -script_location = alembic - -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# timezone to use when rendering the date -# within the migration file as well as the filename. -# string value is passed to dateutil.tz.gettz() -# leave blank for localtime -# timezone = - -# max length of characters to apply to the -# "slug" field -# truncate_slug_length = 40 - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - -# set to 'true' to allow .pyc and .pyo files without -# a source .py file to be detected as revisions in the -# versions/ directory -# sourceless = false - -# version location specification; this defaults -# to alembic/versions. When using multiple version -# directories, initial revisions must be specified with --version-path -# version_locations = %(here)s/bar %(here)s/bat alembic/versions - -# the output encoding used when revision files -# are written from script.py.mako -# output_encoding = utf-8 - - -[post_write_hooks] -# post_write_hooks defines scripts or Python functions that are run -# on newly generated revision scripts. See the documentation for further -# detail and examples - -# format using "black" - use the console_scripts runner, against the "black" entrypoint -# hooks=black -# black.type=console_scripts -# black.entrypoint=black -# black.options=-l 79 - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/users-backend/alembic/README b/users-backend/alembic/README deleted file mode 100644 index 98e4f9c44e..0000000000 --- a/users-backend/alembic/README +++ /dev/null @@ -1 +0,0 @@ -Generic single-database configuration. \ No newline at end of file diff --git a/users-backend/alembic/env.py b/users-backend/alembic/env.py deleted file mode 100644 index 687748b42f..0000000000 --- a/users-backend/alembic/env.py +++ /dev/null @@ -1,85 +0,0 @@ -import os -import sys - -sys.path.insert(0, os.getcwd()) - -from logging.config import fileConfig - -from alembic import context - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -from users.domain.entities import * -from sqlalchemy import engine_from_config, pool - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config - -# Interpret the config file for Python logging. -# This line sets up loggers basically. -fileConfig(config.config_file_name) - -from users.settings import DATABASE_URL - -config.set_main_option("sqlalchemy.url", DATABASE_URL.replace("asyncpg", "psycopg2")) - - -target_metadata = mapper_registry.metadata - -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - - -def run_migrations_offline(): - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ - url = config.get_main_option("sqlalchemy.url") - context.configure( - url=url, - target_metadata=target_metadata, - literal_binds=True, - dialect_opts={"paramstyle": "named"}, - ) - - with context.begin_transaction(): - context.run_migrations() - - -def run_migrations_online(): - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - connectable = engine_from_config( - config.get_section(config.config_ini_section), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) - - with connectable.connect() as connection: - context.configure(connection=connection, target_metadata=target_metadata) - - with context.begin_transaction(): - context.run_migrations() - - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() diff --git a/users-backend/alembic/script.py.mako b/users-backend/alembic/script.py.mako deleted file mode 100644 index 2c0156303a..0000000000 --- a/users-backend/alembic/script.py.mako +++ /dev/null @@ -1,24 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} - -""" -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} -branch_labels = ${repr(branch_labels)} -depends_on = ${repr(depends_on)} - - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} diff --git a/users-backend/alembic/versions/1d5fd146e8a5_import_user_model.py b/users-backend/alembic/versions/1d5fd146e8a5_import_user_model.py deleted file mode 100644 index c1bd9ce8ef..0000000000 --- a/users-backend/alembic/versions/1d5fd146e8a5_import_user_model.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Import user model - -Revision ID: 1d5fd146e8a5 -Revises: -Create Date: 2021-01-28 02:55:43.629000 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '1d5fd146e8a5' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('users', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('full_name', sa.String(length=300), nullable=False), - sa.Column('password', sa.String(length=128), nullable=False), - sa.Column('username', sa.String(length=100), nullable=True), - sa.Column('email', sa.String(length=254), nullable=False), - sa.Column('name', sa.String(length=300), nullable=False), - sa.Column('gender', sa.String(length=10), nullable=False), - sa.Column('date_birth', sa.Date(), nullable=True), - sa.Column('open_to_recruiting', sa.Boolean(), nullable=False), - sa.Column('open_to_newsletter', sa.Boolean(), nullable=False), - sa.Column('country', sa.String(length=50), nullable=False), - sa.Column('date_joined', sa.DateTime(timezone=True), nullable=False), - sa.Column('last_login', sa.DateTime(timezone=True), nullable=True), - sa.Column('is_active', sa.Boolean(), nullable=False), - sa.Column('is_staff', sa.Boolean(), nullable=False), - sa.Column('is_superuser', sa.Boolean(), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('users') - # ### end Alembic commands ### diff --git a/users-backend/alembic/versions/2e5e687d84d9_store_jwt_auth_id.py b/users-backend/alembic/versions/2e5e687d84d9_store_jwt_auth_id.py deleted file mode 100644 index 54eddfb16d..0000000000 --- a/users-backend/alembic/versions/2e5e687d84d9_store_jwt_auth_id.py +++ /dev/null @@ -1,28 +0,0 @@ -"""store jwt auth id - -Revision ID: 2e5e687d84d9 -Revises: c998e741da04 -Create Date: 2021-03-23 01:17:49.193077 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '2e5e687d84d9' -down_revision = 'c998e741da04' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('users', sa.Column('jwt_auth_id', sa.Integer(), server_default='1', nullable=False)) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('users', 'jwt_auth_id') - # ### end Alembic commands ### diff --git a/users-backend/alembic/versions/c998e741da04_make_email_unique.py b/users-backend/alembic/versions/c998e741da04_make_email_unique.py deleted file mode 100644 index c394cde695..0000000000 --- a/users-backend/alembic/versions/c998e741da04_make_email_unique.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Make email unique - -Revision ID: c998e741da04 -Revises: 1d5fd146e8a5 -Create Date: 2021-01-31 21:10:18.748473 - -""" -from alembic import op - - -# revision identifiers, used by Alembic. -revision = 'c998e741da04' -down_revision = '1d5fd146e8a5' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_unique_constraint(None, 'users', ['email']) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'users', type_='unique') - # ### end Alembic commands ### diff --git a/users-backend/gunicorn.conf.py b/users-backend/gunicorn.conf.py deleted file mode 100644 index c26c50f6aa..0000000000 --- a/users-backend/gunicorn.conf.py +++ /dev/null @@ -1,5 +0,0 @@ -import multiprocessing - -bind = "0.0.0.0:8000" -workers = multiprocessing.cpu_count() * 2 + 1 -worker_class = "uvicorn.workers.UvicornWorker" diff --git a/users-backend/main.py b/users-backend/main.py deleted file mode 100644 index ddd5997185..0000000000 --- a/users-backend/main.py +++ /dev/null @@ -1,101 +0,0 @@ -import logging -import subprocess -import sys -from io import StringIO - -from mangum import Mangum -from pythonit_toolkit.sentry.sentry import configure_sentry -from pythonit_toolkit.starlette_backend.middleware import pastaporto_auth_middleware -from sentry_sdk.integrations.asgi import SentryAsgiMiddleware -from starlette.applications import Starlette -from starlette.middleware import Middleware -from starlette.middleware.sessions import SessionMiddleware -from starlette.routing import Route -from users.api.views import GraphQL -from users.db import get_engine, get_session -from users.domain.repository import UsersRepository -from users.internal_api.views import GraphQL as InternalGraphQL -from users.settings import DEBUG, ENVIRONMENT, PASTAPORTO_SECRET, SECRET_KEY, SENTRY_DSN -from users.social_auth.views import google_login, google_login_auth - -if SENTRY_DSN: - configure_sentry(dsn=str(SENTRY_DSN), env=ENVIRONMENT) - -logging.basicConfig(level=logging.INFO) -logging.getLogger("sqlalchemy.engine.Engine").disabled = True - -routes = [ - Route("/graphql", GraphQL()), - Route("/internal-api", InternalGraphQL()), - Route("/login/google", google_login), - Route("/login/google/auth", google_login_auth, name="auth"), -] - -app = Starlette( - debug=DEBUG, - routes=routes, - middleware=[ - Middleware(SessionMiddleware, secret_key=str(SECRET_KEY)), - pastaporto_auth_middleware(str(PASTAPORTO_SECRET)), - ], -) - - -@app.middleware("http") -async def repositories_middleware(request, call_next): - request.state.users_repository = UsersRepository(request.state.session) - return await call_next(request) - - -@app.middleware("http") -async def async_session_middleware(request, call_next): - async with get_session(request.app.state.engine) as session: - request.state.session = session - return await call_next(request) - - -@app.on_event("startup") -async def startup(): - app.state.engine = get_engine() - - -@app.on_event("shutdown") -async def shutdown(): - await app.state.engine.dispose() - - -wrapped_app = SentryAsgiMiddleware(app) - - -def handler(event, context): - if command := event.get("_cli_command"): # noqa - native_stdout = sys.stdout - native_stderr = sys.stderr - output_buffer = StringIO() - - try: - sys.stdout = output_buffer - sys.stderr = output_buffer - - if command.get("action") == "migrate": - result = subprocess.check_output( - "/home/app/.venv/bin/alembic upgrade head", - shell=True, - stderr=subprocess.STDOUT, - ) - output_buffer.write(_to_native(result)) - finally: - sys.stdout = native_stdout - sys.stderr = native_stderr - - return {"output": output_buffer.getvalue()} - - asgi_handler = Mangum(wrapped_app) - response = asgi_handler(event, context) - return response - - -def _to_native(x, charset=sys.getdefaultencoding(), errors="strict"): # noqa - if x is None or isinstance(x, str): - return x - return x.decode(charset, errors) diff --git a/users-backend/poetry.lock b/users-backend/poetry.lock deleted file mode 100644 index 90cef8b0cc..0000000000 --- a/users-backend/poetry.lock +++ /dev/null @@ -1,2123 +0,0 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. - -[[package]] -name = "alembic" -version = "1.10.2" -description = "A database migration tool for SQLAlchemy." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "alembic-1.10.2-py3-none-any.whl", hash = "sha256:8b48368f6533c064b39c024e1daba15ae7f947eac84185c28c06bbe1301a5497"}, - {file = "alembic-1.10.2.tar.gz", hash = "sha256:457eafbdc0769d855c2c92cbafe6b7f319f916c80cf4ed02b8f394f38b51b89d"}, -] - -[package.dependencies] -Mako = "*" -SQLAlchemy = ">=1.3.0" -typing-extensions = ">=4" - -[package.extras] -tz = ["python-dateutil"] - -[[package]] -name = "anyio" -version = "3.6.2" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" -optional = false -python-versions = ">=3.6.2" -files = [ - {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, - {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16,<0.22)"] - -[[package]] -name = "argon2-cffi" -version = "20.1.0" -description = "The secure Argon2 password hashing algorithm." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "argon2-cffi-20.1.0.tar.gz", hash = "sha256:d8029b2d3e4b4cea770e9e5a0104dd8fa185c1724a0f01528ae4826a6d25f97d"}, - {file = "argon2_cffi-20.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:6ea92c980586931a816d61e4faf6c192b4abce89aa767ff6581e6ddc985ed003"}, - {file = "argon2_cffi-20.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:05a8ac07c7026542377e38389638a8a1e9b78f1cd8439cd7493b39f08dd75fbf"}, - {file = "argon2_cffi-20.1.0-cp27-cp27m-win32.whl", hash = "sha256:0bf066bc049332489bb2d75f69216416329d9dc65deee127152caeb16e5ce7d5"}, - {file = "argon2_cffi-20.1.0-cp27-cp27m-win_amd64.whl", hash = "sha256:57358570592c46c420300ec94f2ff3b32cbccd10d38bdc12dc6979c4a8484fbc"}, - {file = "argon2_cffi-20.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7d455c802727710e9dfa69b74ccaab04568386ca17b0ad36350b622cd34606fe"}, - {file = "argon2_cffi-20.1.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:b160416adc0f012fb1f12588a5e6954889510f82f698e23ed4f4fa57f12a0647"}, - {file = "argon2_cffi-20.1.0-cp35-cp35m-win32.whl", hash = "sha256:9bee3212ba4f560af397b6d7146848c32a800652301843df06b9e8f68f0f7361"}, - {file = "argon2_cffi-20.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:392c3c2ef91d12da510cfb6f9bae52512a4552573a9e27600bdb800e05905d2b"}, - {file = "argon2_cffi-20.1.0-cp36-cp36m-win32.whl", hash = "sha256:ba7209b608945b889457f949cc04c8e762bed4fe3fec88ae9a6b7765ae82e496"}, - {file = "argon2_cffi-20.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:da7f0445b71db6d3a72462e04f36544b0de871289b0bc8a7cc87c0f5ec7079fa"}, - {file = "argon2_cffi-20.1.0-cp37-abi3-macosx_10_6_intel.whl", hash = "sha256:cc0e028b209a5483b6846053d5fd7165f460a1f14774d79e632e75e7ae64b82b"}, - {file = "argon2_cffi-20.1.0-cp37-cp37m-win32.whl", hash = "sha256:18dee20e25e4be86680b178b35ccfc5d495ebd5792cd00781548d50880fee5c5"}, - {file = "argon2_cffi-20.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6678bb047373f52bcff02db8afab0d2a77d83bde61cfecea7c5c62e2335cb203"}, - {file = "argon2_cffi-20.1.0-cp38-cp38-win32.whl", hash = "sha256:77e909cc756ef81d6abb60524d259d959bab384832f0c651ed7dcb6e5ccdbb78"}, - {file = "argon2_cffi-20.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:9dfd5197852530294ecb5795c97a823839258dfd5eb9420233c7cfedec2058f2"}, - {file = "argon2_cffi-20.1.0-cp39-cp39-win32.whl", hash = "sha256:e2db6e85c057c16d0bd3b4d2b04f270a7467c147381e8fd73cbbe5bc719832be"}, - {file = "argon2_cffi-20.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8a84934bd818e14a17943de8099d41160da4a336bcc699bb4c394bbb9b94bd32"}, - {file = "argon2_cffi-20.1.0-pp36-pypy36_pp73-macosx_10_7_x86_64.whl", hash = "sha256:b94042e5dcaa5d08cf104a54bfae614be502c6f44c9c89ad1535b2ebdaacbd4c"}, - {file = "argon2_cffi-20.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:8282b84ceb46b5b75c3a882b28856b8cd7e647ac71995e71b6705ec06fc232c3"}, - {file = "argon2_cffi-20.1.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:3aa804c0e52f208973845e8b10c70d8957c9e5a666f702793256242e9167c4e0"}, - {file = "argon2_cffi-20.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:36320372133a003374ef4275fbfce78b7ab581440dfca9f9471be3dd9a522428"}, -] - -[package.dependencies] -cffi = ">=1.0.0" -six = "*" - -[package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pre-commit", "pytest", "sphinx", "wheel"] -docs = ["sphinx"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] - -[[package]] -name = "asgi-lifespan" -version = "1.0.1" -description = "Programmatic startup/shutdown of ASGI apps." -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "asgi-lifespan-1.0.1.tar.gz", hash = "sha256:9a33e7da2073c4764bc79bd6136501d6c42f60e3d2168ba71235e84122eadb7f"}, - {file = "asgi_lifespan-1.0.1-py3-none-any.whl", hash = "sha256:9ea969dc5eb5cf08e52c08dce6f61afcadd28112e72d81c972b1d8eb8691ab53"}, -] - -[package.dependencies] -sniffio = "*" - -[[package]] -name = "asyncpg" -version = "0.21.0" -description = "An asyncio PostgreSQL driver" -category = "main" -optional = false -python-versions = ">=3.5.0" -files = [ - {file = "asyncpg-0.21.0-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:09badce47a4645cfe523cc8a182bd047d5d62af0caaea77935e6a3c9e77dc364"}, - {file = "asyncpg-0.21.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b7807bfedd24dd15cfb2c17c60977ce01410615ecc285268b5144a944ec97ff"}, - {file = "asyncpg-0.21.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dfd491e9865e64a3e91f1587b1d88d71dde1cfb850429253a73d4d44b98c3a0f"}, - {file = "asyncpg-0.21.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:8587e206d78e739ca83a40c9982e03b28f8904c95a54dc782da99e86cf768f73"}, - {file = "asyncpg-0.21.0-cp35-cp35m-win32.whl", hash = "sha256:b1b10916c006e5c2c0dcd5dadeb38cbf61ecd20d66c50164e82f31c22c7e329d"}, - {file = "asyncpg-0.21.0-cp35-cp35m-win_amd64.whl", hash = "sha256:22d161618b59e4b56fb2a5cc956aa9eeb336d07cae924a5b90c9aa1c2d137f15"}, - {file = "asyncpg-0.21.0-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:f2d1aa890ffd1ad062a38b7ff7488764b3da4b0a24e0c83d7bbb1d1a6609df15"}, - {file = "asyncpg-0.21.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e7bfb9269aeb11d78d50accf1be46823683ced99209b7199e307cdf7da849522"}, - {file = "asyncpg-0.21.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:68f7981f65317a5d5f497ec76919b488dbe0e838f8b924e7517a680bdca0f308"}, - {file = "asyncpg-0.21.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:a4c1feb285ec3807ecd5b54ab718a3d065bb55c93ebaf800670eadde31484be8"}, - {file = "asyncpg-0.21.0-cp36-cp36m-win32.whl", hash = "sha256:dddf4d4c5e781310a36529c3c87c1746837c2d2c7ec0f2ec4e4f06450d83c50a"}, - {file = "asyncpg-0.21.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7ee29c4707eb8fb3d3a0348ac4495e06f4afaca3ee38c3bebedc9c8b239125ff"}, - {file = "asyncpg-0.21.0-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:4421407b07b4e22291a226d9de0bf6f3ea8158aa1c12d83bfedbf5c22e13cd55"}, - {file = "asyncpg-0.21.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:aa2e0cb14c01a2f58caeeca7196681b30aa22dd22c82845560b401df5e98e171"}, - {file = "asyncpg-0.21.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:28584783dd0d21b2a0db3bfe54fb12f21425a4cc015e4419083ea99e6de0de9b"}, - {file = "asyncpg-0.21.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:915cebc8a7693c8a5e89804fa106678dbedcc50d0270ebab0b75f16e668bd59b"}, - {file = "asyncpg-0.21.0-cp37-cp37m-win32.whl", hash = "sha256:308b8ba32c42ea1ed84c034320678ec307296bb4faf3fbbeb9f9e20b46db99a5"}, - {file = "asyncpg-0.21.0-cp37-cp37m-win_amd64.whl", hash = "sha256:888593b6688faa7ec1c97ff7f2ca3b5a5b8abb15478fe2a13c5012b607a28737"}, - {file = "asyncpg-0.21.0-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:ecd5232cf64f58caac3b85103f1223fdf20e9eb43bfa053c56ef9e5dd76ab099"}, - {file = "asyncpg-0.21.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3ade59cef35bffae6dbc6f5f3ef56e1d53c67f0a7adc3cc4c714f07568d2d717"}, - {file = "asyncpg-0.21.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ea26604932719b3612541e606508d9d604211f56a65806ccf8c92c64104f4f8a"}, - {file = "asyncpg-0.21.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e51d1a012b779e0ebf0195f80d004f65d3c60cc06f0fa1cef9d3e536262abbd"}, - {file = "asyncpg-0.21.0-cp38-cp38-win32.whl", hash = "sha256:615c7e3adb46e1f2e3aff45e4ee9401b4f24f9f7153e5530a0753369be72a5c6"}, - {file = "asyncpg-0.21.0-cp38-cp38-win_amd64.whl", hash = "sha256:823eca36108bd64a8600efe7bbf1230aa00f2defa3be42852f3b61ab40cf1226"}, - {file = "asyncpg-0.21.0.tar.gz", hash = "sha256:53cb2a0eb326f61e34ef4da2db01d87ce9c0ebe396f65a295829df334e31863f"}, -] - -[package.extras] -dev = ["Cython (==0.29.20)", "Sphinx (>=1.7.3,<1.8.0)", "flake8 (>=3.7.9,<3.8.0)", "pycodestyle (>=2.5.0,<2.6.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "uvloop (>=0.14.0,<0.15.0)"] -docs = ["Sphinx (>=1.7.3,<1.8.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)"] -test = ["flake8 (>=3.7.9,<3.8.0)", "pycodestyle (>=2.5.0,<2.6.0)", "uvloop (>=0.14.0,<0.15.0)"] - -[[package]] -name = "authlib" -version = "0.15.6" -description = "The ultimate Python library in building OAuth and OpenID Connect servers." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "Authlib-0.15.6-py2.py3-none-any.whl", hash = "sha256:6de4508ba8125e438a35bcd910d55df7087dccd3dd8517095c2bd9853c372ec1"}, - {file = "Authlib-0.15.6.tar.gz", hash = "sha256:2988fdf7d0a5c416f5a37ca4b1e7cee360094940229bc97909aed25880326c72"}, -] - -[package.dependencies] -cryptography = "*" - -[package.extras] -client = ["requests"] - -[[package]] -name = "awslambdaric" -version = "2.0.4" -description = "AWS Lambda Runtime Interface Client for Python" -category = "main" -optional = true -python-versions = ">=3.6" -files = [ - {file = "awslambdaric-2.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e90053614f0e5e5d6d6ae6d164412ce95b5d549c6fb0f6ff4290d77c5e9d3e5"}, - {file = "awslambdaric-2.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bad98f2f94cecc90b89ac4e1d4feed96eb664e13c29b7ce232444cc9358e0d36"}, - {file = "awslambdaric-2.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11a365164efec105aa670259dfe473d9609da8f6f2e468790b2dfc24969bfff1"}, - {file = "awslambdaric-2.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe76893a1b42bcee4c91c6456092d2a42455818756e8f62d50e8c5adb22fa9e7"}, - {file = "awslambdaric-2.0.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2eb2fdb1ae0f84669d37f193f247fa115a282a7777e051ced3a33620d6280646"}, - {file = "awslambdaric-2.0.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d64dcba8da9dbea62644133a48c75376a37bfe0f84096ad73bf7fc5b2eb31fc7"}, - {file = "awslambdaric-2.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc7072f642fdd215387d4921bbd5ac91b96a4a705bce5e7853622d09fe59f57d"}, - {file = "awslambdaric-2.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:676a741ad8f3aa27d651bcf3a2b83d5cee815f99c8b2b9abef3cb22ca7b29698"}, - {file = "awslambdaric-2.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8f280b25d8a7ae6b6ff92a9bbc6567b984264be8ef3e0fcb0402a1247f6c75d"}, - {file = "awslambdaric-2.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38f8ae67ecb5b4e9f7fc42746ee39765dd7ddab359cb7e8ebfda1de0f0c0b059"}, - {file = "awslambdaric-2.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:387b94cb0358662ae2b203f0aa2af25e80c6a2019a6b569f733ecd993a4f53d2"}, - {file = "awslambdaric-2.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63a82d21d66146b3fde7eb6086abd058b75bdcab4a02b02afe0e8e4a45edfb5b"}, - {file = "awslambdaric-2.0.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:059c7a66d4470169e01620d93f07424b80d302e3736cd11e68373f293a41e396"}, - {file = "awslambdaric-2.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fd0e1b3891987fa7ebb0c08d24c76af5fc17466f6efdfa9a59848dfb23930ec"}, - {file = "awslambdaric-2.0.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b0781bd41c20a2f2a0b018464a1daa376f663bd5eb7b0b6ba78f483681b1519"}, - {file = "awslambdaric-2.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbbd24446ce2f876335b178f04aa4ec7ec480afc0f9621ebfdd5f55ad4b7c06e"}, - {file = "awslambdaric-2.0.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19da28e8c892b1c52a9db4d2b986af303932e3a4c4632eb0c5d5eb6a673c6022"}, - {file = "awslambdaric-2.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efff2292fc8f8484eb094ffd77808a67815353be898a7f0b33ce51b841af691"}, - {file = "awslambdaric-2.0.4.tar.gz", hash = "sha256:dad646f566aa7ec9b7179f16ca6741a2bea148abec6ed5947f86d00607e0a9a2"}, -] - -[package.dependencies] -simplejson = "3.17.2" - -[[package]] -name = "boto3" -version = "1.26.91" -description = "The AWS SDK for Python" -category = "main" -optional = false -python-versions = ">= 3.7" -files = [ - {file = "boto3-1.26.91-py3-none-any.whl", hash = "sha256:3ce2225a61832d69831d669d912424ea3863268ca1cfa2a82203bb90952acefa"}, - {file = "boto3-1.26.91.tar.gz", hash = "sha256:278d896e9090a976f41ec68da5c572bc4e5b7cb1e515f1898fee8cb2fadfb50d"}, -] - -[package.dependencies] -botocore = ">=1.29.91,<1.30.0" -jmespath = ">=0.7.1,<2.0.0" -s3transfer = ">=0.6.0,<0.7.0" - -[package.extras] -crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] - -[[package]] -name = "botocore" -version = "1.29.91" -description = "Low-level, data-driven core of boto 3." -category = "main" -optional = false -python-versions = ">= 3.7" -files = [ - {file = "botocore-1.29.91-py3-none-any.whl", hash = "sha256:4ed6a488aee1b42367eace71f7d0993dda05b02eebd7dcdd78db5c9ce3d80da5"}, - {file = "botocore-1.29.91.tar.gz", hash = "sha256:a8a800a2a945da807758cace539fc5b5ec1d5082ce363799d3a3870c2c4ed6fc"}, -] - -[package.dependencies] -jmespath = ">=0.7.1,<2.0.0" -python-dateutil = ">=2.1,<3.0.0" -urllib3 = ">=1.25.4,<1.27" - -[package.extras] -crt = ["awscrt (==0.16.9)"] - -[[package]] -name = "cached-property" -version = "1.5.2" -description = "A decorator for caching properties in classes." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, - {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, -] - -[[package]] -name = "certifi" -version = "2022.12.7" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] - -[[package]] -name = "cffi" -version = "1.15.1" -description = "Foreign Function Interface for Python calling C code." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "3.1.0" -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 = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, -] - -[[package]] -name = "click" -version = "7.1.2" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, -] - -[[package]] -name = "click-completion" -version = "0.5.2" -description = "Fish, Bash, Zsh and PowerShell completion for Click" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "click-completion-0.5.2.tar.gz", hash = "sha256:5bf816b81367e638a190b6e91b50779007d14301b3f9f3145d68e3cade7bce86"}, -] - -[package.dependencies] -click = "*" -jinja2 = "*" -shellingham = "*" -six = "*" - -[[package]] -name = "click-default-group" -version = "1.2.2" -description = "Extends click.Group to invoke a command without explicit subcommand name" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "click-default-group-1.2.2.tar.gz", hash = "sha256:d9560e8e8dfa44b3562fbc9425042a0fd6d21956fcc2db0077f63f34253ab904"}, -] - -[package.dependencies] -click = "*" - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "commonmark" -version = "0.9.1" -description = "Python parser for the CommonMark Markdown spec" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, - {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, -] - -[package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] - -[[package]] -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 = [ - {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, - {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, - {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, - {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, - {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, - {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, - {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, - {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, - {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, - {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, - {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, - {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, - {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, - {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, - {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, - {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, - {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, - {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, - {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, - {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, - {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, - {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, - {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, - {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, -] - -[package.extras] -toml = ["toml"] - -[[package]] -name = "cryptography" -version = "39.0.2" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:2725672bb53bb92dc7b4150d233cd4b8c59615cd8288d495eaa86db00d4e5c06"}, - {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:23df8ca3f24699167daf3e23e51f7ba7334d504af63a94af468f468b975b7dd7"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:eb40fe69cfc6f5cdab9a5ebd022131ba21453cf7b8a7fd3631f45bbf52bed612"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc0521cce2c1d541634b19f3ac661d7a64f9555135e9d8af3980965be717fd4a"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffd394c7896ed7821a6d13b24657c6a34b6e2650bd84ae063cf11ccffa4f1a97"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:e8a0772016feeb106efd28d4a328e77dc2edae84dfbac06061319fdb669ff828"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8f35c17bd4faed2bc7797d2a66cbb4f986242ce2e30340ab832e5d99ae60e011"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b49a88ff802e1993b7f749b1eeb31134f03c8d5c956e3c125c75558955cda536"}, - {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c682e736513db7d04349b4f6693690170f95aac449c56f97415c6980edef5"}, - {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:d7d84a512a59f4412ca8549b01f94be4161c94efc598bf09d027d67826beddc0"}, - {file = "cryptography-39.0.2-cp36-abi3-win32.whl", hash = "sha256:c43ac224aabcbf83a947eeb8b17eaf1547bce3767ee2d70093b461f31729a480"}, - {file = "cryptography-39.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:788b3921d763ee35dfdb04248d0e3de11e3ca8eb22e2e48fef880c42e1f3c8f9"}, - {file = "cryptography-39.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d15809e0dbdad486f4ad0979753518f47980020b7a34e9fc56e8be4f60702fac"}, - {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:50cadb9b2f961757e712a9737ef33d89b8190c3ea34d0fb6675e00edbe35d074"}, - {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:103e8f7155f3ce2ffa0049fe60169878d47a4364b277906386f8de21c9234aa1"}, - {file = "cryptography-39.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6236a9610c912b129610eb1a274bdc1350b5df834d124fa84729ebeaf7da42c3"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e944fe07b6f229f4c1a06a7ef906a19652bdd9fd54c761b0ff87e83ae7a30354"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:35d658536b0a4117c885728d1a7032bdc9a5974722ae298d6c533755a6ee3915"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:30b1d1bfd00f6fc80d11300a29f1d8ab2b8d9febb6ed4a38a76880ec564fae84"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e029b844c21116564b8b61216befabca4b500e6816fa9f0ba49527653cae2108"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fa507318e427169ade4e9eccef39e9011cdc19534f55ca2f36ec3f388c1f70f3"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8bc0008ef798231fac03fe7d26e82d601d15bd16f3afaad1c6113771566570f3"}, - {file = "cryptography-39.0.2.tar.gz", hash = "sha256:bc5b871e977c8ee5a1bbc42fa8d19bcc08baf0c51cbf1586b0e87a2694dde42f"}, -] - -[package.dependencies] -cffi = ">=1.12" - -[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)"] -pep8test = ["black", "check-manifest", "mypy", "ruff", "types-pytz", "types-requests"] -sdist = ["setuptools-rust (>=0.11.4)"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist", "pytz"] -test-randomorder = ["pytest-randomly"] -tox = ["tox"] - -[[package]] -name = "cucumber-tag-expressions" -version = "4.1.0" -description = "Provides tag-expression parser for cucumber/behave" -category = "dev" -optional = false -python-versions = ">=2.7" -files = [ - {file = "cucumber-tag-expressions-4.1.0.tar.gz", hash = "sha256:e314d5fed6eebb2f90380271f562248fb15e18636764faf40f4dde4b28b1f960"}, -] - -[package.extras] -develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest-html (>=1.19.0)", "tox (>=2.9)"] - -[[package]] -name = "dnspython" -version = "2.3.0" -description = "DNS toolkit" -category = "main" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "dnspython-2.3.0-py3-none-any.whl", hash = "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46"}, - {file = "dnspython-2.3.0.tar.gz", hash = "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9"}, -] - -[package.extras] -curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] -dnssec = ["cryptography (>=2.6,<40.0)"] -doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.11.0)"] -doq = ["aioquic (>=0.9.20)"] -idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.23)"] -wmi = ["wmi (>=1.5.1,<2.0.0)"] - -[[package]] -name = "email-validator" -version = "1.3.1" -description = "A robust email address syntax and deliverability validation library." -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "email_validator-1.3.1-py2.py3-none-any.whl", hash = "sha256:49a72f5fa6ed26be1c964f0567d931d10bf3fdeeacdf97bc26ef1cd2a44e0bda"}, - {file = "email_validator-1.3.1.tar.gz", hash = "sha256:d178c5c6fa6c6824e9b04f199cf23e79ac15756786573c190d2ad13089411ad2"}, -] - -[package.dependencies] -dnspython = ">=1.15.0" -idna = ">=2.0.0" - -[[package]] -name = "factory-boy" -version = "3.2.1" -description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "factory_boy-3.2.1-py2.py3-none-any.whl", hash = "sha256:eb02a7dd1b577ef606b75a253b9818e6f9eaf996d94449c9d5ebb124f90dc795"}, - {file = "factory_boy-3.2.1.tar.gz", hash = "sha256:a98d277b0c047c75eb6e4ab8508a7f81fb03d2cb21986f627913546ef7a2a55e"}, -] - -[package.dependencies] -Faker = ">=0.7.0" - -[package.extras] -dev = ["Django", "Pillow", "SQLAlchemy", "coverage", "flake8", "isort", "mongoengine", "tox", "wheel (>=0.32.0)", "zest.releaser[recommended]"] -doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"] - -[[package]] -name = "faker" -version = "17.6.0" -description = "Faker is a Python package that generates fake data for you." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Faker-17.6.0-py3-none-any.whl", hash = "sha256:5aaa16fa9cfde7d117eef70b6b293a705021e57158f3fa6b44ed1b70202d2065"}, - {file = "Faker-17.6.0.tar.gz", hash = "sha256:51f37ff9df710159d6d736d0ba1c75e063430a8c806b91334d7794305b5a6114"}, -] - -[package.dependencies] -python-dateutil = ">=2.4" - -[[package]] -name = "fancycompleter" -version = "0.9.1" -description = "colorful TAB completion for Python prompt" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "fancycompleter-0.9.1-py3-none-any.whl", hash = "sha256:dd076bca7d9d524cc7f25ec8f35ef95388ffef9ef46def4d3d25e9b044ad7080"}, - {file = "fancycompleter-0.9.1.tar.gz", hash = "sha256:09e0feb8ae242abdfd7ef2ba55069a46f011814a80fe5476be48f51b00247272"}, -] - -[package.dependencies] -pyreadline = {version = "*", markers = "platform_system == \"Windows\""} -pyrepl = ">=0.8.2" - -[[package]] -name = "graphql-core" -version = "3.1.7" -description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." -category = "main" -optional = false -python-versions = ">=3.6,<4" -files = [ - {file = "graphql-core-3.1.7.tar.gz", hash = "sha256:62ec192150ccecd9a18cfb79e3e72eb7d1fd68fb594ef19c40099b6deec8ef0c"}, - {file = "graphql_core-3.1.7-py3-none-any.whl", hash = "sha256:9b460f60320be01c7f3b1766cf3e406933003008055079b9d983b8f3988f4400"}, -] - -[[package]] -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 = [ - {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"}, - {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"}, - {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"}, - {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"}, - {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"}, - {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, - {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, - {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, - {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, - {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, - {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, - {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, - {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, - {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"}, - {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"}, - {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"}, - {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"}, - {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"}, - {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"}, - {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"}, - {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"}, - {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"}, - {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"}, - {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"}, - {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"}, - {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"}, - {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"}, - {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, - {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, - {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, - {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, - {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, - {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, - {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, - {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, - {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, - {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, - {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, -] - -[package.extras] -docs = ["Sphinx", "docutils (<0.18)"] -test = ["objgraph", "psutil"] - -[[package]] -name = "gunicorn" -version = "20.1.0" -description = "WSGI HTTP Server for UNIX" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, - {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, -] - -[package.dependencies] -setuptools = ">=3.0" - -[package.extras] -eventlet = ["eventlet (>=0.24.1)"] -gevent = ["gevent (>=1.4.0)"] -setproctitle = ["setproctitle"] -tornado = ["tornado (>=0.2)"] - -[[package]] -name = "h11" -version = "0.12.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, - {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, -] - -[[package]] -name = "httpcore" -version = "0.13.7" -description = "A minimal low-level HTTP client." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "httpcore-0.13.7-py3-none-any.whl", hash = "sha256:369aa481b014cf046f7067fddd67d00560f2f00426e79569d99cb11245134af0"}, - {file = "httpcore-0.13.7.tar.gz", hash = "sha256:036f960468759e633574d7c121afba48af6419615d36ab8ede979f1ad6276fa3"}, -] - -[package.dependencies] -anyio = ">=3.0.0,<4.0.0" -h11 = ">=0.11,<0.13" -sniffio = ">=1.0.0,<2.0.0" - -[package.extras] -http2 = ["h2 (>=3,<5)"] - -[[package]] -name = "httptools" -version = "0.5.0" -description = "A collection of framework independent HTTP protocol utils." -category = "main" -optional = false -python-versions = ">=3.5.0" -files = [ - {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f470c79061599a126d74385623ff4744c4e0f4a0997a353a44923c0b561ee51"}, - {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e90491a4d77d0cb82e0e7a9cb35d86284c677402e4ce7ba6b448ccc7325c5421"}, - {file = "httptools-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1d2357f791b12d86faced7b5736dea9ef4f5ecdc6c3f253e445ee82da579449"}, - {file = "httptools-0.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f90cd6fd97c9a1b7fe9215e60c3bd97336742a0857f00a4cb31547bc22560c2"}, - {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5230a99e724a1bdbbf236a1b58d6e8504b912b0552721c7c6b8570925ee0ccde"}, - {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3a47a34f6015dd52c9eb629c0f5a8a5193e47bf2a12d9a3194d231eaf1bc451a"}, - {file = "httptools-0.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:24bb4bb8ac3882f90aa95403a1cb48465de877e2d5298ad6ddcfdebec060787d"}, - {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e67d4f8734f8054d2c4858570cc4b233bf753f56e85217de4dfb2495904cf02e"}, - {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e5eefc58d20e4c2da82c78d91b2906f1a947ef42bd668db05f4ab4201a99f49"}, - {file = "httptools-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0297822cea9f90a38df29f48e40b42ac3d48a28637368f3ec6d15eebefd182f9"}, - {file = "httptools-0.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:557be7fbf2bfa4a2ec65192c254e151684545ebab45eca5d50477d562c40f986"}, - {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:54465401dbbec9a6a42cf737627fb0f014d50dc7365a6b6cd57753f151a86ff0"}, - {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4d9ebac23d2de960726ce45f49d70eb5466725c0087a078866043dad115f850f"}, - {file = "httptools-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e8a34e4c0ab7b1ca17b8763613783e2458e77938092c18ac919420ab8655c8c1"}, - {file = "httptools-0.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f659d7a48401158c59933904040085c200b4be631cb5f23a7d561fbae593ec1f"}, - {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1616b3ba965cd68e6f759eeb5d34fbf596a79e84215eeceebf34ba3f61fdc7"}, - {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3625a55886257755cb15194efbf209584754e31d336e09e2ffe0685a76cb4b60"}, - {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:72ad589ba5e4a87e1d404cc1cb1b5780bfcb16e2aec957b88ce15fe879cc08ca"}, - {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:850fec36c48df5a790aa735417dca8ce7d4b48d59b3ebd6f83e88a8125cde324"}, - {file = "httptools-0.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f222e1e9d3f13b68ff8a835574eda02e67277d51631d69d7cf7f8e07df678c86"}, - {file = "httptools-0.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3cb8acf8f951363b617a8420768a9f249099b92e703c052f9a51b66342eea89b"}, - {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550059885dc9c19a072ca6d6735739d879be3b5959ec218ba3e013fd2255a11b"}, - {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a04fe458a4597aa559b79c7f48fe3dceabef0f69f562daf5c5e926b153817281"}, - {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d0c1044bce274ec6711f0770fd2d5544fe392591d204c68328e60a46f88843b"}, - {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c6eeefd4435055a8ebb6c5cc36111b8591c192c56a95b45fe2af22d9881eee25"}, - {file = "httptools-0.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b65be160adcd9de7a7e6413a4966665756e263f0d5ddeffde277ffeee0576a5"}, - {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fe9c766a0c35b7e3d6b6939393c8dfdd5da3ac5dec7f971ec9134f284c6c36d6"}, - {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:85b392aba273566c3d5596a0a490978c085b79700814fb22bfd537d381dd230c"}, - {file = "httptools-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e3088f4ed33947e16fd865b8200f9cfae1144f41b64a8cf19b599508e096bc"}, - {file = "httptools-0.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c2a56b6aad7cc8f5551d8e04ff5a319d203f9d870398b94702300de50190f63"}, - {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b571b281a19762adb3f48a7731f6842f920fa71108aff9be49888320ac3e24d"}, - {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa47ffcf70ba6f7848349b8a6f9b481ee0f7637931d91a9860a1838bfc586901"}, - {file = "httptools-0.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:bede7ee075e54b9a5bde695b4fc8f569f30185891796b2e4e09e2226801d09bd"}, - {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:64eba6f168803a7469866a9c9b5263a7463fa8b7a25b35e547492aa7322036b6"}, - {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4b098e4bb1174096a93f48f6193e7d9aa7071506a5877da09a783509ca5fff42"}, - {file = "httptools-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9423a2de923820c7e82e18980b937893f4aa8251c43684fa1772e341f6e06887"}, - {file = "httptools-0.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca1b7becf7d9d3ccdbb2f038f665c0f4857e08e1d8481cbcc1a86a0afcfb62b2"}, - {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:50d4613025f15f4b11f1c54bbed4761c0020f7f921b95143ad6d58c151198142"}, - {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8ffce9d81c825ac1deaa13bc9694c0562e2840a48ba21cfc9f3b4c922c16f372"}, - {file = "httptools-0.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:1af91b3650ce518d226466f30bbba5b6376dbd3ddb1b2be8b0658c6799dd450b"}, - {file = "httptools-0.5.0.tar.gz", hash = "sha256:295874861c173f9101960bba332429bb77ed4dcd8cdf5cee9922eb00e4f6bc09"}, -] - -[package.extras] -test = ["Cython (>=0.29.24,<0.30.0)"] - -[[package]] -name = "httpx" -version = "0.20.0" -description = "The next generation HTTP client." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "httpx-0.20.0-py3-none-any.whl", hash = "sha256:33af5aad9bdc82ef1fc89219c1e36f5693bf9cd0ebe330884df563445682c0f8"}, - {file = "httpx-0.20.0.tar.gz", hash = "sha256:09606d630f070d07f9ff28104fbcea429ea0014c1e89ac90b4d8de8286c40e7b"}, -] - -[package.dependencies] -certifi = "*" -charset-normalizer = "*" -httpcore = ">=0.13.3,<0.14.0" -rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10.0.0,<11.0.0)"] -http2 = ["h2 (>=3,<5)"] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "itsdangerous" -version = "1.1.0" -description = "Various helpers to pass data to untrusted environments and back." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, - {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, -] - -[[package]] -name = "jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jmespath" -version = "1.0.1" -description = "JSON Matching Expressions" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, - {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, -] - -[[package]] -name = "mako" -version = "1.2.4" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, - {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, -] - -[package.dependencies] -MarkupSafe = ">=0.9.2" - -[package.extras] -babel = ["Babel"] -lingua = ["lingua"] -testing = ["pytest"] - -[[package]] -name = "mangum" -version = "0.10.0" -description = "AWS Lambda & API Gateway support for ASGI" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mangum-0.10.0-py3-none-any.whl", hash = "sha256:cd8d2d515226099a5f3491da32c68c5db64a92f559bd9215a22b43f881ba874f"}, - {file = "mangum-0.10.0.tar.gz", hash = "sha256:17a8bd5d0c71b8c413d3b9a07857e9aafabc08a5876c672a561a7acaa30f0d1f"}, -] - -[package.dependencies] -typing-extensions = "*" - -[[package]] -name = "markupsafe" -version = "2.1.2" -description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, - {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, -] - -[[package]] -name = "mslex" -version = "0.3.0" -description = "shlex for windows" -category = "dev" -optional = false -python-versions = ">=3.5" -files = [ - {file = "mslex-0.3.0-py2.py3-none-any.whl", hash = "sha256:380cb14abf8fabf40e56df5c8b21a6d533dc5cbdcfe42406bbf08dda8f42e42a"}, - {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"}, -] - -[[package]] -name = "pdbpp" -version = "0.10.3" -description = "pdb++, a drop-in replacement for pdb" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pdbpp-0.10.3-py2.py3-none-any.whl", hash = "sha256:79580568e33eb3d6f6b462b1187f53e10cd8e4538f7d31495c9181e2cf9665d1"}, - {file = "pdbpp-0.10.3.tar.gz", hash = "sha256:d9e43f4fda388eeb365f2887f4e7b66ac09dce9b6236b76f63616530e2f669f5"}, -] - -[package.dependencies] -fancycompleter = ">=0.8" -pygments = "*" -wmctrl = "*" - -[package.extras] -funcsigs = ["funcsigs"] -testing = ["funcsigs", "pytest"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pprintpp" -version = "0.4.0" -description = "A drop-in replacement for pprint that's actually pretty" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pprintpp-0.4.0-py2.py3-none-any.whl", hash = "sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d"}, - {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, -] - -[[package]] -name = "psutil" -version = "5.9.4" -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.*" -files = [ - {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"}, - {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"}, - {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"}, - {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"}, - {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"}, - {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"}, - {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"}, - {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"}, - {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"}, - {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"}, - {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"}, - {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"}, - {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"}, - {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - -[[package]] -name = "psycopg2" -version = "2.9.5" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "psycopg2-2.9.5-cp310-cp310-win32.whl", hash = "sha256:d3ef67e630b0de0779c42912fe2cbae3805ebaba30cda27fea2a3de650a9414f"}, - {file = "psycopg2-2.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:4cb9936316d88bfab614666eb9e32995e794ed0f8f6b3b718666c22819c1d7ee"}, - {file = "psycopg2-2.9.5-cp311-cp311-win32.whl", hash = "sha256:093e3894d2d3c592ab0945d9eba9d139c139664dcf83a1c440b8a7aa9bb21955"}, - {file = "psycopg2-2.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:920bf418000dd17669d2904472efeab2b20546efd0548139618f8fa305d1d7ad"}, - {file = "psycopg2-2.9.5-cp36-cp36m-win32.whl", hash = "sha256:b9ac1b0d8ecc49e05e4e182694f418d27f3aedcfca854ebd6c05bb1cffa10d6d"}, - {file = "psycopg2-2.9.5-cp36-cp36m-win_amd64.whl", hash = "sha256:fc04dd5189b90d825509caa510f20d1d504761e78b8dfb95a0ede180f71d50e5"}, - {file = "psycopg2-2.9.5-cp37-cp37m-win32.whl", hash = "sha256:922cc5f0b98a5f2b1ff481f5551b95cd04580fd6f0c72d9b22e6c0145a4840e0"}, - {file = "psycopg2-2.9.5-cp37-cp37m-win_amd64.whl", hash = "sha256:1e5a38aa85bd660c53947bd28aeaafb6a97d70423606f1ccb044a03a1203fe4a"}, - {file = "psycopg2-2.9.5-cp38-cp38-win32.whl", hash = "sha256:f5b6320dbc3cf6cfb9f25308286f9f7ab464e65cfb105b64cc9c52831748ced2"}, - {file = "psycopg2-2.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:1a5c7d7d577e0eabfcf15eb87d1e19314c8c4f0e722a301f98e0e3a65e238b4e"}, - {file = "psycopg2-2.9.5-cp39-cp39-win32.whl", hash = "sha256:322fd5fca0b1113677089d4ebd5222c964b1760e361f151cbb2706c4912112c5"}, - {file = "psycopg2-2.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:190d51e8c1b25a47484e52a79638a8182451d6f6dff99f26ad9bd81e5359a0fa"}, - {file = "psycopg2-2.9.5.tar.gz", hash = "sha256:a5246d2e683a972e2187a8714b5c2cf8156c064629f9a9b1a873c1730d9e245a"}, -] - -[[package]] -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 = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] - -[[package]] -name = "pydantic" -version = "1.10.6" -description = "Data validation and settings management using python type hints" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9289065611c48147c1dd1fd344e9d57ab45f1d99b0fb26c51f1cf72cd9bcd31"}, - {file = "pydantic-1.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c32b6bba301490d9bb2bf5f631907803135e8085b6aa3e5fe5a770d46dd0160"}, - {file = "pydantic-1.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd9b9e98068fa1068edfc9eabde70a7132017bdd4f362f8b4fd0abed79c33083"}, - {file = "pydantic-1.10.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c84583b9df62522829cbc46e2b22e0ec11445625b5acd70c5681ce09c9b11c4"}, - {file = "pydantic-1.10.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b41822064585fea56d0116aa431fbd5137ce69dfe837b599e310034171996084"}, - {file = "pydantic-1.10.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61f1f08adfaa9cc02e0cbc94f478140385cbd52d5b3c5a657c2fceb15de8d1fb"}, - {file = "pydantic-1.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:32937835e525d92c98a1512218db4eed9ddc8f4ee2a78382d77f54341972c0e7"}, - {file = "pydantic-1.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbd5c531b22928e63d0cb1868dee76123456e1de2f1cb45879e9e7a3f3f1779b"}, - {file = "pydantic-1.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e277bd18339177daa62a294256869bbe84df1fb592be2716ec62627bb8d7c81d"}, - {file = "pydantic-1.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f15277d720aa57e173954d237628a8d304896364b9de745dcb722f584812c7"}, - {file = "pydantic-1.10.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b243b564cea2576725e77aeeda54e3e0229a168bc587d536cd69941e6797543d"}, - {file = "pydantic-1.10.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3ce13a558b484c9ae48a6a7c184b1ba0e5588c5525482681db418268e5f86186"}, - {file = "pydantic-1.10.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3ac1cd4deed871dfe0c5f63721e29debf03e2deefa41b3ed5eb5f5df287c7b70"}, - {file = "pydantic-1.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:b1eb6610330a1dfba9ce142ada792f26bbef1255b75f538196a39e9e90388bf4"}, - {file = "pydantic-1.10.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4ca83739c1263a044ec8b79df4eefc34bbac87191f0a513d00dd47d46e307a65"}, - {file = "pydantic-1.10.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea4e2a7cb409951988e79a469f609bba998a576e6d7b9791ae5d1e0619e1c0f2"}, - {file = "pydantic-1.10.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53de12b4608290992a943801d7756f18a37b7aee284b9ffa794ee8ea8153f8e2"}, - {file = "pydantic-1.10.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:60184e80aac3b56933c71c48d6181e630b0fbc61ae455a63322a66a23c14731a"}, - {file = "pydantic-1.10.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:415a3f719ce518e95a92effc7ee30118a25c3d032455d13e121e3840985f2efd"}, - {file = "pydantic-1.10.6-cp37-cp37m-win_amd64.whl", hash = "sha256:72cb30894a34d3a7ab6d959b45a70abac8a2a93b6480fc5a7bfbd9c935bdc4fb"}, - {file = "pydantic-1.10.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3091d2eaeda25391405e36c2fc2ed102b48bac4b384d42b2267310abae350ca6"}, - {file = "pydantic-1.10.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:751f008cd2afe812a781fd6aa2fb66c620ca2e1a13b6a2152b1ad51553cb4b77"}, - {file = "pydantic-1.10.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12e837fd320dd30bd625be1b101e3b62edc096a49835392dcf418f1a5ac2b832"}, - {file = "pydantic-1.10.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d92831d0115874d766b1f5fddcdde0c5b6c60f8c6111a394078ec227fca6d"}, - {file = "pydantic-1.10.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:476f6674303ae7965730a382a8e8d7fae18b8004b7b69a56c3d8fa93968aa21c"}, - {file = "pydantic-1.10.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3a2be0a0f32c83265fd71a45027201e1278beaa82ea88ea5b345eea6afa9ac7f"}, - {file = "pydantic-1.10.6-cp38-cp38-win_amd64.whl", hash = "sha256:0abd9c60eee6201b853b6c4be104edfba4f8f6c5f3623f8e1dba90634d63eb35"}, - {file = "pydantic-1.10.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6195ca908045054dd2d57eb9c39a5fe86409968b8040de8c2240186da0769da7"}, - {file = "pydantic-1.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43cdeca8d30de9a897440e3fb8866f827c4c31f6c73838e3a01a14b03b067b1d"}, - {file = "pydantic-1.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c19eb5163167489cb1e0161ae9220dadd4fc609a42649e7e84a8fa8fff7a80f"}, - {file = "pydantic-1.10.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:012c99a9c0d18cfde7469aa1ebff922e24b0c706d03ead96940f5465f2c9cf62"}, - {file = "pydantic-1.10.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:528dcf7ec49fb5a84bf6fe346c1cc3c55b0e7603c2123881996ca3ad79db5bfc"}, - {file = "pydantic-1.10.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:163e79386c3547c49366e959d01e37fc30252285a70619ffc1b10ede4758250a"}, - {file = "pydantic-1.10.6-cp39-cp39-win_amd64.whl", hash = "sha256:189318051c3d57821f7233ecc94708767dd67687a614a4e8f92b4a020d4ffd06"}, - {file = "pydantic-1.10.6-py3-none-any.whl", hash = "sha256:acc6783751ac9c9bc4680379edd6d286468a1dc8d7d9906cd6f1186ed682b2b0"}, - {file = "pydantic-1.10.6.tar.gz", hash = "sha256:cf95adb0d1671fc38d8c43dd921ad5814a735e7d9b4d9e437c088002863854fd"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pygments" -version = "2.14.0" -description = "Pygments is a syntax highlighting package written in Python." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"}, - {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"}, -] - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pyjwt" -version = "2.0.1" -description = "JSON Web Token implementation in Python" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyJWT-2.0.1-py3-none-any.whl", hash = "sha256:b70b15f89dc69b993d8a8d32c299032d5355c82f9b5b7e851d1a6d706dffe847"}, - {file = "PyJWT-2.0.1.tar.gz", hash = "sha256:a5c70a06e1f33d81ef25eecd50d50bd30e34de1ca8b2b9fa3fe0daaabcf69bf7"}, -] - -[package.extras] -crypto = ["cryptography (>=3.3.1,<4.0.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.3.1,<4.0.0)", "mypy", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - -[[package]] -name = "pyreadline" -version = "2.1" -description = "A python implmementation of GNU readline." -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1"}, -] - -[[package]] -name = "pyrepl" -version = "0.9.0" -description = "A library for building flexible command line interfaces" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pyrepl-0.9.0.tar.gz", hash = "sha256:292570f34b5502e871bbb966d639474f2b57fbfcd3373c2d6a2f3d56e681a775"}, -] - -[[package]] -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 = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-dotenv" -version = "1.0.0" -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"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "python-multipart" -version = "0.0.5" -description = "A streaming multipart parser for Python" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"}, -] - -[package.dependencies] -six = ">=1.4.0" - -[[package]] -name = "pythonit-toolkit" -version = "0.1.81" -description = "" -category = "main" -optional = false -python-versions = ">=3.9,<4.0" -files = [ - {file = "pythonit-toolkit-0.1.81.tar.gz", hash = "sha256:ec92af55989f40cacc664aae8fbe05c31a5f295ac13abdff3503cd8ad83fc22a"}, - {file = "pythonit_toolkit-0.1.81-py3-none-any.whl", hash = "sha256:8b5b5aba6bb63dcd7eba523bc31de8b2e4a04dfadfa0a6e753d2f616d640f0b3"}, -] - -[package.dependencies] -boto3 = ">=1.17.39,<2.0.0" -httpx = ">=0.20.0,<0.21.0" -pydantic = "*" -PyJWT = "2.0.1" -sentry-sdk = ">=1.1.0,<2.0.0" -starlette = ">=0.14.2,<0.15.0" -strawberry-graphql = "*" - -[[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] - -[[package]] -name = "requests" -version = "2.28.2" -description = "Python HTTP for Humans." -category = "dev" -optional = false -python-versions = ">=3.7, <4" -files = [ - {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, - {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rfc3986" -version = "1.5.0" -description = "Validating URI References per RFC 3986" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, -] - -[package.dependencies] -idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} - -[package.extras] -idna2008 = ["idna"] - -[[package]] -name = "rich" -version = "10.16.2" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "dev" -optional = false -python-versions = ">=3.6.2,<4.0.0" -files = [ - {file = "rich-10.16.2-py3-none-any.whl", hash = "sha256:c59d73bd804c90f747c8d7b1d023b88f2a9ac2454224a4aeaf959b21eeb42d03"}, - {file = "rich-10.16.2.tar.gz", hash = "sha256:720974689960e06c2efdb54327f8bf0cdbdf4eae4ad73b6c94213cad405c371b"}, -] - -[package.dependencies] -colorama = ">=0.4.0,<0.5.0" -commonmark = ">=0.9.0,<0.10.0" -pygments = ">=2.6.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] - -[[package]] -name = "s3transfer" -version = "0.6.0" -description = "An Amazon S3 Transfer Manager" -category = "main" -optional = false -python-versions = ">= 3.7" -files = [ - {file = "s3transfer-0.6.0-py3-none-any.whl", hash = "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd"}, - {file = "s3transfer-0.6.0.tar.gz", hash = "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"}, -] - -[package.dependencies] -botocore = ">=1.12.36,<2.0a.0" - -[package.extras] -crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"] - -[[package]] -name = "sentinel" -version = "0.3.0" -description = "Create sentinel objects, akin to None, NotImplemented, Ellipsis" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "sentinel-0.3.0-py3-none-any.whl", hash = "sha256:bd8710dd26752039c668604f6be2aaf741b56f7811c5924a4dcdfd74359244f3"}, - {file = "sentinel-0.3.0.tar.gz", hash = "sha256:f28143aa4716dbc8f6193f5682176a3c33cd26aaae05d9ecf66c186a9887cc2d"}, -] - -[package.extras] -varname = ["varname (>=0.1)"] - -[[package]] -name = "sentry-sdk" -version = "1.16.0" -description = "Python client for Sentry (https://sentry.io)" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "sentry-sdk-1.16.0.tar.gz", hash = "sha256:a900845bd78c263d49695d48ce78a4bce1030bbd917e0b6cc021fc000c901113"}, - {file = "sentry_sdk-1.16.0-py2.py3-none-any.whl", hash = "sha256:633edefead34d976ff22e7edc367cdf57768e24bc714615ccae746d9d91795ae"}, -] - -[package.dependencies] -certifi = "*" -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -arq = ["arq (>=0.23)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)"] -httpx = ["httpx (>=0.16.0)"] -huey = ["huey (>=2)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "setuptools" -version = "67.6.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-67.6.0-py3-none-any.whl", hash = "sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"}, - {file = "setuptools-67.6.0.tar.gz", hash = "sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "shellingham" -version = "1.5.0.post1" -description = "Tool to Detect Surrounding Shell" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shellingham-1.5.0.post1-py2.py3-none-any.whl", hash = "sha256:368bf8c00754fd4f55afb7bbb86e272df77e4dc76ac29dbcbb81a59e9fc15744"}, - {file = "shellingham-1.5.0.post1.tar.gz", hash = "sha256:823bc5fb5c34d60f285b624e7264f4dda254bc803a3774a147bf99c0e3004a28"}, -] - -[[package]] -name = "simplejson" -version = "3.17.2" -description = "Simple, fast, extensible JSON encoder/decoder for Python" -category = "main" -optional = true -python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "simplejson-3.17.2-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8"}, - {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6"}, - {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:292c2e3f53be314cc59853bd20a35bf1f965f3bc121e007ab6fd526ed412a85d"}, - {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0dd9d9c738cb008bfc0862c9b8fa6743495c03a0ed543884bf92fb7d30f8d043"}, - {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:42b8b8dd0799f78e067e2aaae97e60d58a8f63582939af60abce4c48631a0aa4"}, - {file = "simplejson-3.17.2-cp27-cp27m-win32.whl", hash = "sha256:8042040af86a494a23c189b5aa0ea9433769cc029707833f261a79c98e3375f9"}, - {file = "simplejson-3.17.2-cp27-cp27m-win_amd64.whl", hash = "sha256:034550078a11664d77bc1a8364c90bb7eef0e44c2dbb1fd0a4d92e3997088667"}, - {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:fed0f22bf1313ff79c7fc318f7199d6c2f96d4de3234b2f12a1eab350e597c06"}, - {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:2e7b57c2c146f8e4dadf84977a83f7ee50da17c8861fd7faf694d55e3274784f"}, - {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:da3c55cdc66cfc3fffb607db49a42448785ea2732f055ac1549b69dcb392663b"}, - {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c1cb29b1fced01f97e6d5631c3edc2dadb424d1f4421dad079cb13fc97acb42f"}, - {file = "simplejson-3.17.2-cp33-cp33m-win32.whl", hash = "sha256:8f713ea65958ef40049b6c45c40c206ab363db9591ff5a49d89b448933fa5746"}, - {file = "simplejson-3.17.2-cp33-cp33m-win_amd64.whl", hash = "sha256:344e2d920a7f27b4023c087ab539877a1e39ce8e3e90b867e0bfa97829824748"}, - {file = "simplejson-3.17.2-cp34-cp34m-win32.whl", hash = "sha256:05b43d568300c1cd43f95ff4bfcff984bc658aa001be91efb3bb21df9d6288d3"}, - {file = "simplejson-3.17.2-cp34-cp34m-win_amd64.whl", hash = "sha256:cff6453e25204d3369c47b97dd34783ca820611bd334779d22192da23784194b"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971"}, - {file = "simplejson-3.17.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a"}, - {file = "simplejson-3.17.2-cp35-cp35m-win32.whl", hash = "sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396"}, - {file = "simplejson-3.17.2-cp35-cp35m-win_amd64.whl", hash = "sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a"}, - {file = "simplejson-3.17.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:ffd4e4877a78c84d693e491b223385e0271278f5f4e1476a4962dca6824ecfeb"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:10fc250c3edea4abc15d930d77274ddb8df4803453dde7ad50c2f5565a18a4bb"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:76ac9605bf2f6d9b56abf6f9da9047a8782574ad3531c82eae774947ae99cc3f"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:7f10f8ba9c1b1430addc7dd385fc322e221559d3ae49b812aebf57470ce8de45"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bc00d1210567a4cdd215ac6e17dc00cb9893ee521cee701adfd0fa43f7c73139"}, - {file = "simplejson-3.17.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:af4868da7dd53296cd7630687161d53a7ebe2e63814234631445697bd7c29f46"}, - {file = "simplejson-3.17.2-cp36-cp36m-win32.whl", hash = "sha256:7d276f69bfc8c7ba6c717ba8deaf28f9d3c8450ff0aa8713f5a3280e232be16b"}, - {file = "simplejson-3.17.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a55c76254d7cf8d4494bc508e7abb993a82a192d0db4552421e5139235604625"}, - {file = "simplejson-3.17.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:9a2b7543559f8a1c9ed72724b549d8cc3515da7daf3e79813a15bdc4a769de25"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:311f5dc2af07361725033b13cc3d0351de3da8bede3397d45650784c3f21fbcf"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2862beabfb9097a745a961426fe7daf66e1714151da8bb9a0c430dde3d59c7c0"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:afebfc3dd3520d37056f641969ce320b071bc7a0800639c71877b90d053e087f"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d4813b30cb62d3b63ccc60dd12f2121780c7a3068db692daeb90f989877aaf04"}, - {file = "simplejson-3.17.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fabde09af43e0cbdee407555383063f8b45bfb52c361bc5da83fcffdb4fd278"}, - {file = "simplejson-3.17.2-cp37-cp37m-win32.whl", hash = "sha256:ceaa28a5bce8a46a130cd223e895080e258a88d51bf6e8de2fc54a6ef7e38c34"}, - {file = "simplejson-3.17.2-cp37-cp37m-win_amd64.whl", hash = "sha256:9551f23e09300a9a528f7af20e35c9f79686d46d646152a0c8fc41d2d074d9b0"}, - {file = "simplejson-3.17.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c94dc64b1a389a416fc4218cd4799aa3756f25940cae33530a4f7f2f54f166da"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b59aa298137ca74a744c1e6e22cfc0bf9dca3a2f41f51bc92eb05695155d905a"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ad8f41c2357b73bc9e8606d2fa226233bf4d55d85a8982ecdfd55823a6959995"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:845a14f6deb124a3bcb98a62def067a67462a000e0508f256f9c18eff5847efc"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d0b64409df09edb4c365d95004775c988259efe9be39697d7315c42b7a5e7e94"}, - {file = "simplejson-3.17.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:55d65f9cc1b733d85ef95ab11f559cce55c7649a2160da2ac7a078534da676c8"}, - {file = "simplejson-3.17.2.tar.gz", hash = "sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841"}, -] - -[[package]] -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 = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "sniffio" -version = "1.3.0" -description = "Sniff out which async library your code is running under" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, -] - -[[package]] -name = "sqlalchemy" -version = "1.4.2" -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.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:aed22be55a608787bb6875dbcf3561349a0e88fe33fd88c318c1e5b4eeb2306a"}, - {file = "SQLAlchemy-1.4.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7e1b0ed6d720750f02333d2f52502dfc2a23185aacc2cc6ce6ec29d28c21397c"}, - {file = "SQLAlchemy-1.4.2-cp27-cp27m-win32.whl", hash = "sha256:9406b96a979ab8d6de5d89f58b1f103c9aeef6fb5367448537a8228619f11258"}, - {file = "SQLAlchemy-1.4.2-cp27-cp27m-win_amd64.whl", hash = "sha256:59ec279f1bd55e1d703e3d4b651600cc463cc3eafa8d8e5a70ab844f736348d4"}, - {file = "SQLAlchemy-1.4.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8cfcfcf2582b19c874fa20d0b75100abe17be80a4c637c0683b4eb919946dfee"}, - {file = "SQLAlchemy-1.4.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a6b4b7688fe7d251bbae3f9da4a487568bd584d13201bc7591c8639ad01fecdc"}, - {file = "SQLAlchemy-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0abab6d1044198993256f073340b14c459736777c550a7e914cd00444dcf9c30"}, - {file = "SQLAlchemy-1.4.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:5fb8f6a391992dd6aafe4fdf1dffbf7934fba1f5938593f20b152aa7f9619f82"}, - {file = "SQLAlchemy-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:97e333260a99d989f2a131aa8aa74140636dfbd030987150cb3748da607ea7db"}, - {file = "SQLAlchemy-1.4.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3fa75c854dba3f9b9c28bc5d88d246f6bc6f20b7480367c65339bcb2864d4707"}, - {file = "SQLAlchemy-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:1b9f3c7b281aa1c3d0c74ef12c4633e5f8358bb94f01be7b964887183fd53e5e"}, - {file = "SQLAlchemy-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:da72e3499bde4548e8b7d7f2ab23ceed09a5bac307bf51057e066c406a0ba2e1"}, - {file = "SQLAlchemy-1.4.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:8383292298bb85d7ad79a13c6571aff213b96c49737f3c3af129de63bbfb42c9"}, - {file = "SQLAlchemy-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4d1447183356c9679853926e81c7ebce3fbca9b1c607ea439975298c72137a36"}, - {file = "SQLAlchemy-1.4.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ff76d7dbf33f62e30e5a1d1b095d46afcdc49e42cbe33ce12014110147466700"}, - {file = "SQLAlchemy-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:1ba6922331b3f38e116c9266206b044baf64576e5cebd87917b5ad872d7a025f"}, - {file = "SQLAlchemy-1.4.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:d3b2819f4d7ae56191efc6fc456eb1805ada2bd5ba93d918893bc24fa7a1e30c"}, - {file = "SQLAlchemy-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:3b290ff34de625143a05d2d172a88a064bb04a7938265b09d4e4bf45f21948f6"}, - {file = "SQLAlchemy-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:5289cafee71037f15feeeaf736f01910b9e3572525b73b201bdd21816db010ed"}, - {file = "SQLAlchemy-1.4.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0bb04fd7414718fb1f4dfa17efcb0be787363451cf99a5e992728925d298d9ae"}, - {file = "SQLAlchemy-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4e88549a5e58ba8c80c5ea071ac3b4e590236672a882bb80f56da4afcee45d96"}, - {file = "SQLAlchemy-1.4.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:edec945ed57d11a1123657e4066f0bf747aaa93c8a65ec1c2c98172d1f2a9b7d"}, - {file = "SQLAlchemy-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:06125670280111e39014af87f14d74599fd4b39a512c74f1a10e21e5626eb158"}, - {file = "SQLAlchemy-1.4.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:e1692bdf1b95c97caab1201773a4576f59627997f598d30bdadc50dd9f897fec"}, - {file = "SQLAlchemy-1.4.2-cp38-cp38-win32.whl", hash = "sha256:65c4df9517da9cce2c1255282d3e39f2afbc3a02deba60d99b0a3283ae80ec0b"}, - {file = "SQLAlchemy-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c6197c88ad53c31f58de5a8180936b8ef027356e788cd5f6514b3439d3d897ac"}, - {file = "SQLAlchemy-1.4.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:6d6115edf1297bfa58994986ffe0dff21af18f0cba51dfa6d1769aa8a277be32"}, - {file = "SQLAlchemy-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:facacaea95e0822f7bbeaa6909b30b2836b14cff8790209d52a0c866e240b673"}, - {file = "SQLAlchemy-1.4.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6e517126d3bc13d455826befdc35a89f82f01d163848f68db02caa80d25433fc"}, - {file = "SQLAlchemy-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:09b08eb1bea621e47c2b0fcb0334fcbb00e1da2a3c2d45a98e56cd072b840719"}, - {file = "SQLAlchemy-1.4.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:7eba42098a13a3bcd509080b5e44d73783d9129ba0383793979bf518d01e8bb3"}, - {file = "SQLAlchemy-1.4.2-cp39-cp39-win32.whl", hash = "sha256:920db115eb06fc507fe2c774fb5c82a898b05dffbdadc7fafad51ce2cfd8c549"}, - {file = "SQLAlchemy-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:dcde5067a7dab1ff2eaea2f3622b2055c5225ce2aaf589c5a4c703d43519c4ba"}, - {file = "SQLAlchemy-1.4.2.tar.gz", hash = "sha256:6a8e4c2e65028933a6dc8643c8f5a4f295a367131195b3c708634925cb3e8ec1"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\""} - -[package.extras] -aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] -asyncio = ["greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.800)", "sqlalchemy2-stubs"] -mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] -mysql-connector = ["mysqlconnector"] -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)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql", "pymysql (<1)"] - -[[package]] -name = "starlette" -version = "0.14.2" -description = "The little ASGI library that shines." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "starlette-0.14.2-py3-none-any.whl", hash = "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed"}, - {file = "starlette-0.14.2.tar.gz", hash = "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"}, -] - -[package.extras] -full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] - -[[package]] -name = "strawberry-graphql" -version = "0.90.3" -description = "A library for creating GraphQL APIs" -category = "main" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "strawberry-graphql-0.90.3.tar.gz", hash = "sha256:1a6eb2e02fed40a5e5933184bce3fcf314bc108e65b414c0925814117f1df912"}, - {file = "strawberry_graphql-0.90.3-py3-none-any.whl", hash = "sha256:080f3abc3f98c58b9cfc29d43714acdc6b2e35da5a0902153471224b400205dc"}, -] - -[package.dependencies] -cached-property = ">=1.5.2,<2.0.0" -click = ">=7.0,<9.0" -graphql-core = ">=3.1.0,<3.2.0" -pygments = ">=2.3,<3.0" -python-dateutil = ">=2.7.0,<3.0.0" -python-multipart = ">=0.0.5,<0.0.6" -sentinel = ">=0.3.0,<0.4.0" -typing_extensions = ">=3.7.4,<5.0.0" - -[package.extras] -aiohttp = ["aiohttp (>=3.7.4.post0,<4.0.0)"] -asgi = ["starlette (>=0.13.6,<0.17.0)"] -chalice = ["chalice (>=1.22,<2.0)"] -debug-server = ["starlette (>=0.13.6,<0.17.0)", "uvicorn (>=0.11.6,<0.16.0)"] -django = ["asgiref (>=3.2,<4.0)", "django (>=2,<4)"] -fastapi = ["fastapi (>=0.65.2)"] -flask = ["flask (>=1.1,<2.0)"] -opentelemetry = ["opentelemetry-api (<2)", "opentelemetry-sdk (<2)"] -pydantic = ["pydantic (<2)"] -sanic = ["sanic (>=20.12.2,<22.0.0)"] - -[[package]] -name = "taskipy" -version = "1.10.1" -description = "tasks runner for python projects" -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "taskipy-1.10.1-py3-none-any.whl", hash = "sha256:9b38333654da487b6d16de6fa330b7629d1935d1e74819ba4c5f17a1c372d37b"}, - {file = "taskipy-1.10.1.tar.gz", hash = "sha256:6fa0b11c43d103e376063e90be31d87b435aad50fb7dc1c9a2de9b60a85015ed"}, -] - -[package.dependencies] -colorama = ">=0.4.4,<0.5.0" -mslex = {version = ">=0.3.0,<0.4.0", markers = "sys_platform == \"win32\""} -psutil = ">=5.7.2,<6.0.0" -tomli = ">=1.2.3,<2.0.0" - -[[package]] -name = "time-machine" -version = "2.9.0" -description = "Travel through time in your tests." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "time-machine-2.9.0.tar.gz", hash = "sha256:60222d43f6e93a926adc36ed37a54bc8e4d0d8d1c4d449096afcfe85086129c2"}, - {file = "time_machine-2.9.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fd72c0b2e7443fff6e4481991742b72c17f73735e5fdd176406ca48df187a5c9"}, - {file = "time_machine-2.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5657e0e6077cf15b37f0d8cf78e868113bbb3ecccc60064c40fe52d8166ca8b1"}, - {file = "time_machine-2.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bfa82614a98ecee70272bb6038d210b2ad7b2a6b8a678b400c34bdaf776802a7"}, - {file = "time_machine-2.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4380bd6697cc7db3c9e6843f24779ac0550affa9d9a8e5f9e5d5cc139cb6583"}, - {file = "time_machine-2.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6211beee9f5dace08b1bbbb1fb09e34a69c52d87eea676729f14c8660481dff6"}, - {file = "time_machine-2.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:68ec8b83197db32c7a12da5f6b83c91271af3ed7f5dc122d2900a8de01dff9f0"}, - {file = "time_machine-2.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c5dbc8b87cdc7be070a499f2bd1cd405c7f647abeb3447dfd397639df040bc64"}, - {file = "time_machine-2.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:948ca690f9770ad4a93fa183061c11346505598f5f0b721965bc85ec83bb103d"}, - {file = "time_machine-2.9.0-cp310-cp310-win32.whl", hash = "sha256:f92d5d2eb119a6518755c4c9170112094c706d1c604460f50afc1308eeb97f0e"}, - {file = "time_machine-2.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb51432652ad663b4cbd631c73c90f9e94f463382b86c0b6b854173700512a70"}, - {file = "time_machine-2.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:8976b7b1f7de13598b655d459f5640f90f3cd587283e1b914a22e45946c5485b"}, - {file = "time_machine-2.9.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6463e302c96eb8c691c4340e281bd54327a213b924fa189aea81accf7e7f78df"}, - {file = "time_machine-2.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b632d60aa0883dc7292ac3d32050604d26ec2bbd5c4d42fb0de3b4ef17343e2"}, - {file = "time_machine-2.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d329578abe47ce95baa015ef3825acebb1b73b5fa6f818fdf2d4685a00ca457f"}, - {file = "time_machine-2.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ba5fc2655749066d68986de8368984dad4082db2fbeade78f40506dc5b65672"}, - {file = "time_machine-2.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49df5eea2160068e5b2bf28c22fc4c5aea00862ad88ddc3b62fc0f0683e97538"}, - {file = "time_machine-2.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8830510adbf0a231184da277db9de1d55ef93ed228a575d217aaee295505abf1"}, - {file = "time_machine-2.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b16a2129f9146faa080bfd1b53447761f7386ec5c72890c827a65f33ab200336"}, - {file = "time_machine-2.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2cf80e5deaaa68c6cefb25303a4c870490b4e7591ed8e2435a65728920bc097"}, - {file = "time_machine-2.9.0-cp311-cp311-win32.whl", hash = "sha256:fe013942ab7f3241fcbe66ee43222d47f499d1e0cb69e913791c52e638ddd7f0"}, - {file = "time_machine-2.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:1d0ab46ce8a60baf9d86525694bf698fed9efefd22b8cbe1ca3e74abbb3239e1"}, - {file = "time_machine-2.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:4f3755d9342ca1f1019418db52072272dfd75eb818fa4726fa8aabe208b38c26"}, - {file = "time_machine-2.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9ee553f7732fa51e019e3329a6984593184c4e0410af1e73d91ce38a5d4b34ab"}, - {file = "time_machine-2.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:359c806e5b9a7a3c73dbb808d19dca297f5504a5eefdc5d031db8d918f43e364"}, - {file = "time_machine-2.9.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e2a90b8300812d8d774f2d2fc216fec3c7d94132ac589e062489c395061f16c"}, - {file = "time_machine-2.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36dde844d28549929fab171d683c28a8db1c206547bcf6b7aca77319847d2046"}, - {file = "time_machine-2.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:728263611d7940fda34d21573bd2b3f1491bdb52dbf75c5fe6c226dfe4655201"}, - {file = "time_machine-2.9.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8bcc86b5a07ea9745f26dfad958dde0a4f56748c2ae0c9a96200a334d1b55055"}, - {file = "time_machine-2.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b9c36240876622b7f2f9e11bf72f100857c0a1e1a59af2da3d5067efea62c37"}, - {file = "time_machine-2.9.0-cp37-cp37m-win32.whl", hash = "sha256:eaf334477bc0a9283d5150a56be8670a07295ef676e5b5a7f086952929d1a56b"}, - {file = "time_machine-2.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8e797e5a2a99d1b237183e52251abfc1ad85c376278b39d1aca76a451a97861a"}, - {file = "time_machine-2.9.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:69898aed9b2315a90f5855343d9aa34d05fa06032e2e3bb14f2528941ec89dc1"}, - {file = "time_machine-2.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c01dbc3671d0649023daf623e952f9f0b4d904d57ab546d6d35a4aeb14915e8d"}, - {file = "time_machine-2.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f080f6f7ca8cfca43bc5639288aebd0a273b4b5bd0acff609c2318728b13a18"}, - {file = "time_machine-2.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8670cb5cfda99f483d60de6ce56ceb0ec5d359193e79e4688e1c3c9db3937383"}, - {file = "time_machine-2.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f97ed8bc5b517844a71030f74e9561de92f4902c306e6ccc8331a5b0c8dd0e00"}, - {file = "time_machine-2.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bdbe785e046d124f73cca603ee37d5fae0b15dc4c13702488ad19de56aae08ba"}, - {file = "time_machine-2.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fcdef7687aed5c4331c9808f4a414a41987441c3e7a2ba554e4dccfa4218e788"}, - {file = "time_machine-2.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f6e79643368828d4651146a486be5a662846ac223ab5e2c73ddd519acfcc243c"}, - {file = "time_machine-2.9.0-cp38-cp38-win32.whl", hash = "sha256:bb15b2b79b00d3f6cf7d62096f5e782fa740ecedfe0540c09f1d1e4d3d7b81ba"}, - {file = "time_machine-2.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:3ff5148e2e73392db8418a1fe2f0b06f4a0e76772933502fb61e4c3000b5324e"}, - {file = "time_machine-2.9.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8367fd03f2d7349c7fc20f14de186974eaca2502c64b948212de663742c8fd11"}, - {file = "time_machine-2.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4b55654aaeaba380fcd6c004b8ada2978fdd4ece1e61e6b9717c6d4cc7fbbcd9"}, - {file = "time_machine-2.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae4e3f02ab5dabb35adca606237c7e1a515c86d69c0b7092bbe0e1cfe5cffc61"}, - {file = "time_machine-2.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:010a58a8de1120308befae19e6c9de2ef5ca5206635cea33cb264998725cc027"}, - {file = "time_machine-2.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b32addbf56639a9a8261fb62f8ea83473447671c83ca2c017ab1eabf4841157f"}, - {file = "time_machine-2.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:372a97da01db89533d2f4ce50bbd908e5c56df7b8cfd6a005b177d0b14dc2938"}, - {file = "time_machine-2.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b8faff03231ee55d5a216ce3e9171c5205459f866f54d4b5ee8aa1d860e4ce11"}, - {file = "time_machine-2.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:748d701228e646c224f2adfa6a11b986cd4aa90f1b8c13ef4534a3919c796bc0"}, - {file = "time_machine-2.9.0-cp39-cp39-win32.whl", hash = "sha256:d79d374e32488c76cdb06fbdd4464083aeaa715ddca3e864bac7c7760eb03729"}, - {file = "time_machine-2.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:cc6bf01211b5ea40f633d5502c5aa495b415ebaff66e041820997dae70a508e1"}, - {file = "time_machine-2.9.0-cp39-cp39-win_arm64.whl", hash = "sha256:3ce445775fcf7cb4040cfdba4b7c4888e7fd98bbcccfe1dc3fa8a798ed1f1d24"}, -] - -[package.dependencies] -python-dateutil = "*" - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] - -[[package]] -name = "tomli" -version = "1.2.3" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, - {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, -] - -[[package]] -name = "typing-extensions" -version = "4.5.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, -] - -[[package]] -name = "urllib3" -version = "1.26.15" -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.*" -files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, -] - -[package.extras] -brotli = ["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)"] - -[[package]] -name = "uvicorn" -version = "0.21.1" -description = "The lightning-fast ASGI server." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "uvicorn-0.21.1-py3-none-any.whl", hash = "sha256:e47cac98a6da10cd41e6fd036d472c6f58ede6c5dbee3dbee3ef7a100ed97742"}, - {file = "uvicorn-0.21.1.tar.gz", hash = "sha256:0fac9cb342ba099e0d582966005f3fdba5b0290579fed4a6266dc702ca7bb032"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} -h11 = ">=0.8" -httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} -python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "uvloop" -version = "0.17.0" -description = "Fast implementation of asyncio event loop on top of libuv" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718"}, - {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:68532f4349fd3900b839f588972b3392ee56042e440dd5873dfbbcd2cc67617c"}, - {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0949caf774b9fcefc7c5756bacbbbd3fc4c05a6b7eebc7c7ad6f825b23998d6d"}, - {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff3d00b70ce95adce264462c930fbaecb29718ba6563db354608f37e49e09024"}, - {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a5abddb3558d3f0a78949c750644a67be31e47936042d4f6c888dd6f3c95f4aa"}, - {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8efcadc5a0003d3a6e887ccc1fb44dec25594f117a94e3127954c05cf144d811"}, - {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3378eb62c63bf336ae2070599e49089005771cc651c8769aaad72d1bd9385a7c"}, - {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6aafa5a78b9e62493539456f8b646f85abc7093dd997f4976bb105537cf2635e"}, - {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c686a47d57ca910a2572fddfe9912819880b8765e2f01dc0dd12a9bf8573e539"}, - {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:864e1197139d651a76c81757db5eb199db8866e13acb0dfe96e6fc5d1cf45fc4"}, - {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2a6149e1defac0faf505406259561bc14b034cdf1d4711a3ddcdfbaa8d825a05"}, - {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6708f30db9117f115eadc4f125c2a10c1a50d711461699a0cbfaa45b9a78e376"}, - {file = "uvloop-0.17.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:23609ca361a7fc587031429fa25ad2ed7242941adec948f9d10c045bfecab06b"}, - {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2deae0b0fb00a6af41fe60a675cec079615b01d68beb4cc7b722424406b126a8"}, - {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45cea33b208971e87a31c17622e4b440cac231766ec11e5d22c76fab3bf9df62"}, - {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9b09e0f0ac29eee0451d71798878eae5a4e6a91aa275e114037b27f7db72702d"}, - {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dbbaf9da2ee98ee2531e0c780455f2841e4675ff580ecf93fe5c48fe733b5667"}, - {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a4aee22ece20958888eedbad20e4dbb03c37533e010fb824161b4f05e641f738"}, - {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:307958f9fc5c8bb01fad752d1345168c0abc5d62c1b72a4a8c6c06f042b45b20"}, - {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ebeeec6a6641d0adb2ea71dcfb76017602ee2bfd8213e3fcc18d8f699c5104f"}, - {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1436c8673c1563422213ac6907789ecb2b070f5939b9cbff9ef7113f2b531595"}, - {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8887d675a64cfc59f4ecd34382e5b4f0ef4ae1da37ed665adba0c2badf0d6578"}, - {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3db8de10ed684995a7f34a001f15b374c230f7655ae840964d51496e2f8a8474"}, - {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d37dccc7ae63e61f7b96ee2e19c40f153ba6ce730d8ba4d3b4e9738c1dccc1b"}, - {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cbbe908fda687e39afd6ea2a2f14c2c3e43f2ca88e3a11964b297822358d0e6c"}, - {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d97672dc709fa4447ab83276f344a165075fd9f366a97b712bdd3fee05efae8"}, - {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e507c9ee39c61bfddd79714e4f85900656db1aec4d40c6de55648e85c2799c"}, - {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c092a2c1e736086d59ac8e41f9c98f26bbf9b9222a76f21af9dfe949b99b2eb9"}, - {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30babd84706115626ea78ea5dbc7dd8d0d01a2e9f9b306d24ca4ed5796c66ded"}, - {file = "uvloop-0.17.0.tar.gz", hash = "sha256:0ddf6baf9cf11a1a22c71487f39f15b2cf78eb5bde7e5b45fbb99e8a9d91b9e1"}, -] - -[package.extras] -dev = ["Cython (>=0.29.32,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"] - -[[package]] -name = "ward" -version = "0.65.0b0" -description = "A modern Python testing framework" -category = "dev" -optional = false -python-versions = ">=3.6.1,<4.0.0" -files = [ - {file = "ward-0.65.0b0-py3-none-any.whl", hash = "sha256:497e3602768e21c3db85bcf0fe241ee03494f0f46a75b7b2e0e074bfaddc4f5d"}, - {file = "ward-0.65.0b0.tar.gz", hash = "sha256:4f086c746b0d3d4464cf8ddea54f11fa08e02a784d0fda5bdc9b7f9c9fa63db0"}, -] - -[package.dependencies] -click = ">=7,<9" -click-completion = ">=0.5.2,<0.6.0" -click-default-group = ">=1.2.2,<2.0.0" -cucumber-tag-expressions = ">=2.0.0,<5.0.0" -pluggy = ">=0.13.1,<2.0.0" -pprintpp = ">=0.4.0,<0.5.0" -rich = ">=10.0.0,<11.0.0" -tomli = ">=1.0.0,<2.0.0" - -[[package]] -name = "watchfiles" -version = "0.18.1" -description = "Simple, modern and high performance file watching and code reload in python." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "watchfiles-0.18.1-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:9891d3c94272108bcecf5597a592e61105279def1313521e637f2d5acbe08bc9"}, - {file = "watchfiles-0.18.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:7102342d60207fa635e24c02a51c6628bf0472e5fef067f78a612386840407fc"}, - {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:00ea0081eca5e8e695cffbc3a726bb90da77f4e3f78ce29b86f0d95db4e70ef7"}, - {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8e6db99e49cd7125d8a4c9d33c0735eea7b75a942c6ad68b75be3e91c242fb"}, - {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc7c726855f04f22ac79131b51bf0c9f728cb2117419ed830a43828b2c4a5fcb"}, - {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbaff354d12235002e62d9d3fa8bcf326a8490c1179aa5c17195a300a9e5952f"}, - {file = "watchfiles-0.18.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:888db233e06907c555eccd10da99b9cd5ed45deca47e41766954292dc9f7b198"}, - {file = "watchfiles-0.18.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:dde79930d1b28f15994ad6613aa2865fc7a403d2bb14585a8714a53233b15717"}, - {file = "watchfiles-0.18.1-cp37-abi3-win32.whl", hash = "sha256:e2b2bdd26bf8d6ed90763e6020b475f7634f919dbd1730ea1b6f8cb88e21de5d"}, - {file = "watchfiles-0.18.1-cp37-abi3-win_amd64.whl", hash = "sha256:c541e0f2c3e95e83e4f84561c893284ba984e9d0025352057396d96dceb09f44"}, - {file = "watchfiles-0.18.1-cp37-abi3-win_arm64.whl", hash = "sha256:9a26272ef3e930330fc0c2c148cc29706cc2c40d25760c7ccea8d768a8feef8b"}, - {file = "watchfiles-0.18.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:9fb12a5e2b42e0b53769455ff93546e6bc9ab14007fbd436978d827a95ca5bd1"}, - {file = "watchfiles-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:548d6b42303d40264118178053c78820533b683b20dfbb254a8706ca48467357"}, - {file = "watchfiles-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0d8fdfebc50ac7569358f5c75f2b98bb473befccf9498cf23b3e39993bb45a"}, - {file = "watchfiles-0.18.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0f9a22fff1745e2bb930b1e971c4c5b67ea3b38ae17a6adb9019371f80961219"}, - {file = "watchfiles-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b02e7fa03cd4059dd61ff0600080a5a9e7a893a85cb8e5178943533656eec65e"}, - {file = "watchfiles-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a868ce2c7565137f852bd4c863a164dc81306cae7378dbdbe4e2aca51ddb8857"}, - {file = "watchfiles-0.18.1.tar.gz", hash = "sha256:4ec0134a5e31797eb3c6c624dbe9354f2a8ee9c720e0b46fc5b7bab472b7c6d4"}, -] - -[package.dependencies] -anyio = ">=3.0.0" - -[[package]] -name = "websockets" -version = "10.4" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, - {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, - {file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"}, - {file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"}, - {file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"}, - {file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"}, - {file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"}, - {file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"}, - {file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"}, - {file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"}, - {file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"}, - {file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"}, - {file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"}, - {file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"}, - {file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"}, - {file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"}, - {file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"}, - {file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"}, - {file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"}, - {file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"}, - {file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"}, - {file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"}, - {file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"}, - {file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"}, - {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"}, - {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"}, - {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"}, - {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"}, - {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"}, - {file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"}, - {file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"}, - {file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"}, - {file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"}, - {file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"}, - {file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"}, - {file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"}, - {file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"}, - {file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"}, - {file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"}, - {file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"}, - {file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"}, - {file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"}, - {file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"}, - {file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"}, - {file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"}, - {file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"}, - {file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"}, - {file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"}, - {file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"}, - {file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"}, - {file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"}, - {file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"}, - {file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"}, - {file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"}, - {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"}, - {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"}, - {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"}, - {file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"}, - {file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"}, - {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"}, - {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"}, - {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"}, - {file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"}, - {file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"}, - {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"}, - {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"}, - {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"}, - {file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"}, - {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, -] - -[[package]] -name = "wmctrl" -version = "0.4" -description = "A tool to programmatically control windows inside X" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "wmctrl-0.4.tar.gz", hash = "sha256:66cbff72b0ca06a22ec3883ac3a4d7c41078bdae4fb7310f52951769b10e14e0"}, -] - -[extras] -lambda = ["awslambdaric"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.9" -content-hash = "8d5ef085a81dd3b74fef045b1256960ab3b0d9c827eaa236e140d1c025a53558" diff --git a/users-backend/pyproject.toml b/users-backend/pyproject.toml deleted file mode 100644 index e25e6f001b..0000000000 --- a/users-backend/pyproject.toml +++ /dev/null @@ -1,71 +0,0 @@ -[tool.poetry] -name = "users" -version = "0.1.0" -description = "" -authors = ["Python Italia"] - -[tool.poetry.dependencies] -python = "^3.9" -starlette = "^0.14.1" -uvicorn = {extras = ["standard"], version = "^0.21.1"} -strawberry-graphql = "^0.90" -SQLAlchemy = "1.4.2" -alembic = "^1.5.2" -asyncpg = "^0.21.0" -PyJWT = "2.0.1" -pydantic = "^1.7.3" -email-validator = "^1.1.2" -argon2-cffi = "^20.1.0" -httpx = "0.20.0" -Authlib = "^0.15.3" -itsdangerous = "^1.1.0" -mangum = "^0.10.0" -pythonit-toolkit = "0.1.81" -graphql-core = "^3.1.5" -sentry-sdk = "^1.1.0" -toml = "^0.10.2" -awslambdaric = {version = "^2.0.4", optional = true} -psycopg2 = "^2.9.5" -gunicorn = "^20.1.0" - -[tool.poetry.dev-dependencies] -pdbpp = "^0.10.2" -ward = "^0.65.0b0" -requests = "^2.25.1" -taskipy = "^1.6.0" -factory-boy = "^3.2.0" -asgi-lifespan = "^1.0.1" -coverage = "^5.4" -time-machine = "^2.0.1" - -[tool.poetry.extras] -lambda = ["awslambdaric"] - -[tool.taskipy.tasks] -server = "uvicorn main:wrapped_app --port 8050 --reload" -test = "RUNNING_TESTS=1 python scripts/prepare_db.py && RUNNING_TESTS=1 coverage run -m ward" -"test:coverage" = "task test || coverage xml -i && coverage xml -i" -migrate = "alembic upgrade head" -makemigrations = "alembic revision --autogenerate -m" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" - -[tool.coverage.report] -exclude_lines = [ - "pragma: no cover", - "def __repr__", - "raise AssertionError", - "raise NotImplementedError", - "if 0:", - "if __name__ == .__main__.:", - "if TYPE_CHECKING:", - "if typing.TYPE_CHECKING:", -] - -[tool.coverage.run] -branch = true -include = [ - "users/*", -] diff --git a/users-backend/scripts/__init__.py b/users-backend/scripts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/scripts/db_utils.py b/users-backend/scripts/db_utils.py deleted file mode 100644 index 881d15eb26..0000000000 --- a/users-backend/scripts/db_utils.py +++ /dev/null @@ -1,77 +0,0 @@ -import sqlalchemy -from sqlalchemy.engine.interfaces import Dialect -from sqlalchemy.orm.exc import UnmappedInstanceError -from sqlalchemy.orm.session import object_session - - -def create_database(url, db_name: str) -> None: - with sqlalchemy.create_engine( - url, isolation_level="AUTOCOMMIT" - ).connect() as connection: - query = "CREATE DATABASE {0} TEMPLATE {1}".format( - quote(connection, db_name), "template1" - ) - connection.execute(query) - - -def drop_database(url, db_name: str) -> None: - with sqlalchemy.create_engine( - url, isolation_level="AUTOCOMMIT" - ).connect() as connection: - query = "DROP DATABASE {0}".format(quote(connection, db_name)) - connection.execute(query) - - -def database_exists(url, db_name: str) -> bool: - with sqlalchemy.create_engine( - url, isolation_level="AUTOCOMMIT" - ).connect() as connection: - query = "SELECT 1 FROM pg_database WHERE datname='{0}'".format(db_name) - result = connection.execute(query) - return bool(result.scalar()) - - -def quote(mixed, ident): - """ - Conditionally quote an identifier. - :: - from sqlalchemy_utils import quote - engine = create_engine('sqlite:///:memory:') - quote(engine, 'order') - # '"order"' - quote(engine, 'some_other_identifier') - # 'some_other_identifier' - :param mixed: SQLAlchemy Session / Connection / Engine / Dialect object. - :param ident: identifier to conditionally quote - """ - if isinstance(mixed, Dialect): - dialect = mixed - else: - dialect = get_bind(mixed).dialect - return dialect.preparer(dialect).quote(ident) - - -def get_bind(obj): - """ - Return the bind for given SQLAlchemy Engine / Connection / declarative - model object. - :param obj: SQLAlchemy Engine / Connection / declarative model object - :: - from sqlalchemy_utils import get_bind - get_bind(session) # Connection object - get_bind(user) - """ - if hasattr(obj, "bind"): - conn = obj.bind - else: - try: - conn = object_session(obj).bind - except UnmappedInstanceError: - conn = obj - - if not hasattr(conn, "execute"): - raise TypeError( - "This method accepts only Session, Engine, Connection and " - "declarative model objects." - ) - return conn diff --git a/users-backend/scripts/prepare_db.py b/users-backend/scripts/prepare_db.py deleted file mode 100644 index caa65bd457..0000000000 --- a/users-backend/scripts/prepare_db.py +++ /dev/null @@ -1,36 +0,0 @@ -# isort: off -# flake8: noqa -import asyncio -import os -import sys - -from sqlalchemy.engine.url import make_url # noqa - -sys.path.append(os.path.join(os.path.dirname(__file__), "..")) # noqa - -from db_utils import create_database, database_exists - -from users.db import get_engine -from users.domain.entities import mapper_registry -from users.settings import DATABASE_URL - - -async def run(): - engine = get_engine(echo=False) - metadata = mapper_registry.metadata - db_name = make_url(DATABASE_URL).database - sync_database_url = DATABASE_URL.replace("asyncpg", "psycopg2").replace("TEST_", "") - - if not database_exists(sync_database_url, db_name): - create_database(sync_database_url, db_name) - - async with engine.begin() as connection: - await connection.run_sync(metadata.create_all) - else: - async with engine.begin() as connection: - await connection.run_sync(metadata.drop_all) - await connection.run_sync(metadata.create_all) - - -if __name__ == "__main__": - asyncio.run(run()) diff --git a/users-backend/users/__init__.py b/users-backend/users/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/api/__init__.py b/users-backend/users/api/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/api/context.py b/users-backend/users/api/context.py deleted file mode 100644 index dc89195700..0000000000 --- a/users-backend/users/api/context.py +++ /dev/null @@ -1,34 +0,0 @@ -from dataclasses import dataclass -from typing import TYPE_CHECKING, Optional, Union - -from sqlalchemy.ext.asyncio import AsyncSession -from starlette.requests import Request -from starlette.responses import Response -from starlette.websockets import WebSocket -from strawberry.dataloader import DataLoader - -from users.api.dataloader import users_dataloader -from users.domain.repository import UsersRepository - -if TYPE_CHECKING: - from users.domain.entities import User - - -@dataclass -class Context: - request: Union[Request, WebSocket] - session: AsyncSession - response: Response - _authenticated_as: Optional["User"] = None - - @property - def users_repository(self) -> UsersRepository: - return UsersRepository(session=self.session) - - @property - def users_dataloader(self) -> DataLoader: - return users_dataloader - - -class Info: - context: Context diff --git a/users-backend/users/api/dataloader.py b/users-backend/users/api/dataloader.py deleted file mode 100644 index 99409529e9..0000000000 --- a/users-backend/users/api/dataloader.py +++ /dev/null @@ -1,19 +0,0 @@ -from strawberry.dataloader import DataLoader - -from users.db import get_engine, get_session -from users.domain.entities import User -from users.domain.repository import UsersRepository - - -async def load_users(ids: list[int]) -> list[User]: - try: - engine = get_engine() - async with get_session(engine) as session: - users = await UsersRepository(session).get_batch_by_ids(ids) - users_by_id = {user.id: user for user in users} - return [users_by_id.get(id) for id in ids] - finally: - await engine.dispose() - - -users_dataloader = DataLoader(load_fn=load_users) diff --git a/users-backend/users/api/schema.py b/users-backend/users/api/schema.py deleted file mode 100644 index 6137cccab7..0000000000 --- a/users-backend/users/api/schema.py +++ /dev/null @@ -1,16 +0,0 @@ -import strawberry -from pythonit_toolkit.api.extensions import SentryExtension - -# from .mutation import Mutation - - -@strawberry.type -class Query: - empty: str = "empty" - - -schema = strawberry.federation.Schema( - Query, - # Mutation, - extensions=[SentryExtension], -) diff --git a/users-backend/users/api/tests/__init__.py b/users-backend/users/api/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/api/tests/mutation/__init__.py b/users-backend/users/api/tests/mutation/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/api/tests/mutation/test_login_mutation.py b/users-backend/users/api/tests/mutation/test_login_mutation.py deleted file mode 100644 index 9163068b8b..0000000000 --- a/users-backend/users/api/tests/mutation/test_login_mutation.py +++ /dev/null @@ -1,187 +0,0 @@ -from ward import test - -from users.tests.api import graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("cannot login to non existent user") -async def _(graphql_client=graphql_client, db=db): - query = """ - mutation($input: LoginInput!) { - login(input: $input) { - __typename - } - } - """ - response = await graphql_client.query( - query, variables={"input": {"email": "hah@pycon.it", "password": "ciao"}} - ) - - assert not response.errors - assert response.data["login"]["__typename"] == "WrongEmailOrPassword" - - -@test("cannot login to not active user") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - await user_factory(email="hah@pycon.it", password="test", is_active=False) - - query = """ - mutation($input: LoginInput!) { - login(input: $input) { - __typename - } - } - """ - response = await graphql_client.query( - query, variables={"input": {"email": "hah@pycon.it", "password": "test"}} - ) - - assert not response.errors - assert response.data["login"]["__typename"] == "WrongEmailOrPassword" - - -@test("cannot login with wrong password") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - await user_factory(email="hah@pycon.it", password="test") - - query = """ - mutation($input: LoginInput!) { - login(input: $input) { - __typename - } - } - """ - response = await graphql_client.query( - query, variables={"input": {"email": "hah@pycon.it", "password": "ciao"}} - ) - - assert not response.errors - assert response.data["login"]["__typename"] == "WrongEmailOrPassword" - - -@test("cannot login with empty password") -async def _(graphql_client=graphql_client, db=db): - query = """ - mutation($input: LoginInput!) { - login(input: $input) { - __typename - ... on LoginValidationError { - errors { - password { - message - type - } - email { - message - type - } - } - } - } - } - """ - response = await graphql_client.query( - query, variables={"input": {"email": "hah@pycon.it", "password": ""}} - ) - - assert not response.errors, response.errors - assert response.data["login"] == { - "__typename": "LoginValidationError", - "errors": { - "password": [ - { - "message": "ensure this value has at least 1 characters", - "type": "value_error.any_str.min_length", - } - ], - "email": None, - }, - } - - -@test("cannot login with empty email") -async def _(graphql_client=graphql_client, db=db): - query = """ - mutation($input: LoginInput!) { - login(input: $input) { - __typename - ... on LoginValidationError { - errors { - password { - message - type - } - email { - message - type - } - } - } - } - } - """ - response = await graphql_client.query( - query, variables={"input": {"email": "", "password": "password"}} - ) - - assert not response.errors, response.errors - assert not response.errors, response.errors - assert response.data["login"] == { - "__typename": "LoginValidationError", - "errors": { - "email": [ - { - "message": "value is not a valid email address", - "type": "value_error.email", - } - ], - "password": None, - }, - } - - -@test("cannot login with wrong password") -async def _(graphql_client=graphql_client, user_factory=user_factory, db=db): - user = await user_factory(email="test@email.it", password="hello") - - query = """ - mutation($input: LoginInput!) { - login(input: $input) { - __typename - } - } - """ - response = await graphql_client.query( - query, variables={"input": {"email": user.email, "password": "nope"}} - ) - - assert not response.errors - assert response.data["login"]["__typename"] == "WrongEmailOrPassword" - - -@test("can login") -async def _(graphql_client=graphql_client, user_factory=user_factory, db=db): - user = await user_factory(email="test@email.it", password="hello") - - query = """ - mutation($input: LoginInput!) { - login(input: $input) { - __typename - - ... on LoginSuccess { - user { - id - email - } - } - } - } - """ - response = await graphql_client.query( - query, variables={"input": {"email": user.email, "password": "hello"}} - ) - - assert not response.errors, response.errors - assert response.data["login"]["__typename"] == "LoginSuccess" - assert response.data["login"]["user"] == {"id": str(user.id), "email": user.email} diff --git a/users-backend/users/api/tests/mutation/test_register_mutation.py b/users-backend/users/api/tests/mutation/test_register_mutation.py deleted file mode 100644 index 12fd436e98..0000000000 --- a/users-backend/users/api/tests/mutation/test_register_mutation.py +++ /dev/null @@ -1,152 +0,0 @@ -from ward import each, test - -from users.tests.api import graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("can register") -async def _(graphql_client=graphql_client, db=db): - query = """mutation($input: RegisterInput!) { - register(input: $input) { - __typename - - ... on RegisterSuccess { - user { - id - email - fullname - } - } - } - } - """ - response = await graphql_client.query( - query, - variables={ - "input": { - "email": "marco@provider.it", - "password": "ciaomondo", - "fullname": "Name", - } - }, - ) - - assert not response.errors, response.errors - - assert response.data["register"]["__typename"] == "RegisterSuccess" - assert response.data["register"]["user"]["id"] is not None - assert response.data["register"]["user"]["email"] == "marco@provider.it" - assert response.data["register"]["user"]["fullname"] == "Name" - - -@test("cannot register if the email is already used by other users") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - await user_factory(email="hah@pycon.it") - - query = """mutation($input: RegisterInput!) { - register(input: $input) { - __typename - ... on EmailAlreadyUsed { - message - } - } - } - """ - response = await graphql_client.query( - query, - variables={ - "input": { - "email": "hah@pycon.it", - "password": "verylongpassword", - "fullname": "Name", - } - }, - ) - - assert not response.errors, response.errors - assert response.data["register"]["__typename"] == "EmailAlreadyUsed" - - -@test("cannot register with invalid password") -async def _(graphql_client=graphql_client, password=each("", "short"), db=db): - query = """mutation($input: RegisterInput!) { - register(input: $input) { - __typename - ... on RegisterValidationError { - errors { - password { - message - type - } - email { - message - type - } - } - } - } - } - """ - response = await graphql_client.query( - query, - variables={ - "input": {"email": "hah@pycon.it", "password": password, "fullname": "Name"} - }, - ) - - assert not response.errors, response.errors - assert response.data["register"] == { - "__typename": "RegisterValidationError", - "errors": { - "password": [ - { - "message": "ensure this value has at least 8 characters", - "type": "value_error.any_str.min_length", - } - ], - "email": None, - }, - } - - -@test("cannot register with invalid email") -async def _(graphql_client=graphql_client, email=each("", "invalid.email"), db=db): - query = """mutation($input: RegisterInput!) { - register(input: $input) { - __typename - ... on RegisterValidationError { - errors { - password { - message - type - } - email { - message - type - } - } - } - } - } - """ - response = await graphql_client.query( - query, - variables={ - "input": {"email": email, "password": "ciaomondo!", "fullname": "Name"} - }, - ) - - assert not response.errors, response.errors - assert response.data["register"] == { - "__typename": "RegisterValidationError", - "errors": { - "email": [ - { - "message": "value is not a valid email address", - "type": "value_error.email", - } - ], - "password": None, - }, - } diff --git a/users-backend/users/api/tests/mutation/test_reset_password_mutation.py b/users-backend/users/api/tests/mutation/test_reset_password_mutation.py deleted file mode 100644 index ffdbdcaef0..0000000000 --- a/users-backend/users/api/tests/mutation/test_reset_password_mutation.py +++ /dev/null @@ -1,185 +0,0 @@ -import time_machine -from sqlalchemy.sql.expression import select -from users.domain.entities import User -from users.domain.repository import UsersRepository -from users.tests.api import graphql_client -from users.tests.factories import user_factory -from users.tests.session import db -from ward import test - - -@test("reset password") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - user = await user_factory(email="test@email.it", password="hello", jwt_auth_id=1) - - query = """ - mutation($input: ResetPasswordInput!) { - resetPassword(input: $input) { - __typename - } - } - """ - response = await graphql_client.query( - query, - variables={ - "input": { - "token": user.create_reset_password_token(), - "newPassword": "newpassword", - } - }, - ) - - assert not response.errors - assert response.data["resetPassword"]["__typename"] == "OperationSuccess" - - query = select(User).where(User.email == "test@email.it") - raw_query_user: User = (await db.execute(query)).scalar() - assert raw_query_user.check_password("newpassword") - assert raw_query_user.jwt_auth_id == 2 - - -@test("cannot reset password with short password") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - user = await user_factory(email="test@email.it", password="hello", jwt_auth_id=1) - - query = """ - mutation($input: ResetPasswordInput!) { - resetPassword(input: $input) { - __typename - - ... on ResetPasswordValidationError { - errors { - newPassword { - type - message - } - } - } - } - } - """ - response = await graphql_client.query( - query, - variables={ - "input": {"token": user.create_reset_password_token(), "newPassword": "new"} - }, - ) - - assert not response.errors - assert ( - response.data["resetPassword"]["__typename"] == "ResetPasswordValidationError" - ) - assert response.data["resetPassword"]["errors"]["newPassword"] == [ - { - "type": "value_error.any_str.min_length", - "message": "ensure this value has at least 8 characters", - } - ] - - query = select(User).where(User.email == "test@email.it") - raw_query_user: User = (await db.execute(query)).scalar() - assert raw_query_user.check_password("hello") - assert raw_query_user.jwt_auth_id == 1 - - -@test("cannot reset password with expired token") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - user = await user_factory(email="test@email.it", password="hello") - - with time_machine.travel("2020-10-10 15:00:00", tick=False): - token = user.create_reset_password_token() - - query = """ - mutation($input: ResetPasswordInput!) { - resetPassword(input: $input) { - __typename - - ... on ResetPasswordTokenExpired { - message - } - } - } - """ - - with time_machine.travel("2020-10-13 15:00:00", tick=False): - response = await graphql_client.query( - query, - variables={"input": {"token": token, "newPassword": "newpasswordtest"}}, - ) - - assert not response.errors - assert response.data["resetPassword"]["__typename"] == "ResetPasswordTokenExpired" - query = select(User).where(User.email == "test@email.it") - raw_query_user: User = (await db.execute(query)).scalar() - assert raw_query_user.check_password("hello") - - -@test("cannot reset password of not active user") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - user = await user_factory(email="test@email.it", password="hello", is_active=False) - - token = user.create_reset_password_token() - - query = """ - mutation($input: ResetPasswordInput!) { - resetPassword(input: $input) { - __typename - - ... on ResetPasswordTokenInvalid { - message - } - } - } - """ - - response = await graphql_client.query( - query, variables={"input": {"token": token, "newPassword": "newpasswordtest"}} - ) - - assert not response.errors - assert response.data["resetPassword"]["__typename"] == "ResetPasswordTokenInvalid" - query = select(User).where(User.email == "test@email.it") - raw_query_user: User = (await db.execute(query)).scalar() - assert raw_query_user.check_password("hello") - - -@test("cannot reset password with token invalidated by new id") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - user = await user_factory( - email="test@email.it", - password="hello", - is_active=True, - jwt_auth_id=1, - ) - - token = user.create_reset_password_token() - - db_query = select(User).where(User.id == user.id) - raw_query_user: User = (await db.execute(db_query)).scalar() - raw_query_user.jwt_auth_id = 2 - repository = UsersRepository(db) - await repository.save_user(raw_query_user) - await repository.commit() - - query = """ - mutation($input: ResetPasswordInput!) { - resetPassword(input: $input) { - __typename - - ... on ResetPasswordTokenInvalid { - message - } - } - } - """ - - response = await graphql_client.query( - query, variables={"input": {"token": token, "newPassword": "newpasswordtest"}} - ) - - assert not response.errors - assert response.data["resetPassword"]["__typename"] == "ResetPasswordTokenInvalid" - - query = select(User).where(User.email == "test@email.it") - raw_query_user: User = (await db.execute(query)).scalar() - assert raw_query_user.check_password("hello") diff --git a/users-backend/users/api/tests/mutation/test_update_profile_mutation.py b/users-backend/users/api/tests/mutation/test_update_profile_mutation.py deleted file mode 100644 index 3b94843cde..0000000000 --- a/users-backend/users/api/tests/mutation/test_update_profile_mutation.py +++ /dev/null @@ -1,58 +0,0 @@ -from sqlalchemy.sql.expression import select -from ward import test - -from users.domain.entities import User -from users.tests.api import graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("update profile") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - user = await user_factory(email="test@email.it", password="hello", jwt_auth_id=1) - - graphql_client.force_login(user) - - query = """mutation($input: UpdateProfileInput!) { - updateProfile(input: $input) { - __typename - - ... on User { - id - name - fullName - gender - openToRecruiting - openToNewsletter - country - dateBirth - } - } - }""" - - response = await graphql_client.query( - query, - variables={ - "input": { - "name": "Name", - "fullName": "Fullname", - "gender": "male", - "openToRecruiting": False, - "openToNewsletter": True, - "country": "IT", - "dateBirth": None, - } - }, - ) - - assert not response.errors - query = select(User).where(User.email == "test@email.it") - raw_query_user: User = (await db.execute(query)).scalar() - - assert raw_query_user.name == "Name" - assert raw_query_user.fullname == "Fullname" - assert raw_query_user.gender == "male" - assert raw_query_user.open_to_recruiting is False - assert raw_query_user.open_to_newsletter is True - assert raw_query_user.country == "IT" - assert raw_query_user.date_birth is None diff --git a/users-backend/users/api/tests/query/__init__.py b/users-backend/users/api/tests/query/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/api/tests/query/test_me.py b/users-backend/users/api/tests/query/test_me.py deleted file mode 100644 index 8aa6d10d74..0000000000 --- a/users-backend/users/api/tests/query/test_me.py +++ /dev/null @@ -1,60 +0,0 @@ -import time_machine -from ward import test - -from users.tests.api import graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("cannot get me if unlogged") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - await user_factory(id=1, email="test@me.it") - - query = """{ - me { - id - email - } - }""" - - response = await graphql_client.query(query) - - assert response.errors[0]["message"] == "Not authenticated" - - -@test("cannot fetch me with expired token") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - user = await user_factory(id=1, email="test@me.it") - - with time_machine.travel("1500-10-10 10:10:10", tick=False): - graphql_client.force_login(user) - - query = """{ - me { - id - email - } - }""" - - with time_machine.travel("2021-01-10 10:10:10", tick=False): - response = await graphql_client.query(query) - - assert response.errors[0]["message"] == "Invalid pastaporto" - - -@test("fetch my data when logged") -async def _(graphql_client=graphql_client, db=db, user_factory=user_factory): - user = await user_factory(id=1, email="test@me.it") - graphql_client.force_login(user) - - query = """{ - me { - id - email - } - }""" - - response = await graphql_client.query(query) - - assert not response.errors - assert response.data["me"] == {"id": "1", "email": "test@me.it"} diff --git a/users-backend/users/api/types.py b/users-backend/users/api/types.py deleted file mode 100644 index 9a077da13e..0000000000 --- a/users-backend/users/api/types.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import annotations - - -import strawberry - - -@strawberry.type -class OperationSuccess: - ok: bool diff --git a/users-backend/users/api/views.py b/users-backend/users/api/views.py deleted file mode 100644 index a799d96b3d..0000000000 --- a/users-backend/users/api/views.py +++ /dev/null @@ -1,71 +0,0 @@ -from typing import Optional, Union - -from starlette.requests import Request -from starlette.responses import Response -from starlette.types import Receive, Scope, Send -from starlette.websockets import WebSocket -from strawberry.asgi import GraphQL as BaseGraphQL -from strawberry.asgi.handlers.http_handler import HTTPHandler - -from users.api.context import Context -from users.api.schema import schema -from users.settings import ENVIRONMENT, IDENTITY_COOKIE_KEY - - -class CustomHTTPHandler(HTTPHandler): - async def handle(self, scope: Scope, receive: Receive, send: Send): - request = Request(scope=scope, receive=receive) - root_value = await self.get_root_value(request) - - sub_response = Response( - content=None, - status_code=None, # type: ignore - headers=None, - media_type=None, - background=None, - ) - - context = await self.get_context(request=request, response=sub_response) - - response = await self.get_http_response( - request=request, - execute=self.execute, - process_result=self.process_result, - graphiql=self.graphiql, - root_value=root_value, - context=context, - ) - - response.headers.raw.extend(sub_response.headers.raw) - - if sub_response.background: - response.background = sub_response.background - - if sub_response.status_code: - response.status_code = sub_response.status_code - - if context._authenticated_as: - response.set_cookie( - key=IDENTITY_COOKIE_KEY, - value=context._authenticated_as.create_identity_token(), - expires=60 * 60 * 24 * 90, - httponly=True, - secure=ENVIRONMENT != "local", - samesite="strict", - ) - - await response(scope, receive, send) - - -class GraphQL(BaseGraphQL): - http_handler_class = CustomHTTPHandler - - def __init__(self, *args, **kwargs) -> None: - super().__init__(schema, *args, **kwargs) - - async def get_context( - self, request: Union[Request, WebSocket], response: Optional[Response] = None - ) -> Context: - return Context( - request=request, response=response, session=request.state.session - ) diff --git a/users-backend/users/db.py b/users-backend/users/db.py deleted file mode 100644 index 4875f887ea..0000000000 --- a/users-backend/users/db.py +++ /dev/null @@ -1,13 +0,0 @@ -from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine - -from users.settings import DATABASE_URL, DB_SSL_MODE - - -def get_engine(*, echo=True): - return create_async_engine( - DATABASE_URL, echo=echo, connect_args={"ssl": DB_SSL_MODE} - ) - - -def get_session(engine): - return AsyncSession(engine) diff --git a/users-backend/users/domain/__init__.py b/users-backend/users/domain/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/domain/entities.py b/users-backend/users/domain/entities.py deleted file mode 100644 index ee385974bb..0000000000 --- a/users-backend/users/domain/entities.py +++ /dev/null @@ -1,149 +0,0 @@ -from __future__ import annotations - -from dataclasses import InitVar, dataclass, field -from datetime import date, datetime, timedelta, timezone -from typing import Optional - -import jwt -from sqlalchemy import Boolean, Column, Date, DateTime, Integer, String, Table -from sqlalchemy.orm import registry -from starlette.authentication import BaseUser - -from users.settings import IDENTITY_SECRET, SECRET_KEY -from users.starlette_password.hashers import ( - check_password, - is_password_usable, - make_password, -) - -mapper_registry = registry() - -UNUSABLE_PASSWORD = object() - - -@dataclass -class User(BaseUser): - email: str - date_joined: datetime - - username: str = "" - fullname: str = "" - name: str = "" - gender: str = "" - date_birth: Optional[date] = None - open_to_recruiting: bool = False - open_to_newsletter: bool = False - country: str = "" - is_active: bool = True - is_staff: bool = False - is_superuser: bool = False - - last_login: Optional[datetime] = None - jwt_auth_id: Optional[int] = 1 - id: Optional[int] = None - hashed_password: Optional[str] = field(default=None, repr=False) - new_password: Optional[str] = field(default=None, repr=False) - - password: InitVar[Optional[str]] = None - - _authenticated_user: bool = field(init=False, default=False) - - def __post_init__(self, password): - if password == UNUSABLE_PASSWORD: - self.hashed_password = make_password(None) - elif password: - self.hashed_password = make_password(password) - - def check_password(self, password: str) -> bool: - return check_password(password, self.hashed_password) - - def set_password(self, raw_password: str): - """ - The password will be changed when the user is saved - """ - self.new_password = raw_password - - def has_usable_password(self) -> bool: - return is_password_usable(self.hashed_password) - - def get_reset_password_jwt_id(self) -> str: - return f"reset-password:{self.id}:{self.jwt_auth_id}" - - def get_auth_jwt_id(self) -> str: - return f"auth:{self.id}:{self.jwt_auth_id}" - - def create_reset_password_token(self) -> str: - now = datetime.now(timezone.utc) - return jwt.encode( - { - "jti": self.get_reset_password_jwt_id(), - "user_id": self.id, - "exp": now + timedelta(hours=1), - "iat": now, - "iss": "users", - "aud": "users/reset-password", - }, - str(SECRET_KEY), - algorithm="HS256", - ) - - def create_identity_token(self) -> str: - now = datetime.now(timezone.utc) - return jwt.encode( - { - "jti": self.get_auth_jwt_id(), - "sub": self.id, - "exp": now + timedelta(days=90), - "iat": now, - "iss": "users", - "aud": "identity", - }, - str(IDENTITY_SECRET), - algorithm="HS256", - ) - - @property - def is_authenticated(self) -> bool: - return True - - @property - def display_name(self) -> str: - return self.fullname or self.name - - -user_table = Table( - "users", - mapper_registry.metadata, - Column("id", Integer(), primary_key=True), - Column("full_name", String(300), nullable=False), - Column("password", String(128), nullable=False), - Column("username", String(100), nullable=True), - Column("email", String(254), unique=True, nullable=False), - Column("name", String(300), nullable=False), - Column("gender", String(10), nullable=False), - Column("date_birth", Date(), nullable=True), - Column("open_to_recruiting", Boolean(), default=False, nullable=False), - Column("open_to_newsletter", Boolean(), default=False, nullable=False), - Column("country", String(50), nullable=False), - Column("date_joined", DateTime(timezone=True), nullable=False), - Column("last_login", DateTime(timezone=True), nullable=True), - Column("is_active", Boolean(), default=True, nullable=False), - Column("is_staff", Boolean(), default=False, nullable=False), - Column("is_superuser", Boolean(), default=False, nullable=False), - Column( - "jwt_auth_id", - Integer(), - default=1, - server_default="1", - nullable=False, - ), -) - -mapper_registry.map_imperatively( - User, - user_table, - properties={ - "hashed_password": user_table.c.password, - "fullname": user_table.c.full_name, - }, -) diff --git a/users-backend/users/domain/paginable.py b/users-backend/users/domain/paginable.py deleted file mode 100644 index 797327a5e3..0000000000 --- a/users-backend/users/domain/paginable.py +++ /dev/null @@ -1,39 +0,0 @@ -from dataclasses import dataclass -from typing import Generic, Type, TypeVar - -from sqlalchemy.ext.asyncio.session import AsyncSession -from sqlalchemy.sql.expression import func, select - -from users.settings import DEFAULT_PAGINATION_TO - -T = TypeVar("T") - - -@dataclass -class Page(Generic[T]): - items: list[T] - total_count: int - - -class Paginable(Generic[T]): - def __init__(self, session: AsyncSession, entity: Type) -> None: - super().__init__() - self.session = session - self.entity = entity - - async def page(self, after: int = 0, to: int = DEFAULT_PAGINATION_TO) -> Page: - if after < 0: - raise ValueError("after cannot be negative") - - if to < 0: - raise ValueError("to cannot be negative") - - query_total_count = select(func.count(self.entity.id)) - query_entities = ( - select(self.entity).limit(to - after).offset(after).order_by("id") - ) - - total_count = (await self.session.execute(query_total_count)).scalar() - entities = (await self.session.execute(query_entities)).scalars().all() - - return Page(items=entities, total_count=total_count) diff --git a/users-backend/users/domain/repository.py b/users-backend/users/domain/repository.py deleted file mode 100644 index 822d0c5cd1..0000000000 --- a/users-backend/users/domain/repository.py +++ /dev/null @@ -1,102 +0,0 @@ -from functools import reduce -from typing import Any, Optional - -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy.sql.expression import or_, select - -from users.domain.entities import User -from users.domain.paginable import Paginable -from users.starlette_password.hashers import make_password - - -class AbstractTransaction: - session: Any - - def transaction(self): - raise NotImplementedError() - - async def commit(self): - raise NotImplementedError() - - async def rollback(self): - raise NotImplementedError() - - -class AbstractUsersRepository(AbstractTransaction): - async def get_by_email(self, email: str) -> Optional[User]: - raise NotImplementedError() - - async def get_by_id(self, id: int) -> Optional[User]: - raise NotImplementedError() - - async def create_user(self, user: User) -> User: - raise NotImplementedError() - - async def save_user(self, user: User) -> User: - raise NotImplementedError() - - -class UsersRepository(AbstractUsersRepository): - session: Optional[AsyncSession] - - def __init__(self, session: Optional[AsyncSession] = None) -> None: - self.session = session - - async def get_users(self) -> Paginable[User]: - return Paginable(self.session, User) - - async def get_batch_by_ids(self, ids: list[int]) -> list[User]: - query = select(User).where(User.id.in_(ids)) - users = (await self.session.execute(query)).scalars().all() - return users - - async def get_batch_by_emails(self, emails: list[str]) -> list[User]: - query = select(User).where(User.email.in_(emails)) - users = (await self.session.execute(query)).scalars().all() - return users - - async def get_by_email(self, email: str) -> Optional[User]: - query = select(User).where(User.email == email) - user = (await self.session.execute(query)).scalar_one_or_none() - return user - - async def get_by_id(self, id: int) -> Optional[User]: - query = select(User).where(User.id == id) - user = (await self.session.execute(query)).scalar_one_or_none() - return user - - async def create_user(self, user: User) -> User: - self.session.add(user) - await self.session.flush() - return user - - async def save_user(self, user: User) -> User: - if user.new_password: - user.hashed_password = make_password(user.new_password) - - await self.session.flush() - return user - - async def search(self, search: str) -> list[User]: - if not search: - return [] - - words = search.split(" ") - fields = [User.fullname, User.name, User.email] - where = or_() - - for field in fields: - where = reduce(or_, (field.ilike(f"%{word}%") for word in words), where) - - query = select(User).where(where).limit(10) - users = (await self.session.execute(query)).scalars().all() - return users - - def transaction(self): - return self.session.begin() - - async def commit(self): - await self.session.commit() - - async def rollback(self): - await self.session.rollback() diff --git a/users-backend/users/domain/services/__init__.py b/users-backend/users/domain/services/__init__.py deleted file mode 100644 index dc4c888fc5..0000000000 --- a/users-backend/users/domain/services/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from .login import LoginInputModel, login -from .register import RegisterInputModel, register -from .request_reset_password import request_reset_password -from .reset_password import ResetPasswordInput, reset_password -from .social_login import SocialAccount, SocialLoginInput, social_login - -__all__ = [ - "login", - "LoginInputModel", - "register", - "RegisterInputModel", - "social_login", - "SocialLoginInput", - "SocialAccount", - "reset_password", - "ResetPasswordInput", - "request_reset_password", -] diff --git a/users-backend/users/domain/services/create_pastaporto.py b/users-backend/users/domain/services/create_pastaporto.py deleted file mode 100644 index 710166e2bc..0000000000 --- a/users-backend/users/domain/services/create_pastaporto.py +++ /dev/null @@ -1,62 +0,0 @@ -import dataclasses -from datetime import datetime, timedelta, timezone -from typing import Any - -import jwt -from pythonit_toolkit.pastaporto.entities import ( - Credential, - Pastaporto, - PastaportoUserInfo, -) - -from users.domain.entities import User -from users.domain.services.exceptions import ( - TokenNotValidAnymoreError, - UserIsNotActiveError, -) -from users.settings import PASTAPORTO_SECRET - - -def create_pastaporto(user: User, decoded_identity: dict[str, Any]) -> str: - if not user.is_active: - raise UserIsNotActiveError() - - if user.get_auth_jwt_id() != decoded_identity["jti"]: - raise TokenNotValidAnymoreError() - - if decoded_identity["sub"] != user.id: - raise ValueError("Mismatching sub != passed user id") - - credentials = [Credential.AUTHENTICATED] - - if user.is_staff or user.is_superuser: - credentials.append(Credential.STAFF) - - return encode_pastaporto( - Pastaporto( - user_info=PastaportoUserInfo( - id=user.id, email=user.email, is_staff=user.is_staff - ), - credentials=credentials, - ) - ) - - -def create_not_authenticated_pastaporto() -> str: - return encode_pastaporto(Pastaporto(user_info=None, credentials=[])) - - -def encode_pastaporto(pastaporto: Pastaporto) -> str: - payload = dataclasses.asdict(pastaporto) - now = datetime.now(timezone.utc) - - return jwt.encode( - { - **payload, - "exp": now + timedelta(minutes=1), - "iat": now, - "iss": "users", - }, - str(PASTAPORTO_SECRET), - algorithm="HS256", - ) diff --git a/users-backend/users/domain/services/exceptions.py b/users-backend/users/domain/services/exceptions.py deleted file mode 100644 index 54c674adb1..0000000000 --- a/users-backend/users/domain/services/exceptions.py +++ /dev/null @@ -1,31 +0,0 @@ -class WrongEmailOrPasswordError(Exception): - """Raised when we can't find a user with the username/password - combination""" - - -class UserIsNotActiveError(Exception): - """Raised when the user is marked as not active""" - - -class EmailAlreadyUsedError(Exception): - """Raised when the email is already used by some other user""" - - -class UserIsNotAdminError(Exception): - """Raised when the login is configured to reject non-admin users""" - - -class ResetPasswordTokenInvalidError(Exception): - """Raised when the reset password token JWT-ID is not valid anymore""" - - -class ResetPasswordTokenExpiredError(Exception): - """Raised when the reset password token is expired""" - - -class UserDoesNotExistError(Exception): - """Raised when the user requested does not exist""" - - -class TokenNotValidAnymoreError(Exception): - """Raised when the token is not valid anymore (jwt id invalided)""" diff --git a/users-backend/users/domain/services/login.py b/users-backend/users/domain/services/login.py deleted file mode 100644 index 5c7c21fb82..0000000000 --- a/users-backend/users/domain/services/login.py +++ /dev/null @@ -1,42 +0,0 @@ -import dataclasses -from datetime import datetime, timezone - -from pydantic import BaseModel, EmailStr, constr -from users.domain.entities import User -from users.domain.repository import AbstractUsersRepository - -from .exceptions import ( - UserIsNotActiveError, - UserIsNotAdminError, - WrongEmailOrPasswordError, -) - - -class LoginInputModel(BaseModel): - email: EmailStr - password: constr(min_length=1) - - -async def login( - input: LoginInputModel, - *, - reject_non_admins: bool = False, - users_repository: AbstractUsersRepository -) -> User: - user = await users_repository.get_by_email(input.email) - - if not user or not user.check_password(input.password): - raise WrongEmailOrPasswordError() - - if not user.is_active: - raise UserIsNotActiveError() - - if reject_non_admins and not user.is_staff: - raise UserIsNotAdminError() - - user.last_login = datetime.now(timezone.utc) - await users_repository.save_user(user) - # TODO this fix ugly hack to avoid db ops - updated_user = dataclasses.replace(user, password=None) - await users_repository.commit() - return updated_user diff --git a/users-backend/users/domain/services/register.py b/users-backend/users/domain/services/register.py deleted file mode 100644 index 820c587210..0000000000 --- a/users-backend/users/domain/services/register.py +++ /dev/null @@ -1,41 +0,0 @@ -import dataclasses -from datetime import datetime, timezone - -from pydantic import BaseModel, EmailStr, constr - -from users.domain.entities import User -from users.domain.repository import AbstractUsersRepository -from users.domain.services.exceptions import EmailAlreadyUsedError - - -class RegisterInputModel(BaseModel): - fullname: constr(min_length=1) - email: EmailStr - password: constr(min_length=8) - - -async def register( - input: RegisterInputModel, *, users_repository: AbstractUsersRepository -) -> User: - existing_user = await users_repository.get_by_email(input.email) - - if existing_user: - raise EmailAlreadyUsedError() - - user = User( - fullname=input.fullname, - email=input.email, - password=input.password, - date_joined=datetime.now(timezone.utc), - ) - created_user = await users_repository.create_user(user) - # Create a copy of the object, one that sqlalchemy - # doesn't know about, so using `return created_user` - # after `commit` will not trigger a new select query - # not sure if it's a bug of sqlalchemy 1.4 beta - # but TODO investigate better and fix it :) - # password=None is required by `replace` - # but doesn't change the user password or anything - created_user = dataclasses.replace(created_user, password=None) - await users_repository.commit() - return created_user diff --git a/users-backend/users/domain/services/request_reset_password.py b/users-backend/users/domain/services/request_reset_password.py deleted file mode 100644 index 20ada2f060..0000000000 --- a/users-backend/users/domain/services/request_reset_password.py +++ /dev/null @@ -1,31 +0,0 @@ -import logging - -from pythonit_toolkit.emails.templates import EmailTemplate - -from users.domain.entities import User -from users.domain.services.exceptions import UserIsNotActiveError -from users.emails import send_email - -logger = logging.getLogger(__file__) - - -async def request_reset_password(user: User): - if not user.is_active: - logger.info( - "Trying to request reset password of not active user_id=%s", user.id - ) - raise UserIsNotActiveError() - - token = user.create_reset_password_token() - - send_email( - template=EmailTemplate.RESET_PASSWORD, - to=user.email, - subject="Reset your password", - variables={ - "firstname": user.name, - # "resetpasswordlink": f"{ASSOCIATION_FRONTEND_URL}/reset-password/{token}", - "resetpasswordlink": f"https://pycon.it/en/reset-password/{token}", - }, - ) - logger.info("Sent reset password token of user_id=%s", user.id) diff --git a/users-backend/users/domain/services/reset_password.py b/users-backend/users/domain/services/reset_password.py deleted file mode 100644 index 1851ca65c5..0000000000 --- a/users-backend/users/domain/services/reset_password.py +++ /dev/null @@ -1,63 +0,0 @@ -import logging -from typing import Any - -import jwt -from pydantic import BaseModel, constr - -from users.domain.entities import user_table -from users.domain.repository import UsersRepository -from users.domain.services.exceptions import ( - ResetPasswordTokenExpiredError, - ResetPasswordTokenInvalidError, - UserDoesNotExistError, - UserIsNotActiveError, -) -from users.settings import SECRET_KEY - -logger = logging.getLogger(__name__) - - -class ResetPasswordInput(BaseModel): - token: str - new_password: constr(min_length=8) - - def decode_token(self) -> dict[str, Any]: - return jwt.decode( - self.token, - str(SECRET_KEY), - issuer="users", - audience="users/reset-password", - algorithms=["HS256"], - options={"require": ["exp", "iss", "aud", "iat", "jti"]}, - ) - - -async def reset_password(input: ResetPasswordInput, *, repository: UsersRepository): - try: - decoded_token = input.decode_token() - except jwt.ExpiredSignatureError: - raise ResetPasswordTokenExpiredError() - except (jwt.InvalidAudienceError, jwt.MissingRequiredClaimError): - raise ResetPasswordTokenInvalidError() - - user_id = decoded_token["user_id"] - user = await repository.get_by_id(user_id) - - if not user: - logger.error("Decoding reset password token return invalid user_id=%s", user_id) - raise UserDoesNotExistError() - - if not user.is_active: - logger.error("Trying to reset password of not active user_id=%s", user_id) - raise UserIsNotActiveError() - - jti = decoded_token["jti"] - - if jti != user.get_reset_password_jwt_id(): - raise ResetPasswordTokenInvalidError() - - logger.info("Resetting password of user_id=%s", user_id) - user.set_password(input.new_password) - user.jwt_auth_id = user_table.c.jwt_auth_id + 1 - await repository.save_user(user) - await repository.commit() diff --git a/users-backend/users/domain/services/social_login.py b/users-backend/users/domain/services/social_login.py deleted file mode 100644 index 16113734b3..0000000000 --- a/users-backend/users/domain/services/social_login.py +++ /dev/null @@ -1,54 +0,0 @@ -import dataclasses -import logging -from datetime import datetime, timezone - -from pydantic import BaseModel, EmailStr, constr -from users.domain.entities import UNUSABLE_PASSWORD, User -from users.domain.repository import AbstractUsersRepository - -logger = logging.getLogger(__name__) - - -class SocialAccount(BaseModel): - social_id: constr(min_length=1) - fullname: str = "" - first_name: str = "" - middle_name: str = "" - last_name: str = "" - - -class SocialLoginInput(BaseModel): - email: EmailStr - social_account: SocialAccount - - -async def social_login( - input: SocialLoginInput, *, users_repository: AbstractUsersRepository -) -> User: - logger.info( - "Request social login with social_id=%s and google provider", - input.social_account.social_id, - ) - - user = await users_repository.get_by_email(input.email) - - if not user: - logger.info("Social login not found, creating a new user") - - user = await users_repository.create_user( - User( - email=input.email, - password=UNUSABLE_PASSWORD, - date_joined=datetime.now(timezone.utc), - fullname=input.social_account.fullname, - name=input.social_account.first_name, - ) - ) - user = dataclasses.replace(user, password=None) - await users_repository.commit() - - logger.info("Created user %s for social login", user.id) - else: - logger.info("Found user %s for social login", user.id) - - return user diff --git a/users-backend/users/domain/services/update_profile.py b/users-backend/users/domain/services/update_profile.py deleted file mode 100644 index 37063ee99e..0000000000 --- a/users-backend/users/domain/services/update_profile.py +++ /dev/null @@ -1,47 +0,0 @@ -import dataclasses -from datetime import date -from typing import Optional - -from pydantic import BaseModel, constr - -from users.domain.entities import User -from users.domain.repository import AbstractUsersRepository -from users.domain.services.exceptions import UserDoesNotExistError, UserIsNotActiveError - - -class UpdateProfileInput(BaseModel): - name: constr(min_length=1) - full_name: constr(min_length=1) - gender: str - open_to_recruiting: bool - open_to_newsletter: bool - country: str - date_birth: Optional[date] - - -async def update_profile( - user_id: int, - input: UpdateProfileInput, - *, - users_repository: AbstractUsersRepository -) -> User: - user = await users_repository.get_by_id(user_id) - - if not user: - raise UserDoesNotExistError() - - if not user.is_active: - raise UserIsNotActiveError() - - user.name = input.name - user.fullname = input.full_name - user.gender = input.gender - user.open_to_newsletter = input.open_to_newsletter - user.open_to_recruiting = input.open_to_recruiting - user.date_birth = input.date_birth - user.country = input.country - - await users_repository.save_user(user) - updated_user = dataclasses.replace(user, password=None) - await users_repository.commit() - return updated_user diff --git a/users-backend/users/domain/tests/__init__.py b/users-backend/users/domain/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/domain/tests/fake_repository.py b/users-backend/users/domain/tests/fake_repository.py deleted file mode 100644 index 07d405805a..0000000000 --- a/users-backend/users/domain/tests/fake_repository.py +++ /dev/null @@ -1,58 +0,0 @@ -import dataclasses -from typing import Optional - -from users.domain.entities import User -from users.domain.repository import AbstractUsersRepository - - -class DummyTransaction: - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_value, exc_traceback): - pass - - -class FakeUsersRepository(AbstractUsersRepository): - committed: bool = False - rolledback: bool = False - _id_counter: int = 1 - - def __init__(self, users: list[User]) -> None: - super().__init__() - - self.USERS = users - self.USERS_BY_EMAIL = {user.email: user for user in users} - self.USERS_BY_ID = {user.id: user for user in users} - - async def get_by_email(self, email: str) -> Optional[User]: - return self.USERS_BY_EMAIL.get(email, None) - - async def get_by_id(self, id: int) -> Optional[User]: - return self.USERS_BY_ID.get(id, None) - - async def get_batch_by_ids(self, ids: list[int]) -> list[User]: - return [user for user in self.USERS if user.id in ids] - - async def create_user(self, user: User) -> User: - self._id_counter = self._id_counter + 1 - - new_user = dataclasses.replace( - user, id=self._id_counter, password=user.password - ) - self.USERS.append(new_user) - self.USERS_BY_EMAIL[new_user.email] = new_user - self.USERS_BY_ID[new_user.id] = new_user - return new_user - - async def save_user(self, user: User) -> User: - return user - - def transaction(self): - return DummyTransaction() - - async def commit(self): - self.committed = True - - async def rollback(self): - self.rolledback = True diff --git a/users-backend/users/domain/tests/services/__init__.py b/users-backend/users/domain/tests/services/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/domain/tests/services/test_create_pastaporto.py b/users-backend/users/domain/tests/services/test_create_pastaporto.py deleted file mode 100644 index 39fe4bffe8..0000000000 --- a/users-backend/users/domain/tests/services/test_create_pastaporto.py +++ /dev/null @@ -1,173 +0,0 @@ -from datetime import datetime - -from pythonit_toolkit.pastaporto.entities import Credential -from pythonit_toolkit.pastaporto.tokens import decode_pastaporto -from ward import raises, test - -from users.domain.entities import User -from users.domain.services.create_pastaporto import ( - create_not_authenticated_pastaporto, - create_pastaporto, -) -from users.domain.services.exceptions import ( - TokenNotValidAnymoreError, - UserIsNotActiveError, -) -from users.settings import PASTAPORTO_SECRET - - -@test("normal user pastaporto") -async def _(): - decoded_identity = {"sub": 1, "jti": "auth:1:1"} - - user = User( - id=1, - username="marco", - password="test", - email="marco@acierno.it", - fullname="Marco Acierno", - name="Marco", - gender="", - date_birth=None, - open_to_newsletter=False, - open_to_recruiting=False, - country="", - date_joined=datetime(2020, 1, 1), - is_staff=False, - is_superuser=False, - is_active=True, - ) - - encoded_pastaporto = create_pastaporto(user, decoded_identity) - decoded_pastaporto = decode_pastaporto(encoded_pastaporto, PASTAPORTO_SECRET) - - assert decoded_pastaporto["user_info"]["id"] == 1 - assert decoded_pastaporto["user_info"]["email"] == "marco@acierno.it" - assert decoded_pastaporto["user_info"]["is_staff"] is False - - assert decoded_pastaporto["credentials"] == [Credential.AUTHENTICATED] - - -@test("staff user pastaporto") -async def _(): - decoded_identity = {"sub": 50, "jti": "auth:50:1"} - - user = User( - id=50, - username="marco", - password="test", - email="test@staff.it", - fullname="Marco Acierno", - name="Marco", - gender="", - date_birth=None, - open_to_newsletter=False, - open_to_recruiting=False, - country="", - date_joined=datetime(2020, 1, 1), - is_staff=True, - is_superuser=False, - is_active=True, - ) - - encoded_pastaporto = create_pastaporto(user, decoded_identity) - decoded_pastaporto = decode_pastaporto(encoded_pastaporto, PASTAPORTO_SECRET) - - assert decoded_pastaporto["user_info"]["id"] == 50 - assert decoded_pastaporto["user_info"]["email"] == "test@staff.it" - assert decoded_pastaporto["user_info"]["is_staff"] is True - - assert len(decoded_pastaporto["credentials"]) == 2 - assert Credential.AUTHENTICATED in decoded_pastaporto["credentials"] - assert Credential.STAFF in decoded_pastaporto["credentials"] - - -@test("cannot create pastaporto of not active user") -async def _(): - decoded_identity = {"sub": 50, "jti": "auth:50:1"} - - user = User( - id=50, - username="marco", - password="test", - email="test@staff.it", - fullname="Marco Acierno", - name="Marco", - gender="", - date_birth=None, - open_to_newsletter=False, - open_to_recruiting=False, - country="", - date_joined=datetime(2020, 1, 1), - is_staff=True, - is_superuser=False, - is_active=False, - ) - - with raises(UserIsNotActiveError): - create_pastaporto(user, decoded_identity) - - -@test("cannot create pastaporto if jwt id changed") -async def _(): - decoded_identity = {"sub": 50, "jti": "auth:50:50"} - - user = User( - id=50, - jwt_auth_id=40, - username="marco", - password="test", - email="test@staff.it", - fullname="Marco Acierno", - name="Marco", - gender="", - date_birth=None, - open_to_newsletter=False, - open_to_recruiting=False, - country="", - date_joined=datetime(2020, 1, 1), - is_staff=True, - is_superuser=False, - is_active=True, - ) - - with raises(TokenNotValidAnymoreError): - create_pastaporto(user, decoded_identity) - - -@test("mismatching user and identity are detected and stopped") -async def _(): - decoded_identity = {"sub": 30, "jti": "auth:50:1"} - - user = User( - id=50, - jwt_auth_id=1, - username="marco", - password="test", - email="test@staff.it", - fullname="Marco Acierno", - name="Marco", - gender="", - date_birth=None, - open_to_newsletter=False, - open_to_recruiting=False, - country="", - date_joined=datetime(2020, 1, 1), - is_staff=True, - is_superuser=False, - is_active=True, - ) - - with raises(ValueError) as exc: - create_pastaporto(user, decoded_identity) - - assert str(exc.raised) == "Mismatching sub != passed user id" - - -@test("not authenticated pastaporto") -async def _(): - encoded_pastaporto = create_not_authenticated_pastaporto() - decoded_pastaporto = decode_pastaporto(encoded_pastaporto, PASTAPORTO_SECRET) - - assert decoded_pastaporto["user_info"] is None - assert decoded_pastaporto["credentials"] == [] diff --git a/users-backend/users/domain/tests/services/test_login.py b/users-backend/users/domain/tests/services/test_login.py deleted file mode 100644 index 9a578d6714..0000000000 --- a/users-backend/users/domain/tests/services/test_login.py +++ /dev/null @@ -1,171 +0,0 @@ -from datetime import datetime, timezone - -import pydantic -import time_machine -from users.domain.entities import User -from users.domain.services.exceptions import ( - UserIsNotActiveError, - UserIsNotAdminError, - WrongEmailOrPasswordError, -) -from users.domain.services.login import LoginInputModel, login -from users.domain.tests.fake_repository import FakeUsersRepository -from ward import raises, test - - -@test("cannot login to not existent email") -async def _(): - repository = FakeUsersRepository(users=[]) - - with raises(WrongEmailOrPasswordError): - await login( - LoginInputModel(email="marco@marco.it", password="hello"), - users_repository=repository, - ) - - -@test("cannot login with wrong password") -async def _(): - user = User( - id=1, - username="marco", - password="test", - email="marco@acierno.it", - fullname="Marco Acierno", - name="Marco", - gender="", - date_birth=None, - open_to_newsletter=False, - open_to_recruiting=False, - country="", - date_joined=datetime(2020, 1, 1), - is_staff=False, - is_superuser=False, - is_active=True, - ) - repository = FakeUsersRepository(users=[user]) - - with raises(WrongEmailOrPasswordError): - await login( - LoginInputModel(email="marco@acierno.it", password="tes"), - users_repository=repository, - ) - - -@test("cannot login to not active user") -async def _(): - user = User( - id=1, - username="marco", - password="test", - email="marco@acierno.it", - fullname="Marco Acierno", - name="Marco", - gender="", - date_birth=None, - open_to_newsletter=False, - open_to_recruiting=False, - country="", - date_joined=datetime(2020, 1, 1), - is_staff=False, - is_superuser=False, - is_active=False, - ) - repository = FakeUsersRepository(users=[user]) - - with raises(UserIsNotActiveError): - await login( - LoginInputModel(email="marco@acierno.it", password="test"), - users_repository=repository, - ) - - -@test("can login") -async def _(): - user = User( - id=1, - username="marco", - password="test", - email="marco@acierno.it", - fullname="Marco Acierno", - name="Marco", - gender="", - date_birth=None, - open_to_newsletter=False, - open_to_recruiting=False, - country="", - date_joined=datetime(2020, 1, 1), - is_staff=False, - is_superuser=False, - is_active=True, - ) - repository = FakeUsersRepository(users=[user]) - - with time_machine.travel("2020-10-10 10:10:00Z", tick=False): - logged_user = await login( - LoginInputModel(email="marco@acierno.it", password="test"), - users_repository=repository, - ) - - assert logged_user.id == user.id - assert logged_user.last_login == datetime( - 2020, 10, 10, 10, 10, 00, tzinfo=timezone.utc - ) - - -@test("cannot login with empty email") -async def _(): - with raises(pydantic.ValidationError) as exc: - LoginInputModel(email="", password="password") - - errors = exc.raised.errors() - assert len(errors) == 1 - assert { - "loc": ("email",), - "msg": "value is not a valid email address", - "type": "value_error.email", - } in errors - - -@test("cannot login with empty password") -async def _(): - with raises(pydantic.ValidationError) as exc: - LoginInputModel(email="my@email.it", password="") - - errors = exc.raised.errors() - assert len(errors) == 1 - assert { - "loc": ("password",), - "msg": "ensure this value has at least 1 characters", - "type": "value_error.any_str.min_length", - "ctx": {"limit_value": 1}, - } in errors - - -@test("reject non admins") -async def _(): - user = User( - id=1, - username="marco", - password="test", - email="marco@acierno.it", - fullname="Marco Acierno", - name="Marco", - gender="", - date_birth=None, - open_to_newsletter=False, - open_to_recruiting=False, - country="", - date_joined=datetime(2020, 1, 1), - is_staff=False, - is_superuser=False, - is_active=True, - ) - repository = FakeUsersRepository(users=[user]) - - with raises(UserIsNotAdminError): - await login( - LoginInputModel(email="marco@acierno.it", password="test"), - reject_non_admins=True, - users_repository=repository, - ) diff --git a/users-backend/users/domain/tests/services/test_register.py b/users-backend/users/domain/tests/services/test_register.py deleted file mode 100644 index c3eb5a0108..0000000000 --- a/users-backend/users/domain/tests/services/test_register.py +++ /dev/null @@ -1,86 +0,0 @@ -from datetime import datetime, timezone - -import pydantic -from ward import raises, test - -from users.domain.entities import User -from users.domain.services import RegisterInputModel, register -from users.domain.services.exceptions import EmailAlreadyUsedError -from users.domain.tests.fake_repository import FakeUsersRepository - - -@test("can register") -async def _(): - repository = FakeUsersRepository(users=[]) - - user = await register( - RegisterInputModel( - email="marco@marco.it", password="hello_world", fullname="name" - ), - users_repository=repository, - ) - - assert repository.committed - assert user.id is not None - assert user.email == "marco@marco.it" - assert user.fullname == "name" - assert user.check_password("hello_world") - - -@test("cannot register with an already used email") -async def _(): - repository = FakeUsersRepository( - users=[User(email="marco@marco.it", date_joined=datetime.now(timezone.utc))] - ) - - with raises(EmailAlreadyUsedError): - await register( - RegisterInputModel( - email="marco@marco.it", password="hello_world", fullname="name" - ), - users_repository=repository, - ) - - -@test("password should be at least 8 chars") -async def _(): - with raises(pydantic.ValidationError) as exc: - RegisterInputModel(email="hello@python.it", password="short", fullname="name") - - errors = exc.raised.errors() - assert len(errors) == 1 - assert { - "loc": ("password",), - "msg": "ensure this value has at least 8 characters", - "type": "value_error.any_str.min_length", - "ctx": {"limit_value": 8}, - } in errors - - -@test("cannot register with empty email") -async def _(): - with raises(pydantic.ValidationError) as exc: - RegisterInputModel(email="", password="password", fullname="name") - - errors = exc.raised.errors() - assert len(errors) == 1 - assert { - "loc": ("email",), - "msg": "value is not a valid email address", - "type": "value_error.email", - } in errors - - -@test("cannot register with empty password") -async def _(): - with raises(pydantic.ValidationError) as exc: - RegisterInputModel(email="my@email.it", password="", fullname="name") - - errors = exc.raised.errors() - assert len(errors) == 1 - assert { - "loc": ("password",), - "msg": "ensure this value has at least 8 characters", - "type": "value_error.any_str.min_length", - "ctx": {"limit_value": 8}, - } in errors diff --git a/users-backend/users/domain/tests/services/test_request_reset_password.py b/users-backend/users/domain/tests/services/test_request_reset_password.py deleted file mode 100644 index de5ac060b9..0000000000 --- a/users-backend/users/domain/tests/services/test_request_reset_password.py +++ /dev/null @@ -1,25 +0,0 @@ -from datetime import datetime, timezone - -from users.domain.entities import User -from users.domain.services.exceptions import UserIsNotActiveError -from users.domain.services.request_reset_password import request_reset_password -from ward import raises, test - - -@test("test request reset password") -async def _(): - user = User( - email="test@email.it", date_joined=datetime.now(timezone.utc), is_active=True - ) - await request_reset_password(user) - - # assert email sent? - - -@test("cannot request reset password of not active user") -async def _(): - user = User( - email="test@email.it", date_joined=datetime.now(timezone.utc), is_active=False - ) - with raises(UserIsNotActiveError): - await request_reset_password(user) diff --git a/users-backend/users/domain/tests/services/test_reset_password.py b/users-backend/users/domain/tests/services/test_reset_password.py deleted file mode 100644 index 09eb522de4..0000000000 --- a/users-backend/users/domain/tests/services/test_reset_password.py +++ /dev/null @@ -1,195 +0,0 @@ -from datetime import datetime, timedelta, timezone - -import jwt -import time_machine -from ward import raises, test - -from users.domain.entities import User -from users.domain.services.exceptions import ( - ResetPasswordTokenExpiredError, - ResetPasswordTokenInvalidError, - UserDoesNotExistError, - UserIsNotActiveError, -) -from users.domain.services.reset_password import ResetPasswordInput, reset_password -from users.domain.tests.fake_repository import FakeUsersRepository -from users.settings import SECRET_KEY - - -@test("cannot reset password of not active user") -async def _(): - user = User( - id=10, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - is_active=False, - ) - - with raises(UserIsNotActiveError): - await reset_password( - ResetPasswordInput( - token=user.create_reset_password_token(), new_password="testnewpassword" - ), - repository=FakeUsersRepository([user]), - ) - - -@test("cannot reset password with invalidated jwt") -async def _(): - user = User( - id=10, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - password="old_password", - is_active=True, - jwt_auth_id=1, - ) - - token = user.create_reset_password_token() - user.jwt_auth_id = 2 - - with raises(ResetPasswordTokenInvalidError): - await reset_password( - ResetPasswordInput(token=token, new_password="testnewpassword"), - repository=FakeUsersRepository([user]), - ) - - -@test("cannot reset password with jwt not for reset password") -async def _(): - user = User( - id=10, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - password="old_password", - is_active=True, - jwt_auth_id=1, - ) - - with time_machine.travel("2021-10-10 15:00:00Z", tick=False): - token = jwt.encode( - { - "jti": user.get_reset_password_jwt_id(), - "user_id": 10, - "exp": datetime.now(timezone.utc) + timedelta(minutes=30), - "iat": datetime.now(timezone.utc), - "iss": "users", - "aud": "users/not-reset-password", - }, - str(SECRET_KEY), - ) - - with raises(ResetPasswordTokenInvalidError): - await reset_password( - ResetPasswordInput(token=token, new_password="testnewpassword"), - repository=FakeUsersRepository([user]), - ) - - -@test("cannot reset password with jwt without id") -async def _(): - user = User( - id=10, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - password="old_password", - is_active=True, - jwt_auth_id=1, - ) - - with time_machine.travel("2021-10-10 15:00:00Z", tick=False): - token = jwt.encode( - { - "user_id": 10, - "exp": datetime.now(timezone.utc) + timedelta(minutes=30), - "iat": datetime.now(timezone.utc), - "iss": "users", - "aud": "users/not-reset-password", - }, - str(SECRET_KEY), - ) - - with raises(ResetPasswordTokenInvalidError): - await reset_password( - ResetPasswordInput(token=token, new_password="testnewpassword"), - repository=FakeUsersRepository([user]), - ) - - -@test("cannot reset password with jwt of not existent user") -async def _(): - user = User( - id=10, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - password="old_password", - is_active=True, - jwt_auth_id=1, - ) - - with time_machine.travel("2021-10-10 15:00:00Z", tick=False): - token = jwt.encode( - { - "jti": "reset-password:20:1", - "user_id": 20, - "exp": datetime.now(timezone.utc) + timedelta(minutes=30), - "iat": datetime.now(timezone.utc), - "iss": "users", - "aud": "users/reset-password", - }, - str(SECRET_KEY), - ) - - with raises(UserDoesNotExistError): - await reset_password( - ResetPasswordInput(token=token, new_password="testnewpassword"), - repository=FakeUsersRepository([user]), - ) - - -@test("cannot reset password with expired jwt") -async def _(): - user = User( - id=10, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - password="old_password", - is_active=True, - jwt_auth_id=1, - ) - - with time_machine.travel("2020-10-10 10:10:10Z", tick=False): - token = user.create_reset_password_token() - - with time_machine.travel("2020-10-10 15:10:10Z", tick=False), raises( - ResetPasswordTokenExpiredError - ): - await reset_password( - ResetPasswordInput(token=token, new_password="testnewpassword"), - repository=FakeUsersRepository([user]), - ) - - -@test("reset user password") -async def _(): - user = User( - id=10, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - password="old_password", - is_active=True, - jwt_auth_id=1, - ) - - await reset_password( - ResetPasswordInput( - token=user.create_reset_password_token(), new_password="testnewpassword" - ), - repository=FakeUsersRepository([user]), - ) - - assert user.new_password == "testnewpassword" - - # we did column + 1 so the value updated in the DB and not python - assert user.jwt_auth_id.left.name == "jwt_auth_id" - assert user.jwt_auth_id.right.value == 1 diff --git a/users-backend/users/domain/tests/services/test_social_login.py b/users-backend/users/domain/tests/services/test_social_login.py deleted file mode 100644 index 388ec631f9..0000000000 --- a/users-backend/users/domain/tests/services/test_social_login.py +++ /dev/null @@ -1,104 +0,0 @@ -from datetime import datetime, timezone - -import time_machine -from pydantic import ValidationError -from users.domain.entities import User -from users.domain.services import SocialAccount, SocialLoginInput, social_login -from users.domain.tests.fake_repository import FakeUsersRepository -from ward import raises, test -from ward.testing import each - - -@test("can login with non existent account") -async def _(): - repository = FakeUsersRepository(users=[]) - - with time_machine.travel("2020-10-10 10:10:00Z", tick=False): - user = await social_login( - SocialLoginInput( - email="test@me.it", - social_account=SocialAccount( - social_id="1", - fullname="Test Account", - first_name="Test", - last_name="Account", - ), - ), - users_repository=repository, - ) - - assert user - assert user.id is not None - assert user.date_joined == datetime(2020, 10, 10, 10, 10, 0, tzinfo=timezone.utc) - assert not user.has_usable_password() - assert user.fullname == "Test Account" - assert user.name == "Test" - assert user.email == "test@me.it" - - -@test("can login to account with same email") -async def _(): - repository = FakeUsersRepository( - users=[ - User( - id=10, - email="test@me.it", - date_joined=datetime.now(timezone.utc), - password="my_password", - fullname="Hello World", - name="Hello", - ) - ] - ) - - user = await social_login( - SocialLoginInput( - email="test@me.it", - social_account=SocialAccount( - social_id="1", - fullname="Test Account", - first_name="Test", - last_name="Account", - ), - ), - users_repository=repository, - ) - - assert user - assert user.has_usable_password() - assert user.check_password("my_password") - assert user.id == 10 - assert user.fullname == "Hello World" - assert user.name == "Hello" - assert user.email == "test@me.it" - - -@test("cannot social login with an invalid email") -async def _(email=each("", "invalid")): - with raises(ValidationError) as exc: - SocialLoginInput(email=email, social_account=SocialAccount(social_id="10")) - - errors = exc.raised.errors() - assert len(errors) == 1 - assert { - "loc": ("email",), - "msg": "value is not a valid email address", - "type": "value_error.email", - } in errors - - -@test("cannot social login without social id") -async def _(): - with raises(ValidationError) as exc: - SocialLoginInput( - email="test@email.it", social_account=SocialAccount(social_id="") - ) - - errors = exc.raised.errors() - assert len(errors) == 1 - assert { - "loc": ("social_id",), - "msg": "ensure this value has at least 1 characters", - "type": "value_error.any_str.min_length", - "ctx": {"limit_value": 1}, - } in errors diff --git a/users-backend/users/domain/tests/services/test_update_profile.py b/users-backend/users/domain/tests/services/test_update_profile.py deleted file mode 100644 index 09878be21a..0000000000 --- a/users-backend/users/domain/tests/services/test_update_profile.py +++ /dev/null @@ -1,101 +0,0 @@ -from datetime import date, datetime, timezone - -from ward import raises, test - -from users.domain.entities import User -from users.domain.services.exceptions import UserDoesNotExistError, UserIsNotActiveError -from users.domain.services.update_profile import UpdateProfileInput, update_profile -from users.domain.tests.fake_repository import FakeUsersRepository - - -@test("update profile") -async def _(): - user = User( - id=10, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - password="password", - is_active=True, - jwt_auth_id=1, - name="Old name", - fullname="Old fullname", - gender="f", - open_to_recruiting=True, - open_to_newsletter=True, - date_birth=date(1900, 1, 1), - country="US", - ) - - user = await update_profile( - user.id, - UpdateProfileInput( - name="New name", - full_name="Full name", - gender="m", - open_to_recruiting=False, - open_to_newsletter=False, - date_birth=date(2020, 10, 1), - country="IT", - ), - users_repository=FakeUsersRepository([user]), - ) - - assert user.name == "New name" - assert user.fullname == "Full name" - assert user.gender == "m" - assert user.open_to_recruiting is False - assert user.open_to_newsletter is False - assert user.date_birth == date(2020, 10, 1) - assert user.country == "IT" - - -@test("cannot update not active user") -async def _(): - user = User( - id=10, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - password="password", - is_active=False, - jwt_auth_id=1, - name="Old name", - fullname="Old fullname", - gender="f", - open_to_recruiting=True, - open_to_newsletter=True, - date_birth=date(1900, 1, 1), - country="US", - ) - - with raises(UserIsNotActiveError): - await update_profile( - user.id, - UpdateProfileInput( - name="New name", - full_name="Full name", - gender="m", - open_to_recruiting=False, - open_to_newsletter=False, - date_birth=date(2020, 10, 1), - country="IT", - ), - users_repository=FakeUsersRepository([user]), - ) - - -@test("cannot update if user does not exist") -async def _(): - with raises(UserDoesNotExistError): - await update_profile( - 5, - UpdateProfileInput( - name="New name", - full_name="Full name", - gender="m", - open_to_recruiting=False, - open_to_newsletter=False, - date_birth=date(2020, 10, 1), - country="IT", - ), - users_repository=FakeUsersRepository([]), - ) diff --git a/users-backend/users/domain/tests/test_entities.py b/users-backend/users/domain/tests/test_entities.py deleted file mode 100644 index f832cb8588..0000000000 --- a/users-backend/users/domain/tests/test_entities.py +++ /dev/null @@ -1,31 +0,0 @@ -from datetime import datetime, timezone - -import jwt -import time_machine -from users.domain.entities import User -from users.settings import SECRET_KEY -from ward import test - - -@test("generate reset password token") -async def _(): - user = User( - id=50, - email="test@email.it", - date_joined=datetime.now(timezone.utc), - jwt_auth_id=1, - ) - - with time_machine.travel("2020-10-10 10:10:10Z", tick=False): - token = user.create_reset_password_token() - - decoded_token = jwt.decode( - token, - str(SECRET_KEY), - audience="users/reset-password", - issuer="users", - algorithms=["HS256"], - ) - - assert decoded_token["user_id"] == 50 - assert decoded_token["jti"] == "reset-password:50:1" diff --git a/users-backend/users/domain/tests/test_paginable.py b/users-backend/users/domain/tests/test_paginable.py deleted file mode 100644 index 6bd8de5bce..0000000000 --- a/users-backend/users/domain/tests/test_paginable.py +++ /dev/null @@ -1,65 +0,0 @@ -from users.domain.entities import User -from users.domain.paginable import Paginable -from users.tests.factories import user_factory -from users.tests.session import db -from ward import raises, test - - -@test("paginate items") -async def _(db=db, user_factory=user_factory): - user_1 = await user_factory(email="user1@email.it") - user_2 = await user_factory(email="user2@email.it") - user_3 = await user_factory(email="user3@email.it") - - paginable = Paginable(db, User) - page = await paginable.page(0, 1) - - assert page.total_count == 3 - assert page.items[0].id == user_1.id - - page = await paginable.page(1, 3) - - assert page.total_count == 3 - assert page.items[0].id == user_2.id - assert page.items[1].id == user_3.id - - -@test("size outside total") -async def _(db=db, user_factory=user_factory): - user_1 = await user_factory(email="user1@email.it") - user_2 = await user_factory(email="user2@email.it") - user_3 = await user_factory(email="user3@email.it") - - paginable = Paginable(db, User) - page = await paginable.page(0, 1000) - - assert page.total_count == 3 - assert [i.id for i in page.items] == [user_1.id, user_2.id, user_3.id] - - -@test("negative after is not allowed") -async def _(db=db, user_factory=user_factory): - await user_factory(email="user1@email.it") - await user_factory(email="user2@email.it") - await user_factory(email="user3@email.it") - - paginable = Paginable(db, User) - - with raises(ValueError) as exc: - await paginable.page(-10, 1000) - - assert str(exc.raised) == "after cannot be negative" - - -@test("negative to is not allowed") -async def _(db=db, user_factory=user_factory): - await user_factory(email="user1@email.it") - await user_factory(email="user2@email.it") - await user_factory(email="user3@email.it") - - paginable = Paginable(db, User) - - with raises(ValueError) as exc: - await paginable.page(0, -100) - - assert str(exc.raised) == "to cannot be negative" diff --git a/users-backend/users/domain/tests/test_repository.py b/users-backend/users/domain/tests/test_repository.py deleted file mode 100644 index 93acce9fc3..0000000000 --- a/users-backend/users/domain/tests/test_repository.py +++ /dev/null @@ -1,287 +0,0 @@ -import datetime -from typing import cast - -from sqlalchemy.ext.asyncio.session import AsyncSession -from sqlalchemy.sql.expression import select -from ward import test - -from users.domain.entities import User -from users.domain.repository import UsersRepository -from users.tests.factories import user_factory -from users.tests.session import db, second_session - - -@test("create user") -async def _(db=db, second_session=second_session): - db = cast(AsyncSession, db) - second_session = cast(AsyncSession, second_session) - - repository = UsersRepository(db) - - await repository.create_user( - User( - email="test@user.it", - password="hello", - date_joined=datetime.datetime(2020, 1, 1, 1, tzinfo=datetime.timezone.utc), - username="username", - fullname="Nanan Nana", - gender="male", - date_birth=datetime.date(2000, 5, 1), - open_to_newsletter=False, - open_to_recruiting=True, - country="IT", - is_active=True, - is_staff=False, - is_superuser=False, - ) - ) - await repository.commit() - - query = select(User).where(User.email == "test@user.it") - db_user: User = (await second_session.execute(query)).scalar() - - assert db_user - assert db_user.date_joined == datetime.datetime( - 2020, 1, 1, 1, tzinfo=datetime.timezone.utc - ) - assert db_user.username == "username" - assert db_user.fullname == "Nanan Nana" - assert db_user.gender == "male" - assert db_user.date_birth == datetime.date(2000, 5, 1) - assert db_user.open_to_newsletter is False - assert db_user.open_to_recruiting is True - assert db_user.country == "IT" - assert db_user.is_active is True - assert db_user.is_staff is False - assert db_user.is_superuser is False - - -@test("get all users") -async def _(db=db, second_session=second_session, user_factory=user_factory): - db = cast(AsyncSession, db) - second_session = cast(AsyncSession, second_session) - - user = await user_factory(email="user1@user.it", id=1) - user_2 = await user_factory(email="user2@user.it", id=2) - user_3 = await user_factory(email="user3@user.it", id=3) - - repository = UsersRepository(db) - paginable = await repository.get_users() - - page = await paginable.page(0, 1) - - assert page.total_count == 3 - assert page.items[0].id == user.id - - page = await paginable.page(1, 3) - assert page.total_count == 3 - assert [i.id for i in page.items] == [user_2.id, user_3.id] - - -@test("get user by email") -async def _( - db=db, - second_session=second_session, - user_factory=user_factory, -): - db = cast(AsyncSession, db) - second_session = cast(AsyncSession, second_session) - - await user_factory(email="existing@user.it") - await db.commit() - - repository = UsersRepository(db) - found_user = await repository.get_by_email("existing@user.it") - - query = select(User).where(User.email == "existing@user.it") - raw_query_user: User = (await second_session.execute(query)).scalar() - - # Check what we get executing the "raw query" - # and what the repository returned. Is this the same thing? - assert found_user.id == raw_query_user.id - assert found_user.date_joined == raw_query_user.date_joined - assert found_user.username == raw_query_user.username - assert found_user.fullname == raw_query_user.fullname - assert found_user.gender == raw_query_user.gender - assert found_user.date_birth == raw_query_user.date_birth - assert found_user.open_to_newsletter is raw_query_user.open_to_newsletter - assert found_user.open_to_recruiting is raw_query_user.open_to_recruiting - assert found_user.country == raw_query_user.country - assert found_user.is_active is raw_query_user.is_active - assert found_user.is_staff is raw_query_user.is_staff - assert found_user.is_superuser is raw_query_user.is_superuser - - -@test("get user by id") -async def _( - db=db, - second_session=second_session, - user_factory=user_factory, -): - db = cast(AsyncSession, db) - second_session = cast(AsyncSession, second_session) - - created_user = await user_factory(id=10, email="existing@user.it") - await db.commit() - - repository = UsersRepository(db) - # TODO test if it makes sense - found_user = await repository.get_by_id(10) - - query = select(User).where(User.id == 10) - raw_query_user: User = (await second_session.execute(query)).scalar() - - assert found_user.id == created_user.id - assert found_user.username == created_user.username - assert found_user.email == created_user.email - - # Check what we get executing the "raw query" - # and what the repository returned. Is this the same thing? - assert found_user.id == raw_query_user.id - assert found_user.date_joined == raw_query_user.date_joined - assert found_user.username == raw_query_user.username - assert found_user.fullname == raw_query_user.fullname - assert found_user.gender == raw_query_user.gender - assert found_user.date_birth == raw_query_user.date_birth - assert found_user.open_to_newsletter is raw_query_user.open_to_newsletter - assert found_user.open_to_recruiting is raw_query_user.open_to_recruiting - assert found_user.country == raw_query_user.country - assert found_user.is_active is raw_query_user.is_active - assert found_user.is_staff is raw_query_user.is_staff - assert found_user.is_superuser is raw_query_user.is_superuser - - -@test("search users") -async def _(db=db, user_factory=user_factory): - user_1 = await user_factory(email="marco@email.it", fullname="Marco Ciao", name="") - await user_factory(email="nina@email.it", fullname="Nina Nana", name="") - await user_factory(email="patrick@email.it", fullname="Patrick Ciao", name="") - - repository = UsersRepository(db) - found_users = await repository.search("Marco") - - assert len(found_users) == 1 - assert found_users[0].id == user_1.id - - -@test("search users is case-insensitive") -async def _(db=db, user_factory=user_factory): - user_1 = await user_factory(email="marco@email.it", fullname="Marco Ciao", name="") - await user_factory(email="nina@email.it", fullname="Nina Nana", name="") - user_3 = await user_factory( - email="patrick@email.it", fullname="Patrick Ciao", name="" - ) - - repository = UsersRepository(db) - found_users = await repository.search("ciao") - - assert len(found_users) == 2 - ids = [u.id for u in found_users] - - assert user_1.id in ids - assert user_3.id in ids - - -@test("search users returns empty if there are no results") -async def _(db=db, user_factory=user_factory): - await user_factory(email="marco@email.it", fullname="Marco Ciao", name="") - await user_factory(email="nina@email.it", fullname="Nina Nana", name="") - await user_factory(email="patrick@email.it", fullname="Patrick Ciao", name="") - - repository = UsersRepository(db) - found_users = await repository.search("nono") - - assert len(found_users) == 0 - - -@test("search users by email") -async def _(db=db, user_factory=user_factory): - await user_factory(email="marco@email.it", fullname="Marco Ciao", name="") - await user_factory(email="nina@email.it", fullname="Nina Nana", name="") - user = await user_factory( - email="ohhello@email.it", fullname="Not In my name", name="" - ) - - repository = UsersRepository(db) - found_users = await repository.search("ohhello") - - assert len(found_users) == 1 - assert found_users[0].id == user.id - - -@test("search users by email, fullname and name") -async def _(db=db, user_factory=user_factory): - user_1 = await user_factory(email="marco@email.it", fullname="Hello Ciao", name="") - user_2 = await user_factory( - email="nina@email.it", fullname="Nina Nana", name="Nope Hello!" - ) - user_3 = await user_factory( - email="ohhello@email.it", fullname="Not In my name", name="" - ) - await user_factory(email="notincluded@email.it", fullname="Ciao mondo!", name="") - - repository = UsersRepository(db) - found_users = await repository.search("hello") - - assert len(found_users) == 3 - ids = [u.id for u in found_users] - - assert user_1.id in ids - assert user_2.id in ids - assert user_3.id in ids - - -@test("search users by multiple words") -async def _(db=db, user_factory=user_factory): - user_1 = await user_factory(email="marco@email.it", fullname="Hello Ciao", name="") - user_2 = await user_factory( - email="nina@email.it", fullname="Nina Nana", name="Nope Hello!" - ) - await user_factory(email="ohhello@email.it", fullname="Not In my name", name="") - - repository = UsersRepository(db) - found_users = await repository.search("Nina Ciao") - - assert len(found_users) == 2 - ids = [u.id for u in found_users] - - assert user_1.id in ids - assert user_2.id in ids - - -@test("cannot search users by empty query") -async def _(db=db, user_factory=user_factory): - await user_factory(email="marco@email.it", fullname="Hello Ciao", name="") - await user_factory(email="nina@email.it", fullname="Nina Nana", name="Nope Hello!") - await user_factory(email="ohhello@email.it", fullname="Not In my name", name="") - await user_factory(email="notincluded@email.it", fullname="Ciao mondo!", name="") - - repository = UsersRepository(db) - found_users = await repository.search("") - - assert len(found_users) == 0 - - -@test("get multiple users by id") -async def _(db=db, user_factory=user_factory): - u1 = await user_factory(email="marco@email.it", fullname="Hello Ciao", name="") - u2 = await user_factory( - email="nina@email.it", fullname="Nina Nana", name="Nope Hello!" - ) - u3 = await user_factory( - email="ohhello@email.it", fullname="Not In my name", name="" - ) - u4 = await user_factory( - email="notincluded@email.it", fullname="Ciao mondo!", name="" - ) - - repository = UsersRepository(db) - users = await repository.get_batch_by_ids([u1.id, u2.id, u3.id]) - - assert len(users) == 3 - found_ids = [u.id for u in users] - - assert u1.id in found_ids - assert u2.id in found_ids - assert u3.id in found_ids - assert u4.id not in found_ids diff --git a/users-backend/users/emails.py b/users-backend/users/emails.py deleted file mode 100644 index 2f3d9e1599..0000000000 --- a/users-backend/users/emails.py +++ /dev/null @@ -1,20 +0,0 @@ -from typing import Optional - -from pythonit_toolkit.emails import get_email_backend -from pythonit_toolkit.emails.templates import EmailTemplate -from users.settings import DEFAULT_EMAIL_FROM, EMAIL_BACKEND, ENVIRONMENT - - -def send_email( - *, - template: EmailTemplate, - to: str, - subject: str, - from_: Optional[str] = None, - variables: Optional[dict[str, str]] = None -): - from_ = from_ or DEFAULT_EMAIL_FROM - backend = get_email_backend(EMAIL_BACKEND, environment=ENVIRONMENT) - backend.send_email( - template=template, from_=from_, to=to, subject=subject, variables=variables - ) diff --git a/users-backend/users/internal_api/__init__.py b/users-backend/users/internal_api/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/internal_api/context.py b/users-backend/users/internal_api/context.py deleted file mode 100644 index 795c8b3b12..0000000000 --- a/users-backend/users/internal_api/context.py +++ /dev/null @@ -1,23 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass - -from sqlalchemy.ext.asyncio import AsyncSession -from starlette.requests import Request - -from users.domain.repository import UsersRepository - - -@dataclass -class Info: - context: Context - - -@dataclass -class Context: - request: Request - session: AsyncSession - - @property - def users_repository(self) -> UsersRepository: - return UsersRepository(session=self.session) diff --git a/users-backend/users/internal_api/input_types.py b/users-backend/users/internal_api/input_types.py deleted file mode 100644 index bb46002ea6..0000000000 --- a/users-backend/users/internal_api/input_types.py +++ /dev/null @@ -1,8 +0,0 @@ -import strawberry - - -@strawberry.input -class LoginInput: - email: str - password: str - staff_only: bool diff --git a/users-backend/users/internal_api/mutation.py b/users-backend/users/internal_api/mutation.py deleted file mode 100644 index 1842b52f84..0000000000 --- a/users-backend/users/internal_api/mutation.py +++ /dev/null @@ -1,38 +0,0 @@ -import logging -from typing import Optional - -import strawberry - -from users.domain.services.exceptions import ( - UserIsNotActiveError, - UserIsNotAdminError, - WrongEmailOrPasswordError, -) -from users.domain.services.login import LoginInputModel, login as login_service -from users.internal_api.context import Info -from users.internal_api.input_types import LoginInput -from users.internal_api.permissions import IsService -from users.internal_api.types import User - -logger = logging.getLogger(__name__) - - -@strawberry.type -class Mutation: - @strawberry.mutation(permission_classes=[IsService(["pycon-backend"])]) - async def login(self, info: Info, input: LoginInput) -> Optional[User]: - logger.info("Internal api request to login") - try: - logged_user = await login_service( - input=LoginInputModel(email=input.email, password=input.password), - reject_non_admins=input.staff_only, - users_repository=info.context.users_repository, - ) - return User.from_domain(logged_user) - except ( - WrongEmailOrPasswordError, - UserIsNotActiveError, - UserIsNotAdminError, - ) as e: - logger.info("Login failed", exc_info=e) - return None diff --git a/users-backend/users/internal_api/permissions.py b/users-backend/users/internal_api/permissions.py deleted file mode 100644 index 97691c271b..0000000000 --- a/users-backend/users/internal_api/permissions.py +++ /dev/null @@ -1,9 +0,0 @@ -from pythonit_toolkit.api.permissions import IsService as BaseIsService - -from users.settings import SERVICE_TO_SERVICE_SECRET - - -def IsService(allowed_callers: list[str]): - return BaseIsService( - allowed_callers, str(SERVICE_TO_SERVICE_SECRET), "users-backend" - ) diff --git a/users-backend/users/internal_api/query.py b/users-backend/users/internal_api/query.py deleted file mode 100644 index 0ed4a21e17..0000000000 --- a/users-backend/users/internal_api/query.py +++ /dev/null @@ -1,75 +0,0 @@ -import logging -from typing import Optional - -import jwt -import strawberry -from strawberry import ID - -from users.domain.services import exceptions -from users.domain.services.create_pastaporto import create_pastaporto -from users.internal_api.context import Info -from users.internal_api.permissions import IsService -from users.internal_api.types import CreatePastaporto, User -from users.settings import IDENTITY_SECRET - -logger = logging.getLogger(__name__) - - -@strawberry.type -class Query: - @strawberry.field(permission_classes=[IsService(["gateway"])]) - async def create_pastaporto( - self, info: Info, identity_token: Optional[str] - ) -> CreatePastaporto: - if not identity_token: - return CreatePastaporto.not_authenticated() - - try: - decoded_identity = jwt.decode( - identity_token, - str(IDENTITY_SECRET), - audience="identity", - issuer="users", - algorithms=["HS256"], - ) - except jwt.exceptions.InvalidTokenError as e: - raise ValueError("Identity token is not valid") from e - - user_id = decoded_identity["sub"] - user = await info.context.users_repository.get_by_id(int(user_id)) - - try: - pastaporto_token = create_pastaporto(user, decoded_identity) - except (exceptions.TokenNotValidAnymoreError, exceptions.UserIsNotActiveError): - return CreatePastaporto.not_authenticated() - - return CreatePastaporto(pastaporto_token=pastaporto_token) - - @strawberry.field(permission_classes=[IsService(["gateway", "pycon-backend"])]) - async def user(self, info: Info, id: ID) -> Optional[User]: - logger.info("Internal api request to get user_id=%s information", id) - user = await info.context.users_repository.get_by_id(int(id)) - return User.from_domain(user) if user else None - - @strawberry.field(permission_classes=[IsService(["pycon-backend"])]) - async def users_by_ids(self, info: Info, ids: list[ID]) -> list[User]: - logger.info("Internal api request to get users=%s information", ids) - users = await info.context.users_repository.get_batch_by_ids( - [int(id) for id in ids] - ) - return [User.from_domain(user) for user in users] - - @strawberry.field(permission_classes=[IsService(["pycon-backend"])]) - async def search_users(self, info: Info, query: str) -> list[User]: - users = await info.context.users_repository.search(query) - return [User.from_domain(user) for user in users] - - @strawberry.field(permission_classes=[IsService(["association-backend"])]) - async def user_by_email(self, info: Info, email: str) -> Optional[User]: - user = await info.context.users_repository.get_by_email(email) - return User.from_domain(user) if user else None - - @strawberry.field(permission_classes=[IsService(["pycon-backend"])]) - async def users_by_emails(self, info: Info, emails: list[str]) -> list[User]: - users = await info.context.users_repository.get_batch_by_emails(emails) - return [User.from_domain(user) for user in users] diff --git a/users-backend/users/internal_api/schema.py b/users-backend/users/internal_api/schema.py deleted file mode 100644 index 7bddbffbc6..0000000000 --- a/users-backend/users/internal_api/schema.py +++ /dev/null @@ -1,6 +0,0 @@ -import strawberry - -from .mutation import Mutation -from .query import Query - -schema = strawberry.Schema(query=Query, mutation=Mutation) diff --git a/users-backend/users/internal_api/tests/__init__.py b/users-backend/users/internal_api/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/internal_api/tests/mutations/test_login.py b/users-backend/users/internal_api/tests/mutations/test_login.py deleted file mode 100644 index 4b669c5631..0000000000 --- a/users-backend/users/internal_api/tests/mutations/test_login.py +++ /dev/null @@ -1,116 +0,0 @@ -from ward import test - -from users.tests.api import internalapi_graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("login") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - user = await user_factory(email="testuser@user.it", password="hello", is_staff=True) - - query = """mutation($input: LoginInput!) { - login(input: $input) { - id - email - } - }""" - - response = await internalapi_graphql_client.query( - query, - variables={ - "input": {"email": user.email, "password": "hello", "staffOnly": True} - }, - ) - - assert not response.errors - assert response.data["login"]["id"] == str(user.id) - assert response.data["login"]["email"] == user.email - - -@test("cannot login if only staff allowed") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - user = await user_factory( - email="testuser@user.it", password="hello", is_staff=False - ) - - query = """mutation($input: LoginInput!) { - login(input: $input) { - id - email - } - }""" - - response = await internalapi_graphql_client.query( - query, - variables={ - "input": {"email": user.email, "password": "hello", "staffOnly": True} - }, - ) - - assert not response.errors - assert not response.data["login"] - - -@test("cannot login with wrong password") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - user = await user_factory(email="testuser@user.it", password="hello", is_staff=True) - - query = """mutation($input: LoginInput!) { - login(input: $input) { - id - email - } - }""" - - response = await internalapi_graphql_client.query( - query, - variables={ - "input": {"email": user.email, "password": "hellowrong", "staffOnly": True} - }, - ) - - assert not response.errors - assert not response.data["login"] - - -@test("cannot login with empty password") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - user = await user_factory(email="testuser@user.it", password="hello", is_staff=True) - - query = """mutation($input: LoginInput!) { - login(input: $input) { - id - email - } - }""" - - response = await internalapi_graphql_client.query( - query, - variables={"input": {"email": user.email, "password": "", "staffOnly": True}}, - ) - - assert ( - "ensure this value has at least 1 characters" in response.errors[0]["message"] - ) - assert not response.data["login"] diff --git a/users-backend/users/internal_api/tests/queries/__init__.py b/users-backend/users/internal_api/tests/queries/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/internal_api/tests/queries/test_create_pastaporto_query.py b/users-backend/users/internal_api/tests/queries/test_create_pastaporto_query.py deleted file mode 100644 index 5dc22d4cdd..0000000000 --- a/users-backend/users/internal_api/tests/queries/test_create_pastaporto_query.py +++ /dev/null @@ -1,169 +0,0 @@ -import jwt -import time_machine -from pythonit_toolkit.pastaporto.entities import Credential -from pythonit_toolkit.pastaporto.tokens import decode_pastaporto -from ward import each, test - -from users.settings import PASTAPORTO_SECRET -from users.tests.api import internalapi_graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("create pastaporto for user") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login() - user = await user_factory(email="testuser@user.it", is_staff=False) - - query = """query($token: String) { - createPastaporto(identityToken: $token) { - pastaportoToken - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"token": user.create_identity_token()} - ) - - assert not response.errors - token = response.data["createPastaporto"]["pastaportoToken"] - pastaporto = decode_pastaporto(token, PASTAPORTO_SECRET) - - assert pastaporto["user_info"]["id"] == user.id - assert pastaporto["user_info"]["email"] == user.email - assert pastaporto["user_info"]["is_staff"] == user.is_staff - assert pastaporto["credentials"] == [Credential.AUTHENTICATED] - - -@test("create pastaporto for invalid token {token_to_try} is not authenticated") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, - token_to_try=each("", None), -): - internalapi_graphql_client.force_service_login() - - query = """query($token: String) { - createPastaporto(identityToken: $token) { - pastaportoToken - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"token": token_to_try} - ) - - assert not response.errors - token = response.data["createPastaporto"]["pastaportoToken"] - pastaporto = decode_pastaporto(token, PASTAPORTO_SECRET) - - assert pastaporto["user_info"] is None - assert pastaporto["credentials"] == [] - - -@test("not active user doesn't create token") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login() - user = await user_factory(email="testuser@user.it", is_staff=False, is_active=False) - - query = """query($token: String) { - createPastaporto(identityToken: $token) { - pastaportoToken - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"token": user.create_identity_token()} - ) - - assert not response.errors - token = response.data["createPastaporto"]["pastaportoToken"] - pastaporto = decode_pastaporto(token, PASTAPORTO_SECRET) - - assert pastaporto["user_info"] is None - assert pastaporto["credentials"] == [] - - -@test("invalid identity token is rejected") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login() - await user_factory(email="testuser@user.it", is_staff=False, is_active=False) - - query = """query($token: String) { - createPastaporto(identityToken: $token) { - pastaportoToken - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"token": "random_value"} - ) - - assert response.errors[0]["message"] == "Identity token is not valid" - assert not response.data - - -@test("expired identity is rejected") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login() - user = await user_factory(email="testuser@user.it", is_staff=False, is_active=False) - - with time_machine.travel("2020-10-10 10:00:00", tick=False): - expired_token = user.create_identity_token() - - query = """query($token: String) { - createPastaporto(identityToken: $token) { - pastaportoToken - } - }""" - - with time_machine.travel("2022-03-03 10:00:00", tick=False): - response = await internalapi_graphql_client.query( - query, variables={"token": expired_token} - ) - - assert response.errors[0]["message"] == "Identity token is not valid" - assert not response.data - - -@test("identity signed with a different key is rejected") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login() - await user_factory(email="testuser@user.it", is_staff=False, is_active=False) - - invalid_token = jwt.encode({}, "invalidme") - - query = """query($token: String) { - createPastaporto(identityToken: $token) { - pastaportoToken - } - }""" - - with time_machine.travel("2022-03-03 10:00:00", tick=False): - response = await internalapi_graphql_client.query( - query, variables={"token": invalid_token} - ) - - assert response.errors[0]["message"] == "Identity token is not valid" - assert not response.data diff --git a/users-backend/users/internal_api/tests/queries/test_search_users.py b/users-backend/users/internal_api/tests/queries/test_search_users.py deleted file mode 100644 index 7ca05197fe..0000000000 --- a/users-backend/users/internal_api/tests/queries/test_search_users.py +++ /dev/null @@ -1,84 +0,0 @@ -from ward import test - -from users.tests.api import internalapi_graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("search users") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - - user_1 = await user_factory( - email="testuser@user.it", fullname="Name", is_staff=False - ) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - user_3 = await user_factory( - email="testuser3@user.it", fullname="Name", is_staff=False - ) - - query = """query($query: String!) { - searchUsers(query: $query) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"query": "name"} - ) - assert not response.errors - assert len(response.data["searchUsers"]) == 2 - assert {"id": str(user_1.id)} in response.data["searchUsers"] - assert {"id": str(user_3.id)} in response.data["searchUsers"] - - -@test("cannot call without token") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - await user_factory(email="testuser@user.it", fullname="Name", is_staff=False) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($query: String!) { - searchUsers(query: $query) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"query": "name"} - ) - assert response.errors[0]["message"] == "Forbidden" - assert not response.data - - -@test("cannot call token of not allowed service") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="not-allowed-service") - - await user_factory(email="testuser@user.it", fullname="Name", is_staff=False) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($query: String!) { - searchUsers(query: $query) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"query": "name"} - ) - assert response.errors[0]["message"] == "Forbidden" - assert not response.data diff --git a/users-backend/users/internal_api/tests/queries/test_user_by_email.py b/users-backend/users/internal_api/tests/queries/test_user_by_email.py deleted file mode 100644 index baf204cc3e..0000000000 --- a/users-backend/users/internal_api/tests/queries/test_user_by_email.py +++ /dev/null @@ -1,105 +0,0 @@ -from ward import test - -from users.tests.api import internalapi_graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("correctly gets the user when sending a valid email") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="association-backend") - - user_1 = await user_factory( - email="testuser@user.it", fullname="Name", is_staff=False - ) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($email: String!) { - userByEmail(email: $email) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"email": user_1.email} - ) - assert not response.errors - assert response.data["userByEmail"]["id"] == str(user_1.id) - - -@test("returns None when the email doesn't exist") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="association-backend") - - await user_factory(email="testuser@user.it", fullname="Name", is_staff=False) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($email: String!) { - userByEmail(email: $email) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"email": "testemail@email.it"} - ) - assert not response.errors - assert response.data["userByEmail"] is None - - -@test("returns None with invalid email") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="association-backend") - - await user_factory(email="testuser@user.it", fullname="Name", is_staff=False) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($email: String!) { - userByEmail(email: $email) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"email": "testemail"} - ) - assert not response.errors - assert response.data["userByEmail"] is None - - -@test("requires authentication") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - await user_factory(email="testuser@user.it", fullname="Name", is_staff=False) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($email: String!) { - userByEmail(email: $email) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"email": "testemail"} - ) - assert response.errors[0]["message"] == "Forbidden" - assert response.data["userByEmail"] is None diff --git a/users-backend/users/internal_api/tests/queries/test_user_query.py b/users-backend/users/internal_api/tests/queries/test_user_query.py deleted file mode 100644 index b40a4dc105..0000000000 --- a/users-backend/users/internal_api/tests/queries/test_user_query.py +++ /dev/null @@ -1,76 +0,0 @@ -from ward import test - -from users.tests.api import internalapi_graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("get user by id") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login() - - user = await user_factory(email="testuser@user.it", is_staff=False) - - query = """query($id: ID!) { - user(id: $id) { - id - email - isStaff - } - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": user.id}) - assert not response.errors - assert { - "id": str(user.id), - "email": user.email, - "isStaff": False, - } == response.data["user"] - - -@test("get user by not existent id") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login() - - await user_factory(email="testuser@user.it", is_staff=False) - - query = """query($id: ID!) { - user(id: $id) { - id - email - isStaff - } - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": 100}) - assert not response.errors - assert response.data["user"] is None - - -@test("cannot get user without a service to service token") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - user = await user_factory(email="testuser@user.it", is_staff=False) - - query = """query($id: ID!) { - user(id: $id) { - id - email - isStaff - } - }""" - - response = await internalapi_graphql_client.query(query, variables={"id": user.id}) - assert response.errors[0]["message"] == "Forbidden" - assert not response.data["user"] diff --git a/users-backend/users/internal_api/tests/queries/test_users_by_emails.py b/users-backend/users/internal_api/tests/queries/test_users_by_emails.py deleted file mode 100644 index cc7fc1d933..0000000000 --- a/users-backend/users/internal_api/tests/queries/test_users_by_emails.py +++ /dev/null @@ -1,138 +0,0 @@ -from ward import test - -from users.tests.api import internalapi_graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("get users by emails") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - user_1 = await user_factory( - email="testuser@user.it", fullname="Name", is_staff=False - ) - user_2 = await user_factory( - email="testuser2@user.it", fullname="Another", is_staff=False - ) - query = """query($emails: [String!]!) { - usersByEmails(emails: $emails) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"emails": [user_1.email, user_2.email]} - ) - - assert not response.errors - assert len(response.data["usersByEmails"]) == 2 - assert {"id": str(user_1.id)} in response.data["usersByEmails"] - assert {"id": str(user_2.id)} in response.data["usersByEmails"] - - -@test("get users by emails with no emails passed returns nothing") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - - await user_factory(email="testuser@user.it", fullname="Name", is_staff=False) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($emails: [String!]!) { - usersByEmails(emails: $emails) { - id - } - }""" - - response = await internalapi_graphql_client.query(query, variables={"emails": []}) - assert not response.errors - assert len(response.data["usersByEmails"]) == 0 - - -@test("user is not returned if the email does not exist") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - - await user_factory(email="testuser@user.it", fullname="Name", is_staff=False) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($emails: [String!]!) { - usersByEmails(emails: $emails) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"emails": ["sushi@user.it"]} - ) - assert not response.errors - assert len(response.data["usersByEmails"]) == 0 - - -@test("cannot call without token") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - user_1 = await user_factory( - email="testuser@user.it", fullname="Name", is_staff=False - ) - user_2 = await user_factory( - email="testuser2@user.it", fullname="Another", is_staff=False - ) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($emails: [String!]!) { - usersByEmails(emails: $emails) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"emails": [user_1.email, user_2.email]} - ) - assert response.errors[0]["message"] == "Forbidden" - assert not response.data - - -@test("cannot call token of not allowed service") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="not-allowed-service") - - user_1 = await user_factory( - email="testuser@user.it", fullname="Name", is_staff=False - ) - user_2 = await user_factory( - email="testuser2@user.it", fullname="Another", is_staff=False - ) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($emails: [String!]!) { - usersByEmails(emails: $emails) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"emails": [user_1.email, user_2.email]} - ) - assert response.errors[0]["message"] == "Forbidden" - assert not response.data diff --git a/users-backend/users/internal_api/tests/queries/test_users_by_ids.py b/users-backend/users/internal_api/tests/queries/test_users_by_ids.py deleted file mode 100644 index 281ac53397..0000000000 --- a/users-backend/users/internal_api/tests/queries/test_users_by_ids.py +++ /dev/null @@ -1,140 +0,0 @@ -from ward import test - -from users.tests.api import internalapi_graphql_client -from users.tests.factories import user_factory -from users.tests.session import db - - -@test("get users by ids") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - - user_1 = await user_factory( - email="testuser@user.it", fullname="Name", is_staff=False - ) - user_2 = await user_factory( - email="testuser2@user.it", fullname="Another", is_staff=False - ) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($ids: [ID!]!) { - usersByIds(ids: $ids) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"ids": [user_1.id, user_2.id]} - ) - assert not response.errors - assert len(response.data["usersByIds"]) == 2 - assert {"id": str(user_1.id)} in response.data["usersByIds"] - assert {"id": str(user_2.id)} in response.data["usersByIds"] - - -@test("get users by ids with no ids passed returns nothing") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - - await user_factory(email="testuser@user.it", fullname="Name", is_staff=False) - await user_factory(email="testuser2@user.it", fullname="Another", is_staff=False) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($ids: [ID!]!) { - usersByIds(ids: $ids) { - id - } - }""" - - response = await internalapi_graphql_client.query(query, variables={"ids": []}) - assert not response.errors - assert len(response.data["usersByIds"]) == 0 - - -@test("user is not returned if the id does not exist") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="pycon-backend") - - await user_factory(id=1, email="testuser@user.it", fullname="Name", is_staff=False) - await user_factory( - id=2, email="testuser2@user.it", fullname="Another", is_staff=False - ) - await user_factory(id=3, email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($ids: [ID!]!) { - usersByIds(ids: $ids) { - id - } - }""" - - response = await internalapi_graphql_client.query(query, variables={"ids": [50]}) - assert not response.errors - assert len(response.data["usersByIds"]) == 0 - - -@test("cannot call without token") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - user_1 = await user_factory( - email="testuser@user.it", fullname="Name", is_staff=False - ) - user_2 = await user_factory( - email="testuser2@user.it", fullname="Another", is_staff=False - ) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($ids: [ID!]!) { - usersByIds(ids: $ids) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"ids": [user_1.id, user_2.id]} - ) - assert response.errors[0]["message"] == "Forbidden" - assert not response.data - - -@test("cannot call token of not allowed service") -async def _( - internalapi_graphql_client=internalapi_graphql_client, - db=db, - user_factory=user_factory, -): - internalapi_graphql_client.force_service_login(issuer="not-allowed-service") - - user_1 = await user_factory( - email="testuser@user.it", fullname="Name", is_staff=False - ) - user_2 = await user_factory( - email="testuser2@user.it", fullname="Another", is_staff=False - ) - await user_factory(email="testuser3@user.it", fullname="Name", is_staff=False) - - query = """query($ids: [ID!]!) { - usersByIds(ids: $ids) { - id - } - }""" - - response = await internalapi_graphql_client.query( - query, variables={"ids": [user_1.id, user_2.id]} - ) - assert response.errors[0]["message"] == "Forbidden" - assert not response.data diff --git a/users-backend/users/internal_api/types.py b/users-backend/users/internal_api/types.py deleted file mode 100644 index 346dea020b..0000000000 --- a/users-backend/users/internal_api/types.py +++ /dev/null @@ -1,49 +0,0 @@ -from typing import Optional - -import strawberry - -from users.domain import entities -from users.domain.services.create_pastaporto import create_not_authenticated_pastaporto - - -@strawberry.type -class User: - id: strawberry.ID - fullname: str - username: Optional[str] - name: str - email: str - is_active: bool - is_staff: bool - jwt_auth_id: int - gender: str - country: str - - @strawberry.field - def display_name(self) -> str: - name = self.fullname or self.name or self.username - return f"{name} <{self.email}>" - - @classmethod - def from_domain(cls, user: entities.User): - return User( - id=user.id, - email=user.email, - is_active=user.is_active, - is_staff=user.is_staff, - jwt_auth_id=user.jwt_auth_id, - fullname=user.fullname, - username=user.username, - name=user.name, - gender=user.gender, - country=user.country, - ) - - -@strawberry.type -class CreatePastaporto: - pastaporto_token: str - - @classmethod - def not_authenticated(cls) -> str: - return cls(pastaporto_token=create_not_authenticated_pastaporto()) diff --git a/users-backend/users/internal_api/views.py b/users-backend/users/internal_api/views.py deleted file mode 100644 index bfa3e4a911..0000000000 --- a/users-backend/users/internal_api/views.py +++ /dev/null @@ -1,40 +0,0 @@ -from typing import Optional, Union - -from starlette.requests import Request -from starlette.responses import Response -from starlette.types import Receive, Scope, Send -from starlette.websockets import WebSocket -from strawberry.asgi import GraphQL as BaseGraphQL -from strawberry.utils.debug import pretty_print_graphql_operation - -from users.internal_api.context import Context -from users.internal_api.schema import schema - - -class GraphQL(BaseGraphQL): - def __init__(self) -> None: - super().__init__(schema) - - async def get_context( - self, request: Union[Request, WebSocket], response: Optional[Response] - ) -> Context: - return Context(request=request, session=request.state.session) - - async def handle_websocket(self, scope: Scope, receive: Receive, send: Send): - websocket = WebSocket(scope=scope, receive=receive, send=send) - await websocket.close() - - async def execute( - self, query, variables=None, context=None, operation_name=None, root_value=None - ): - if self.debug: - pretty_print_graphql_operation(operation_name, query, variables) - - return await self.schema.execute( - query, - root_value=root_value, - variable_values=variables, - operation_name=operation_name, - context_value=context, - validate_queries=False, - ) diff --git a/users-backend/users/settings.py b/users-backend/users/settings.py deleted file mode 100644 index 5f59544763..0000000000 --- a/users-backend/users/settings.py +++ /dev/null @@ -1,83 +0,0 @@ -from sqlalchemy.engine.url import URL, make_url -from starlette.config import Config -from starlette.datastructures import Secret - -config = Config(".env") - -DEBUG = config("DEBUG", cast=bool, default=False) -DATABASE_URL = config("DATABASE_URL") -ENVIRONMENT = config("ENV", default="local") - -# Emails -EMAIL_BACKEND = config( - "EMAIL_BACKEND", default="pythonit_toolkit.emails.backends.local.LocalEmailBackend" -) -DEFAULT_EMAIL_FROM = config("DEFAULT_EMAIL_FROM", default="noreply@pycon.it") - -# Google social auth -GOOGLE_AUTH_CLIENT_ID = config("GOOGLE_AUTH_CLIENT_ID", cast=Secret, default=None) -GOOGLE_AUTH_CLIENT_SECRET = config( - "GOOGLE_AUTH_CLIENT_SECRET", cast=Secret, default=None -) - -SOCIAL_LOGIN_JWT_COOKIE_NAME = config( - "SOCIAL_LOGIN_JWT_COOKIE_NAME", cast=str, default="social-jwt-token" -) - -# Identity / Pastaporto secrets -IDENTITY_SECRET = config("IDENTITY_SECRET", cast=Secret) -PASTAPORTO_SECRET = config("PASTAPORTO_SECRET", cast=Secret) -SERVICE_TO_SERVICE_SECRET = config( - "SERVICE_TO_SERVICE_SECRET", cast=Secret, default=None -) -SECRET_KEY = config("SECRET_KEY", cast=Secret) - -IDENTITY_EXPIRES_AFTER_MINUTES = config( - "IDENTITY_EXPIRES_AFTER_MINUTES", cast=int, default=60 -) - -# Sentry -SENTRY_DSN = config("SENTRY_DSN", cast=Secret, default="") - -# Pagination - -DEFAULT_PAGINATION_TO = 20 - -# Passwords - -PASSWORD_HASHERS = [ - "users.starlette_password.hashers.Argon2PasswordHasher", - "users.starlette_password.hashers.PBKDF2PasswordHasher", - "users.starlette_password.hashers.PBKDF2SHA1PasswordHasher", - "users.starlette_password.hashers.BCryptSHA256PasswordHasher", -] - -# Services URLs - -ASSOCIATION_FRONTEND_URL = config( - "ASSOCIATION_FRONTEND_URL", -) - -# Unit test configuration - -RUNNING_TESTS = config("RUNNING_TESTS", cast=bool, default=False) - -IDENTITY_COOKIE_KEY = "identity_v2" - -if RUNNING_TESTS: - original_url = make_url(DATABASE_URL) - test_db_url = URL.create( - drivername=original_url.drivername, - username=original_url.username, - password=original_url.password, - host=original_url.host, - port=original_url.port, - database=f"TEST_{original_url.database}", - query=original_url.query, - ) - - DATABASE_URL = str(test_db_url) - PASSWORD_HASHERS = ["users.starlette_password.plain_hasher.PlainPasswordHasher"] - SERVICE_TO_SERVICE_SECRET = "test-service-to-service" - -DB_SSL_MODE = config("DB_SSL_MODE", default="prefer") diff --git a/users-backend/users/social_auth/__init__.py b/users-backend/users/social_auth/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/social_auth/tests/__init__.py b/users-backend/users/social_auth/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/social_auth/tests/test_google_login.py b/users-backend/users/social_auth/tests/test_google_login.py deleted file mode 100644 index bf04c846f7..0000000000 --- a/users-backend/users/social_auth/tests/test_google_login.py +++ /dev/null @@ -1,105 +0,0 @@ -from typing import cast -from unittest.mock import patch - -from sqlalchemy.ext.asyncio.session import AsyncSession -from sqlalchemy.sql.expression import select -from users.domain.entities import User -from users.tests.api import testclient -from users.tests.factories import user_factory -from users.tests.session import db -from ward import test - - -@test("social login creates a new account if it does not exist") -async def _(testclient=testclient, db=db): - db = cast(AsyncSession, db) - - query = select(User).where(User.email == "google@user.it") - db_user: User = (await db.execute(query)).scalar() - - assert not db_user - - with patch("users.social_auth.views.oauth.google.authorize_access_token"), patch( - "users.social_auth.views.oauth.google.parse_id_token" - ) as parse_id_mock: - parse_id_mock.return_value = { - "email": "google@user.it", - "sub": "10001001010", - "name": "Nina Nana", - "email_verified": True, - "given_name": "Nina", - "family_name": "Nana", - } - - response = await testclient.get("/login/google/auth") - - assert response.status_code == 200 - query = select(User).where(User.email == "google@user.it") - db_user: User = (await db.execute(query)).scalar() - - assert db_user - assert db_user.fullname == "Nina Nana" - assert db_user.name == "Nina" - assert not db_user.has_usable_password() - - -@test("social login to account with same email") -async def _( - testclient=testclient, - user_factory=user_factory, - db=db, -): - db = cast(AsyncSession, db) - - existent_user = await user_factory( - email="i@exist.it", fullname="I Already", name="Exist" - ) - await db.commit() - - with patch("users.social_auth.views.oauth.google.authorize_access_token"), patch( - "users.social_auth.views.oauth.google.parse_id_token" - ) as parse_id_mock: - parse_id_mock.return_value = { - "email": "i@exist.it", - "sub": "10001001010", - "name": "Nina Nana", - "email_verified": True, - "given_name": "Nina", - "family_name": "Nana", - } - - response = await testclient.get("/login/google/auth") - - assert response.status_code == 200 - - query = select(User).where(User.email == existent_user.email) - db_user: User = (await db.execute(query)).scalar() - - assert db_user - assert db_user.fullname == "I Already" - assert db_user.name == "Exist" - - -@test("reject google account if the email is not verified") -async def _(testclient=testclient, db=db): - db = cast(AsyncSession, db) - - with patch("users.social_auth.views.oauth.google.authorize_access_token"), patch( - "users.social_auth.views.oauth.google.parse_id_token" - ) as parse_id_mock: - parse_id_mock.return_value = { - "email": "i@exist.it", - "sub": "10001001010", - "name": "Nina Nana", - "email_verified": False, - "given_name": "Nina", - "family_name": "Nana", - } - - response = await testclient.get("/login/google/auth") - - assert response.status_code == 400 - query = select(User).where(User.email == "i@exist.it") - db_user: User = (await db.execute(query)).scalar() - - assert not db_user diff --git a/users-backend/users/social_auth/views.py b/users-backend/users/social_auth/views.py deleted file mode 100644 index eb28cd79f1..0000000000 --- a/users-backend/users/social_auth/views.py +++ /dev/null @@ -1,53 +0,0 @@ -from authlib.integrations.starlette_client import OAuth -from starlette.responses import JSONResponse - -from users.domain import services -from users.domain.services.social_login import SocialAccount, SocialLoginInput -from users.settings import GOOGLE_AUTH_CLIENT_ID, GOOGLE_AUTH_CLIENT_SECRET - -oauth = OAuth() - - -CONF_URL = "https://accounts.google.com/.well-known/openid-configuration" -oauth.register( - name="google", - server_metadata_url=CONF_URL, - client_id=str(GOOGLE_AUTH_CLIENT_ID), - client_secret=str(GOOGLE_AUTH_CLIENT_SECRET), - client_kwargs={"scope": "openid email profile"}, -) - - -async def google_login(request): - redirect_uri = request.url_for("auth") - return await oauth.google.authorize_redirect(request, redirect_uri) - - -async def google_login_auth(request): - token = await oauth.google.authorize_access_token(request) - user = await oauth.google.parse_id_token(request, token) - - if not user["email_verified"]: - return JSONResponse({"error": "Email not verified"}, status_code=400) - - email = user["email"] - user = await services.social_login( - SocialLoginInput( - email=email, - social_account=SocialAccount( - social_id=user["sub"], - fullname=user["name"], - first_name=user["given_name"], - last_name=user["family_name"], - ), - ), - users_repository=request.state.users_repository, - ) - - # response = RedirectResponse(url="/") - # Temporary - response = JSONResponse({"ok": True}) - # response.set_cookie( - # SOCIAL_LOGIN_JWT_COOKIE_NAME, json.dumps({"jwt": user.generate_token()}) - # ) - return response diff --git a/users-backend/users/starlette_password/__init__.py b/users-backend/users/starlette_password/__init__.py deleted file mode 100644 index 019723bac5..0000000000 --- a/users-backend/users/starlette_password/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Copied over from django 3.2 diff --git a/users-backend/users/starlette_password/crypto.py b/users-backend/users/starlette_password/crypto.py deleted file mode 100644 index 0653da479a..0000000000 --- a/users-backend/users/starlette_password/crypto.py +++ /dev/null @@ -1,71 +0,0 @@ -import datetime -import hashlib -import secrets -from decimal import Decimal - -RANDOM_STRING_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - - -def force_bytes(s, encoding="utf-8", strings_only=False, errors="strict"): - """ - Similar to smart_bytes, except that lazy instances are resolved to - strings, rather than kept as lazy objects. - If strings_only is True, don't convert (some) non-string-like objects. - """ - # Handle the common case first for performance reasons. - if isinstance(s, bytes): - if encoding == "utf-8": - return s - else: - return s.decode("utf-8", errors).encode(encoding, errors) - if strings_only and is_protected_type(s): - return s - if isinstance(s, memoryview): - return bytes(s) - return str(s).encode(encoding, errors) - - -def is_protected_type(obj): - """Determine if the object instance is of a protected type. - Objects of protected types are preserved as-is when passed to - force_str(strings_only=True). - """ - return isinstance(obj, _PROTECTED_TYPES) - - -_PROTECTED_TYPES = ( - type(None), - int, - float, - Decimal, - datetime.datetime, - datetime.date, - datetime.time, -) - - -def constant_time_compare(val1, val2): - """Return True if the two strings are equal, False otherwise.""" - return secrets.compare_digest(force_bytes(val1), force_bytes(val2)) - - -def get_random_string(length, allowed_chars=RANDOM_STRING_CHARS): - """ - Return a securely generated random string. - The bit length of the returned value can be calculated with the formula: - log_2(len(allowed_chars)^length) - For example, with default `allowed_chars` (26+26+10), this gives: - * length: 12, bit length =~ 71 bits - * length: 22, bit length =~ 131 bits - """ - return "".join(secrets.choice(allowed_chars) for i in range(length)) - - -def pbkdf2(password, salt, iterations, dklen=0, digest=None): - """Return the hash of password using pbkdf2.""" - if digest is None: - digest = hashlib.sha256 - dklen = dklen or None - password = force_bytes(password) - salt = force_bytes(salt) - return hashlib.pbkdf2_hmac(digest().name, password, salt, iterations, dklen) diff --git a/users-backend/users/starlette_password/hashers.py b/users-backend/users/starlette_password/hashers.py deleted file mode 100644 index 166cf25d4a..0000000000 --- a/users-backend/users/starlette_password/hashers.py +++ /dev/null @@ -1,737 +0,0 @@ -# Copied over from django 3.2 - -import base64 -import binascii -import functools -import hashlib -import importlib -import math -import warnings - -from users.settings import PASSWORD_HASHERS - -from .crypto import ( - RANDOM_STRING_CHARS, - constant_time_compare, - get_random_string, - pbkdf2, -) -from .imports import import_string - -UNUSABLE_PASSWORD_PREFIX = "!" # This will never be a valid encoded hash -UNUSABLE_PASSWORD_SUFFIX_LENGTH = ( - 40 # number of random chars to add after UNUSABLE_PASSWORD_PREFIX -) - - -def is_password_usable(encoded): - """ - Return True if this password wasn't generated by - User.set_unusable_password(), i.e. make_password(None). - """ - return encoded is None or not encoded.startswith(UNUSABLE_PASSWORD_PREFIX) - - -def check_password(password, encoded, setter=None, preferred="default"): - """ - Return a boolean of whether the raw password matches the three - part encoded digest. - - If setter is specified, it'll be called when you need to - regenerate the password. - """ - if password is None or not is_password_usable(encoded): - return False - - preferred = get_hasher(preferred) - try: - hasher = identify_hasher(encoded) - except ValueError: - # encoded is gibberish or uses a hasher that's no longer installed. - return False - - hasher_changed = hasher.algorithm != preferred.algorithm - must_update = hasher_changed or preferred.must_update(encoded) - is_correct = hasher.verify(password, encoded) - - # If the hasher didn't change (we don't protect against enumeration if it - # does) and the password should get updated, try to close the timing gap - # between the work factor of the current encoded password and the default - # work factor. - if not is_correct and not hasher_changed and must_update: - hasher.harden_runtime(password, encoded) - - if setter and is_correct and must_update: - setter(password) - return is_correct - - -def make_password(password, salt=None, hasher="default"): - """ - Turn a plain-text password into a hash for database storage - - Same as encode() but generate a new random salt. If password is None then - return a concatenation of UNUSABLE_PASSWORD_PREFIX and a random string, - which disallows logins. Additional random string reduces chances of gaining - access to staff or superuser accounts. See ticket #20079 for more info. - """ - if password is None: - return UNUSABLE_PASSWORD_PREFIX + get_random_string( - UNUSABLE_PASSWORD_SUFFIX_LENGTH - ) - if not isinstance(password, (bytes, str)): - raise TypeError( - "Password must be a string or bytes, got %s." % type(password).__qualname__ - ) - hasher = get_hasher(hasher) - salt = salt or hasher.salt() - return hasher.encode(password, salt) - - -@functools.lru_cache() -def get_hashers(): - hashers = [] - for hasher_path in PASSWORD_HASHERS: - hasher_cls = import_string(hasher_path) - hasher = hasher_cls() - if not getattr(hasher, "algorithm"): - raise ValueError( - "hasher doesn't specify an " "algorithm name: %s" % hasher_path - ) - hashers.append(hasher) - return hashers - - -@functools.lru_cache() -def get_hashers_by_algorithm(): - return {hasher.algorithm: hasher for hasher in get_hashers()} - - -# @receiver(setting_changed) -# def reset_hashers(**kwargs): -# if kwargs['setting'] == 'PASSWORD_HASHERS': -# get_hashers.cache_clear() -# get_hashers_by_algorithm.cache_clear() - - -def get_hasher(algorithm="default"): - """ - Return an instance of a loaded password hasher. - - If algorithm is 'default', return the default hasher. Lazily import hashers - specified in the project's settings file if needed. - """ - if hasattr(algorithm, "algorithm"): - return algorithm - - elif algorithm == "default": - return get_hashers()[0] - - else: - hashers = get_hashers_by_algorithm() - try: - return hashers[algorithm] - except KeyError: - raise ValueError( - "Unknown password hashing algorithm '%s'. " - "Did you specify it in the PASSWORD_HASHERS " - "setting?" % algorithm - ) - - -def identify_hasher(encoded): - """ - Return an instance of a loaded password hasher. - - Identify hasher algorithm by examining encoded hash, and call - get_hasher() to return hasher. Raise ValueError if - algorithm cannot be identified, or if hasher is not loaded. - """ - # Ancient versions of Django created plain MD5 passwords and accepted - # MD5 passwords with an empty salt. - if (len(encoded) == 32 and "$" not in encoded) or ( - len(encoded) == 37 and encoded.startswith("md5$$") - ): - algorithm = "unsalted_md5" - # Ancient versions of Django accepted SHA1 passwords with an empty salt. - elif len(encoded) == 46 and encoded.startswith("sha1$$"): - algorithm = "unsalted_sha1" - else: - algorithm = encoded.split("$", 1)[0] - return get_hasher(algorithm) - - -def mask_hash(hash, show=6, char="*"): - """ - Return the given hash, with only the first ``show`` number shown. The - rest are masked with ``char`` for security reasons. - """ - masked = hash[:show] - masked += char * len(hash[show:]) - return masked - - -def must_update_salt(salt, expected_entropy): - # Each character in the salt provides log_2(len(alphabet)) bits of entropy. - return len(salt) * math.log2(len(RANDOM_STRING_CHARS)) < expected_entropy - - -class BasePasswordHasher: - """ - Abstract base class for password hashers - - When creating your own hasher, you need to override algorithm, - verify(), encode() and safe_summary(). - - PasswordHasher objects are immutable. - """ - - algorithm = None - library = None - salt_entropy = 128 - - def _load_library(self): - if self.library is not None: - if isinstance(self.library, (tuple, list)): - name, mod_path = self.library - else: - mod_path = self.library - try: - module = importlib.import_module(mod_path) - except ImportError as e: - raise ValueError( - "Couldn't load %r algorithm library: %s" - % (self.__class__.__name__, e) - ) - return module - raise ValueError( - "Hasher %r doesn't specify a library attribute" % self.__class__.__name__ - ) - - def salt(self): - """ - Generate a cryptographically secure nonce salt in ASCII with an entropy - of at least `salt_entropy` bits. - """ - # Each character in the salt provides - # log_2(len(alphabet)) bits of entropy. - char_count = math.ceil(self.salt_entropy / math.log2(len(RANDOM_STRING_CHARS))) - return get_random_string(char_count, allowed_chars=RANDOM_STRING_CHARS) - - def verify(self, password, encoded): - """Check if the given password is correct.""" - raise NotImplementedError( - "subclasses of BasePasswordHasher must provide a verify() method" - ) - - def encode(self, password, salt): - """ - Create an encoded database value. - - The result is normally formatted as "algorithm$salt$hash" and - must be fewer than 128 characters. - """ - raise NotImplementedError( - "subclasses of BasePasswordHasher must provide an encode() method" - ) - - def decode(self, encoded): - """ - Return a decoded database value. - - The result is a dictionary and should contain `algorithm`, `hash`, and - `salt`. Extra keys can be algorithm specific like `iterations` or - `work_factor`. - """ - raise NotImplementedError( - "subclasses of BasePasswordHasher must provide a decode() method." - ) - - def safe_summary(self, encoded): - """ - Return a summary of safe values. - - The result is a dictionary and will be used where the password field - must be displayed to construct a safe representation of the password. - """ - raise NotImplementedError( - "subclasses of BasePasswordHasher must provide a safe_summary() method" - ) - - def must_update(self, encoded): - return False - - def harden_runtime(self, password, encoded): - """ - Bridge the runtime gap between the work factor supplied in `encoded` - and the work factor suggested by this hasher. - - Taking PBKDF2 as an example, if `encoded` contains 20000 iterations and - `self.iterations` is 30000, this method should run password through - another 10000 iterations of PBKDF2. Similar approaches should exist - for any hasher that has a work factor. If not, this method should be - defined as a no-op to silence the warning. - """ - warnings.warn( - "subclasses of BasePasswordHasher should provide a harden_runtime() method" - ) - - -class PBKDF2PasswordHasher(BasePasswordHasher): - """ - Secure password hashing using the PBKDF2 algorithm (recommended) - - Configured to use PBKDF2 + HMAC + SHA256. - The result is a 64 byte binary string. Iterations may be changed - safely but you must rename the algorithm if you change SHA256. - """ - - algorithm = "pbkdf2_sha256" - iterations = 320000 - digest = hashlib.sha256 - - def encode(self, password, salt, iterations=None): - assert password is not None - assert salt and "$" not in salt - iterations = iterations or self.iterations - hash = pbkdf2(password, salt, iterations, digest=self.digest) - hash = base64.b64encode(hash).decode("ascii").strip() - return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash) - - def decode(self, encoded): - algorithm, iterations, salt, hash = encoded.split("$", 3) - assert algorithm == self.algorithm - return { - "algorithm": algorithm, - "hash": hash, - "iterations": int(iterations), - "salt": salt, - } - - def verify(self, password, encoded): - decoded = self.decode(encoded) - encoded_2 = self.encode(password, decoded["salt"], decoded["iterations"]) - return constant_time_compare(encoded, encoded_2) - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return { - "algorithm": decoded["algorithm"], - "iterations": decoded["iterations"], - "salt": mask_hash(decoded["salt"]), - "hash": mask_hash(decoded["hash"]), - } - - def must_update(self, encoded): - decoded = self.decode(encoded) - update_salt = must_update_salt(decoded["salt"], self.salt_entropy) - return (decoded["iterations"] != self.iterations) or update_salt - - def harden_runtime(self, password, encoded): - decoded = self.decode(encoded) - extra_iterations = self.iterations - decoded["iterations"] - if extra_iterations > 0: - self.encode(password, decoded["salt"], extra_iterations) - - -class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher): - """ - Alternate PBKDF2 hasher which uses SHA1, the default PRF - recommended by PKCS #5. This is compatible with other - implementations of PBKDF2, such as openssl's - PKCS5_PBKDF2_HMAC_SHA1(). - """ - - algorithm = "pbkdf2_sha1" - digest = hashlib.sha1 - - -class Argon2PasswordHasher(BasePasswordHasher): - """ - Secure password hashing using the argon2 algorithm. - - This is the winner of the Password Hashing Competition 2013-2015 - (https://password-hashing.net). It requires the argon2-cffi library which - depends on native C code and might cause portability issues. - """ - - algorithm = "argon2" - library = "argon2" - - time_cost = 2 - memory_cost = 102400 - parallelism = 8 - - def encode(self, password, salt): - argon2 = self._load_library() - params = self.params() - data = argon2.low_level.hash_secret( - password.encode(), - salt.encode(), - time_cost=params.time_cost, - memory_cost=params.memory_cost, - parallelism=params.parallelism, - hash_len=params.hash_len, - type=params.type, - ) - return self.algorithm + data.decode("ascii") - - def decode(self, encoded): - argon2 = self._load_library() - algorithm, rest = encoded.split("$", 1) - assert algorithm == self.algorithm - params = argon2.extract_parameters("$" + rest) - variety, *_, b64salt, hash = rest.split("$") - # Add padding. - b64salt += "=" * (-len(b64salt) % 4) - salt = base64.b64decode(b64salt).decode("latin1") - return { - "algorithm": algorithm, - "hash": hash, - "memory_cost": params.memory_cost, - "parallelism": params.parallelism, - "salt": salt, - "time_cost": params.time_cost, - "variety": variety, - "version": params.version, - "params": params, - } - - def verify(self, password, encoded): - argon2 = self._load_library() - algorithm, rest = encoded.split("$", 1) - assert algorithm == self.algorithm - try: - return argon2.PasswordHasher().verify("$" + rest, password) - except argon2.exceptions.VerificationError: - return False - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return { - "algorithm": decoded["algorithm"], - "variety": decoded["variety"], - "version": decoded["version"], - "memory cost": decoded["memory_cost"], - "time cost": decoded["time_cost"], - "parallelism": decoded["parallelism"], - "salt": mask_hash(decoded["salt"]), - "hash": mask_hash(decoded["hash"]), - } - - def must_update(self, encoded): - decoded = self.decode(encoded) - current_params = decoded["params"] - new_params = self.params() - # Set salt_len to the salt_len of the current parameters because salt - # is explicitly passed to argon2. - new_params.salt_len = current_params.salt_len - update_salt = must_update_salt(decoded["salt"], self.salt_entropy) - return (current_params != new_params) or update_salt - - def harden_runtime(self, password, encoded): - # The runtime for Argon2 is too complicated to implement a sensible - # hardening algorithm. - pass - - def params(self): - argon2 = self._load_library() - # salt_len is a noop, because we provide our own salt. - return argon2.Parameters( - type=argon2.low_level.Type.ID, - version=argon2.low_level.ARGON2_VERSION, - salt_len=argon2.DEFAULT_RANDOM_SALT_LENGTH, - hash_len=argon2.DEFAULT_HASH_LENGTH, - time_cost=self.time_cost, - memory_cost=self.memory_cost, - parallelism=self.parallelism, - ) - - -class BCryptSHA256PasswordHasher(BasePasswordHasher): - """ - Secure password hashing using the bcrypt algorithm (recommended) - - This is considered by many to be the most secure algorithm but you - must first install the bcrypt library. Please be warned that - this library depends on native C code and might cause portability - issues. - """ - - algorithm = "bcrypt_sha256" - digest = hashlib.sha256 - library = ("bcrypt", "bcrypt") - rounds = 12 - - def salt(self): - bcrypt = self._load_library() - return bcrypt.gensalt(self.rounds) - - def encode(self, password, salt): - bcrypt = self._load_library() - password = password.encode() - # Hash the password prior to using bcrypt to prevent password - # truncation as described in #20138. - if self.digest is not None: - # Use binascii.hexlify() because a hex encoded bytestring is str. - password = binascii.hexlify(self.digest(password).digest()) - - data = bcrypt.hashpw(password, salt) - return "%s$%s" % (self.algorithm, data.decode("ascii")) - - def decode(self, encoded): - algorithm, empty, algostr, work_factor, data = encoded.split("$", 4) - assert algorithm == self.algorithm - return { - "algorithm": algorithm, - "algostr": algostr, - "checksum": data[22:], - "salt": data[:22], - "work_factor": int(work_factor), - } - - def verify(self, password, encoded): - algorithm, data = encoded.split("$", 1) - assert algorithm == self.algorithm - encoded_2 = self.encode(password, data.encode("ascii")) - return constant_time_compare(encoded, encoded_2) - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return { - "algorithm": decoded["algorithm"], - "work factor": decoded["work_factor"], - "salt": mask_hash(decoded["salt"]), - "checksum": mask_hash(decoded["checksum"]), - } - - def must_update(self, encoded): - decoded = self.decode(encoded) - return decoded["work_factor"] != self.rounds - - def harden_runtime(self, password, encoded): - _, data = encoded.split("$", 1) - salt = data[:29] # Length of the salt in bcrypt. - rounds = data.split("$")[2] - # work factor is logarithmic, adding one doubles the load. - diff = 2 ** (self.rounds - int(rounds)) - 1 - while diff > 0: - self.encode(password, salt.encode("ascii")) - diff -= 1 - - -class BCryptPasswordHasher(BCryptSHA256PasswordHasher): - """ - Secure password hashing using the bcrypt algorithm - - This is considered by many to be the most secure algorithm but you - must first install the bcrypt library. Please be warned that - this library depends on native C code and might cause portability - issues. - - This hasher does not first hash the password which means it is subject to - bcrypt's 72 bytes password truncation. Most use cases should prefer the - BCryptSHA256PasswordHasher. - """ - - algorithm = "bcrypt" - digest = None - - -class SHA1PasswordHasher(BasePasswordHasher): - """ - The SHA1 password hashing algorithm (not recommended) - """ - - algorithm = "sha1" - - def encode(self, password, salt): - assert password is not None - assert salt and "$" not in salt - hash = hashlib.sha1((salt + password).encode()).hexdigest() - return "%s$%s$%s" % (self.algorithm, salt, hash) - - def decode(self, encoded): - algorithm, salt, hash = encoded.split("$", 2) - assert algorithm == self.algorithm - return {"algorithm": algorithm, "hash": hash, "salt": salt} - - def verify(self, password, encoded): - decoded = self.decode(encoded) - encoded_2 = self.encode(password, decoded["salt"]) - return constant_time_compare(encoded, encoded_2) - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return { - "algorithm": decoded["algorithm"], - "salt": mask_hash(decoded["salt"], show=2), - "hash": mask_hash(decoded["hash"]), - } - - def must_update(self, encoded): - decoded = self.decode(encoded) - return must_update_salt(decoded["salt"], self.salt_entropy) - - def harden_runtime(self, password, encoded): - pass - - -class MD5PasswordHasher(BasePasswordHasher): - """ - The Salted MD5 password hashing algorithm (not recommended) - """ - - algorithm = "md5" - - def encode(self, password, salt): - assert password is not None - assert salt and "$" not in salt - hash = hashlib.md5((salt + password).encode()).hexdigest() - return "%s$%s$%s" % (self.algorithm, salt, hash) - - def decode(self, encoded): - algorithm, salt, hash = encoded.split("$", 2) - assert algorithm == self.algorithm - return {"algorithm": algorithm, "hash": hash, "salt": salt} - - def verify(self, password, encoded): - decoded = self.decode(encoded) - encoded_2 = self.encode(password, decoded["salt"]) - return constant_time_compare(encoded, encoded_2) - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return { - "algorithm": decoded["algorithm"], - "salt": mask_hash(decoded["salt"], show=2), - "hash": mask_hash(decoded["hash"]), - } - - def must_update(self, encoded): - decoded = self.decode(encoded) - return must_update_salt(decoded["salt"], self.salt_entropy) - - def harden_runtime(self, password, encoded): - pass - - -class UnsaltedSHA1PasswordHasher(BasePasswordHasher): - """ - Very insecure algorithm that you should *never* use; store SHA1 hashes - with an empty salt. - - This class is implemented because Django used to accept such password - hashes. Some older Django installs still have these values lingering - around so we need to handle and upgrade them properly. - """ - - algorithm = "unsalted_sha1" - - def salt(self): - return "" - - def encode(self, password, salt): - assert salt == "" - hash = hashlib.sha1(password.encode()).hexdigest() - return "sha1$$%s" % hash - - def decode(self, encoded): - assert encoded.startswith("sha1$$") - return {"algorithm": self.algorithm, "hash": encoded[6:], "salt": None} - - def verify(self, password, encoded): - encoded_2 = self.encode(password, "") - return constant_time_compare(encoded, encoded_2) - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return {"algorithm": decoded["algorithm"], "hash": mask_hash(decoded["hash"])} - - def harden_runtime(self, password, encoded): - pass - - -class UnsaltedMD5PasswordHasher(BasePasswordHasher): - """ - Incredibly insecure algorithm that you should *never* use; stores unsalted - MD5 hashes without the algorithm prefix, also accepts MD5 hashes with an - empty salt. - - This class is implemented because Django used to store passwords this way - and to accept such password hashes. Some older Django installs still have - these values lingering around so we need to handle and upgrade them - properly. - """ - - algorithm = "unsalted_md5" - - def salt(self): - return "" - - def encode(self, password, salt): - assert salt == "" - return hashlib.md5(password.encode()).hexdigest() - - def decode(self, encoded): - return {"algorithm": self.algorithm, "hash": encoded, "salt": None} - - def verify(self, password, encoded): - if len(encoded) == 37 and encoded.startswith("md5$$"): - encoded = encoded[5:] - encoded_2 = self.encode(password, "") - return constant_time_compare(encoded, encoded_2) - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return { - "algorithm": decoded["algorithm"], - "hash": mask_hash(decoded["hash"], show=3), - } - - def harden_runtime(self, password, encoded): - pass - - -class CryptPasswordHasher(BasePasswordHasher): - """ - Password hashing using UNIX crypt (not recommended) - - The crypt module is not supported on all platforms. - """ - - algorithm = "crypt" - library = "crypt" - - def salt(self): - return get_random_string(2) - - def encode(self, password, salt): - crypt = self._load_library() - assert len(salt) == 2 - hash = crypt.crypt(password, salt) - assert hash is not None # A platform like OpenBSD with a dummy crypt module. - # we don't need to store the salt, but Django used to do this - return "%s$%s$%s" % (self.algorithm, "", hash) - - def decode(self, encoded): - algorithm, salt, hash = encoded.split("$", 2) - assert algorithm == self.algorithm - return {"algorithm": algorithm, "hash": hash, "salt": salt} - - def verify(self, password, encoded): - crypt = self._load_library() - decoded = self.decode(encoded) - data = crypt.crypt(password, decoded["hash"]) - return constant_time_compare(decoded["hash"], data) - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return { - "algorithm": decoded["algorithm"], - "salt": decoded["salt"], - "hash": mask_hash(decoded["hash"], show=3), - } - - def harden_runtime(self, password, encoded): - pass diff --git a/users-backend/users/starlette_password/imports.py b/users-backend/users/starlette_password/imports.py deleted file mode 100644 index 07d700310c..0000000000 --- a/users-backend/users/starlette_password/imports.py +++ /dev/null @@ -1,22 +0,0 @@ -from importlib import import_module - - -def import_string(dotted_path): - """ - Import a dotted module path and return the attribute/class designated by the - last name in the path. Raise ImportError if the import failed. - """ - try: - module_path, class_name = dotted_path.rsplit(".", 1) - except ValueError as err: - raise ImportError("%s doesn't look like a module path" % dotted_path) from err - - module = import_module(module_path) - - try: - return getattr(module, class_name) - except AttributeError as err: - raise ImportError( - 'Module "%s" does not define a "%s" attribute/class' - % (module_path, class_name) - ) from err diff --git a/users-backend/users/starlette_password/plain_hasher.py b/users-backend/users/starlette_password/plain_hasher.py deleted file mode 100644 index 4737b4ccf7..0000000000 --- a/users-backend/users/starlette_password/plain_hasher.py +++ /dev/null @@ -1,37 +0,0 @@ -from users.starlette_password.hashers import BasePasswordHasher, constant_time_compare - - -class PlainPasswordHasher(BasePasswordHasher): - """ - Used to have fast unit tests! - - Not for production use! - """ - - algorithm = "plain" - - def salt(self): - return "" - - def encode(self, password, salt): - return "%s$%s$%s" % (self.algorithm, "", password) - - def decode(self, encoded): - algorithm, salt, password = encoded.split("$", 2) - assert algorithm == self.algorithm - return {"algorithm": algorithm, "password": password, "salt": salt} - - def verify(self, password, encoded): - decoded = self.decode(encoded) - return constant_time_compare(decoded["password"], password) - - def safe_summary(self, encoded): - decoded = self.decode(encoded) - return { - "algorithm": decoded["algorithm"], - "salt": decoded["salt"], - "password": decoded["password"], - } - - def harden_runtime(self, password, encoded): - pass diff --git a/users-backend/users/tests/__init__.py b/users-backend/users/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/users-backend/users/tests/api.py b/users-backend/users/tests/api.py deleted file mode 100644 index 8c394cbc76..0000000000 --- a/users-backend/users/tests/api.py +++ /dev/null @@ -1,33 +0,0 @@ -from asgi_lifespan import LifespanManager -from httpx import AsyncClient -from main import app -from pythonit_toolkit.api.graphql_test_client import GraphQLClient -from users.settings import PASTAPORTO_SECRET, SERVICE_TO_SERVICE_SECRET -from ward import Scope, fixture - - -@fixture(scope=Scope.Global) -async def client(): - async with AsyncClient(app=app, base_url="http://testserver") as async_client: - yield async_client - - -@fixture -async def testclient(client=client): - async with LifespanManager(app): - yield client - - -@fixture() -async def graphql_client(testclient=testclient): - yield GraphQLClient(testclient, pastaporto_secret=PASTAPORTO_SECRET) - - -@fixture() -async def internalapi_graphql_client(testclient=testclient): - yield GraphQLClient( - testclient, - internal_api_endpoint=True, - pastaporto_secret=PASTAPORTO_SECRET, - service_to_service_secret=SERVICE_TO_SERVICE_SECRET, - ) diff --git a/users-backend/users/tests/factories.py b/users-backend/users/tests/factories.py deleted file mode 100644 index 10dd66651a..0000000000 --- a/users-backend/users/tests/factories.py +++ /dev/null @@ -1,64 +0,0 @@ -import dataclasses -import datetime -from datetime import timezone - -import factory -from factory.alchemy import SQLAlchemyModelFactory -from users.domain.entities import User -from users.starlette_password.hashers import make_password -from ward import fixture - -from .session import test_session - - -class UserFactory(SQLAlchemyModelFactory): - class Meta: - model = User - sqlalchemy_session = test_session - - username = "user" - email = "user@pycon.it" - fullname = "First Last" - name = "First" - gender = "male" - is_active = True - is_staff = False - is_superuser = False - date_birth = None - open_to_recruiting = False - open_to_newsletter = False - country = "US" - date_joined = datetime.datetime.now(timezone.utc) - - @factory.post_generation - def password(obj, create, extracted, **kwargs): - if not create: - return - - password = extracted or "user" - obj.hashed_password = make_password(password) - return None - - -@fixture -async def user_factory(): - async def func(**kwargs): - obj = UserFactory.create(**kwargs) - await UserFactory._meta.sqlalchemy_session.flush() - # all of this is to avoid `return obj` triggering a query to the DB - # without await. The whole async DB is messy - obj = dataclasses.replace(obj, password=kwargs.get("password", None)) - await UserFactory._meta.sqlalchemy_session.commit() - return obj - - return func - - -@fixture -async def user_factory_batch(): - async def func(size, **kwargs): - obj = UserFactory.create_batch(size, **kwargs) - await UserFactory._meta.sqlalchemy_session.flush() - return obj - - return func diff --git a/users-backend/users/tests/session.py b/users-backend/users/tests/session.py deleted file mode 100644 index 6f9216966d..0000000000 --- a/users-backend/users/tests/session.py +++ /dev/null @@ -1,30 +0,0 @@ -import logging -from unittest.mock import patch - -from ward import fixture - -from users.db import get_engine, get_session -from users.domain.entities import mapper_registry - -logger = logging.getLogger(__name__) -engine = get_engine(echo=False) -test_session = get_session(engine) - - -@fixture -async def db(): - with patch("main.get_session", return_value=test_session): - yield test_session - - metadata = mapper_registry.metadata - - async with engine.begin() as connection: - for table in metadata.sorted_tables: - await connection.execute(table.delete()) - - logger.debug("rolling back after unit-test done") - - -@fixture -async def second_session(): - return get_session(engine) From 3bf07b7073ddd53d2785eb14f981c2c3a93c6e81 Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sat, 25 Nov 2023 21:15:49 +0100 Subject: [PATCH 5/9] Update deploy workflow --- .github/workflows/deploy.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7594188159..bc8fc7febb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -68,16 +68,10 @@ jobs: fail-fast: false matrix: service: - - name: gateway - dir: gateway - - name: association-backend - dir: association-backend - name: pycon-backend dir: backend - name: pretix dir: pretix - - name: cms - dir: cms steps: - uses: actions/checkout@v2 From 3cd35040faa76c7d388509561afe53edcf655312 Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sat, 25 Nov 2023 21:16:41 +0100 Subject: [PATCH 6/9] Cleanup deploy --- .github/workflows/deploy.yml | 59 ------------------------------------ 1 file changed, 59 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bc8fc7febb..8f4865ee8c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -272,62 +272,3 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} - - migrate-association-backend-db: - runs-on: ubuntu-latest - needs: [terraform, wait-aws-update] - steps: - - uses: actions/checkout@v2 - with: - ref: ${{ github.ref }} - - name: Migrate Association Backend DB - run: | - aws lambda invoke --function-name ${{ env.TF_WORKSPACE }}-association-backend --cli-binary-format raw-in-base64-out --payload '{ "_cli_command": { "action": "migrate" } }' response.json - cat response.json - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} - - # Upload new schemas to Apollo Studio - update-default-apollo-studio-graph: - runs-on: ubuntu-latest - needs: [terraform, wait-aws-update] - environment: - name: ${{ fromJSON('["pastaporto", "production"]')[github.ref == 'refs/heads/main'] }} - - strategy: - fail-fast: false - matrix: - service: - - name: association-backend - url: https://${{ fromJSON('["pastaporto-", ""]')[github.ref == 'refs/heads/main'] }}association-api.python.it/graphql - - name: pycon-backend - url: https://${{ fromJSON('["pastaporto-", ""]')[github.ref == 'refs/heads/main'] }}admin.pycon.it/graphql - - name: cms - url: https://${{ fromJSON('["staging-", ""]')[github.ref == 'refs/heads/main'] }}cms.python.it/graphql/ - - steps: - - uses: actions/checkout@v2 - with: - ref: ${{ github.ref }} - - name: Cache Apollo Rover - uses: actions/cache@v1 - id: cache-rover - with: - path: ~/.rover/bin - key: rover-cache-${{ env.APOLLO_ROVER_VERSION }} - - name: Install Apollo Rover - if: steps.cache-rover.outputs.cache-hit != 'true' - run: curl -sSL https://rover.apollo.dev/nix/v$APOLLO_ROVER_VERSION | sh - - name: Append Apollo Rover to path - run: echo "$HOME/.rover/bin" >> $GITHUB_PATH - - name: Publish schema - uses: ./.github/actions/publish-graph-schema - with: - service-name: ${{ matrix.service.name }} - service-graphql-url: ${{ matrix.service.url }} - apollo-key: ${{ secrets.DEFAULT_APOLLO_KEY }} - graph: default-python-italia - variant: ${{ env.TF_WORKSPACE }} From 267bda9c90c26b2868389b6628ae05ea2616a8a3 Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sun, 26 Nov 2023 02:23:35 +0100 Subject: [PATCH 7/9] workflows renames --- ...-backend-checks.yml => backend-checks.yml} | 0 ...ycon-backend-test.yml => backend-test.yml} | 0 .github/workflows/python-lint.yml | 60 ------------------- .github/workflows/rebase-pr.yml | 18 ------ 4 files changed, 78 deletions(-) rename .github/workflows/{pycon-backend-checks.yml => backend-checks.yml} (100%) rename .github/workflows/{pycon-backend-test.yml => backend-test.yml} (100%) delete mode 100644 .github/workflows/python-lint.yml delete mode 100644 .github/workflows/rebase-pr.yml diff --git a/.github/workflows/pycon-backend-checks.yml b/.github/workflows/backend-checks.yml similarity index 100% rename from .github/workflows/pycon-backend-checks.yml rename to .github/workflows/backend-checks.yml diff --git a/.github/workflows/pycon-backend-test.yml b/.github/workflows/backend-test.yml similarity index 100% rename from .github/workflows/pycon-backend-test.yml rename to .github/workflows/backend-test.yml diff --git a/.github/workflows/python-lint.yml b/.github/workflows/python-lint.yml deleted file mode 100644 index aa6e7076fb..0000000000 --- a/.github/workflows/python-lint.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Python Lint - -on: pull_request - -jobs: - tests: - runs-on: ubuntu-latest - strategy: - matrix: - service: - - name: users-backend - driver: postgresql+asyncpg - - name: association-backend - driver: postgresql - defaults: - run: - working-directory: ./${{ matrix.service.name }} - services: - postgres: - image: postgres:14.5 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres - ports: - - 5432/tcp - options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: "3.11.6" - - run: pip install poetry - if: steps.changed.outputs.changed.${{ matrix.service.name }} == 'true' - - name: Set Poetry config - if: steps.changed.outputs.changed.${{ matrix.service.name }} == 'true' - run: poetry config virtualenvs.path ~/.virtualenvs - - name: Cache Poetry - if: steps.changed.outputs.changed.${{ matrix.service.name }} == 'true' - uses: actions/cache@v1 - id: cache - with: - path: ~/.virtualenvs - key: poetry-${{ matrix.service.name }}-${{ hashFiles('**/poetry.lock') }}-v2 - - name: Install deps - if: steps.changed.outputs.changed.${{ matrix.service.name }} == 'true' - run: poetry install - - name: Unit tests - if: steps.changed.outputs.changed.${{ matrix.service.name }} == 'true' - run: poetry run task test - env: - DATABASE_URL: ${{ matrix.service.driver }}://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres - IDENTITY_SECRET: 'secret' - PASTAPORTO_SECRET: 'secret' - SERVICE_TO_SERVICE_SECRET: 'secret' - SECRET_KEY: 'secret' - STRIPE_SECRET_API_KEY: 'secret_stripe_key' - STRIPE_SUBSCRIPTION_PRICE_ID: 'secret_price_id' - STRIPE_WEBHOOK_SIGNATURE_SECRET: 'secret_webhook' - ASSOCIATION_FRONTEND_URL: "https://associazione.python.it" diff --git a/.github/workflows/rebase-pr.yml b/.github/workflows/rebase-pr.yml deleted file mode 100644 index a587912fec..0000000000 --- a/.github/workflows/rebase-pr.yml +++ /dev/null @@ -1,18 +0,0 @@ -on: - issue_comment: - types: [created] - -name: Automatic Rebase -jobs: - rebase: - name: Rebase - if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Automatic Rebase - uses: cirrus-actions/rebase@1.4 - env: - GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} From b27eab165f2d1f9f3a39705087a34b5b1f10163d Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sun, 26 Nov 2023 02:27:35 +0100 Subject: [PATCH 8/9] more cleanup --- .../actions/publish-graph-schema/action.yml | 34 ------------------- .github/labeler.yml | 11 +----- .github/workflows/deploy.yml | 5 ++- 3 files changed, 3 insertions(+), 47 deletions(-) delete mode 100644 .github/actions/publish-graph-schema/action.yml diff --git a/.github/actions/publish-graph-schema/action.yml b/.github/actions/publish-graph-schema/action.yml deleted file mode 100644 index 19422dd06d..0000000000 --- a/.github/actions/publish-graph-schema/action.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: 'Generate and publish schema to Apollo' -inputs: - service-name: - description: 'Name of the service' - required: true - service-graphql-url: - description: 'Service GraphQL URL' - required: true - apollo-key: - description: 'Apollo Key' - required: true - graph: - description: 'What graph to use' - required: true - default: 'default-python-italia' - variant: - description: 'Variant of the graph' - required: true - default: 'ghactions' - -runs: - using: "composite" - steps: - - name: Download & Publish service ${{ inputs.service-name }} to Apollo Studio - shell: bash - env: - APOLLO_KEY: ${{ inputs.apollo-key }} - run: | - rover subgraph introspect ${{ inputs.service-graphql-url }} > ${{ inputs.service-name }}-schema.graphql - rover subgraph publish ${{ inputs.graph }}@${{ inputs.variant }} \ - --schema ${{ inputs.service-name }}-schema.graphql \ - --name ${{ inputs.service-name }} \ - --convert \ - --routing-url ${{ inputs.service-graphql-url }} diff --git a/.github/labeler.yml b/.github/labeler.yml index 5bb62ca2e5..cf93422d76 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,21 +1,12 @@ pycon-frontend: - frontend/* -pycon-backend: +backend: - backend/* -association-backend: -- association-backend/* - association-frontend: - association-frontend/* -users-backend: -- users-backend/* - -gateway: -- gateway/* - infrastructure: - infrastructure/* diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8f4865ee8c..84494efbb3 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,14 +10,13 @@ on: inputs: comment-id: description: 'The comment-id of the slash command' - required: true + required: false event-number: description: 'The event-id of the slash command' - required: true + required: false env: TF_WORKSPACE: ${{ fromJSON('["pastaporto", "production"]')[github.ref == 'refs/heads/main'] }} - APOLLO_ROVER_VERSION: 0.7.0 jobs: create-db: From db3b739f3fd4df880f4965613b639d74cb293bae Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sun, 26 Nov 2023 02:47:44 +0100 Subject: [PATCH 9/9] delete aws resources --- infrastructure/applications/applications.tf | 21 ----- .../applications/association_backend/main.tf | 92 ------------------- .../association_backend/secrets.tf | 8 -- .../association_backend/variables.tf | 6 -- infrastructure/applications/gateway/main.tf | 54 ----------- .../applications/gateway/providers.tf | 9 -- .../applications/gateway/secrets.tf | 8 -- .../applications/gateway/variables.tf | 4 - .../applications/users_backend/main.tf | 90 ------------------ .../applications/users_backend/secrets.tf | 8 -- .../applications/users_backend/variables.tf | 6 -- 11 files changed, 306 deletions(-) delete mode 100644 infrastructure/applications/association_backend/main.tf delete mode 100644 infrastructure/applications/association_backend/secrets.tf delete mode 100644 infrastructure/applications/association_backend/variables.tf delete mode 100644 infrastructure/applications/gateway/main.tf delete mode 100644 infrastructure/applications/gateway/providers.tf delete mode 100644 infrastructure/applications/gateway/secrets.tf delete mode 100644 infrastructure/applications/gateway/variables.tf delete mode 100644 infrastructure/applications/users_backend/main.tf delete mode 100644 infrastructure/applications/users_backend/secrets.tf delete mode 100644 infrastructure/applications/users_backend/variables.tf diff --git a/infrastructure/applications/applications.tf b/infrastructure/applications/applications.tf index 0b5f300f8d..1fc0027b12 100644 --- a/infrastructure/applications/applications.tf +++ b/infrastructure/applications/applications.tf @@ -30,27 +30,6 @@ module "pycon_backend" { } } -module "gateway" { - source = "./gateway" - - providers = { - aws = aws - aws.us = aws.us - } -} - -module "users_backend" { - source = "./users_backend" - depends_on = [module.database] - enable_proxy = local.enable_proxy -} - -module "association_backend" { - source = "./association_backend" - depends_on = [module.database] - enable_proxy = local.enable_proxy -} - module "email_templates" { source = "./email_templates" } diff --git a/infrastructure/applications/association_backend/main.tf b/infrastructure/applications/association_backend/main.tf deleted file mode 100644 index fbbc43ef2d..0000000000 --- a/infrastructure/applications/association_backend/main.tf +++ /dev/null @@ -1,92 +0,0 @@ -locals { - is_prod = terraform.workspace == "production" - domain = local.is_prod ? "${local.domain_name}.python.it" : "${terraform.workspace}-${local.domain_name}.python.it" - - # TODO: Need to coordinate between env and vercel - association_frontend_url = "https://associazione.python.it" - users_backend_url = local.is_prod ? "https://users-api.python.it" : "https://${terraform.workspace}-users-api.python.it" - - db_connection = var.enable_proxy ? "postgresql://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_proxy.proxy[0].endpoint}:${data.aws_db_instance.database.port}/association" : "postgresql://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_instance.database.address}:${data.aws_db_instance.database.port}/association" -} - -data "aws_db_instance" "database" { - db_instance_identifier = "pythonit-${terraform.workspace}" -} - -data "aws_iam_role" "lambda" { - name = "pythonit-lambda-role" -} - -data "aws_vpc" "default" { - filter { - name = "tag:Name" - values = ["pythonit-vpc"] - } -} - -data "aws_subnet_ids" "private" { - vpc_id = data.aws_vpc.default.id - - tags = { - Type = "private" - } -} - -data "aws_security_group" "lambda" { - name = "pythonit-lambda-security-group" -} - -data "aws_security_group" "rds" { - name = "pythonit-rds-security-group" -} - -data "aws_db_proxy" "proxy" { - count = var.enable_proxy ? 1 : 0 - name = "pythonit-${terraform.workspace}-database-proxy" -} - -module "lambda" { - source = "../../components/application_lambda" - - application = local.application - role_arn = data.aws_iam_role.lambda.arn - subnet_ids = [for subnet in data.aws_subnet_ids.private.ids : subnet] - security_group_ids = [data.aws_security_group.rds.id, data.aws_security_group.lambda.id] - env_vars = { - DEBUG = "false" - DATABASE_URL = local.db_connection - SENTRY_DSN = module.secrets.value.sentry_dsn - - # Services - ASSOCIATION_FRONTEND_URL = local.association_frontend_url - USERS_SERVICE = local.users_backend_url - - PRETIX_API = "https://tickets.pycon.it/api/v1/" - PRETIX_API_TOKEN = module.common_secrets.value.pretix_api_token - - # Secrets - STRIPE_WEBHOOK_SIGNATURE_SECRET = module.secrets.value.stripe_webhook_secret - STRIPE_SUBSCRIPTION_PRICE_ID = module.secrets.value.stripe_membership_price_id - STRIPE_SECRET_API_KEY = module.secrets.value.stripe_secret_api_key - PASTAPORTO_SECRET = module.common_secrets.value.pastaporto_secret - SERVICE_TO_SERVICE_SECRET = module.common_secrets.value.service_to_service_secret - PRETIX_WEBHOOK_SECRET = module.secrets.value.pretix_webhook_secret - } -} - -data "aws_acm_certificate" "cert" { - domain = "*.python.it" - statuses = ["ISSUED"] -} - -module "api" { - source = "../../components/http_api_gateway" - - application = local.application - use_domain = true - domain = local.domain - zone_name = "python.it" - certificate_arn = data.aws_acm_certificate.cert.arn - lambda_invoke_arn = module.lambda.invoke_arn - lambda_function_name = module.lambda.function_name -} diff --git a/infrastructure/applications/association_backend/secrets.tf b/infrastructure/applications/association_backend/secrets.tf deleted file mode 100644 index b3bda1cd8e..0000000000 --- a/infrastructure/applications/association_backend/secrets.tf +++ /dev/null @@ -1,8 +0,0 @@ -module "secrets" { - source = "../../components/secrets" - service = "association-backend" -} - -module "common_secrets" { - source = "../../components/secrets" -} diff --git a/infrastructure/applications/association_backend/variables.tf b/infrastructure/applications/association_backend/variables.tf deleted file mode 100644 index 964c2128d4..0000000000 --- a/infrastructure/applications/association_backend/variables.tf +++ /dev/null @@ -1,6 +0,0 @@ -locals { - application = "association-backend" - domain_name = "association-api" -} - -variable "enable_proxy" {} diff --git a/infrastructure/applications/gateway/main.tf b/infrastructure/applications/gateway/main.tf deleted file mode 100644 index 646a20fee5..0000000000 --- a/infrastructure/applications/gateway/main.tf +++ /dev/null @@ -1,54 +0,0 @@ -locals { - is_prod = terraform.workspace == "production" - domain = local.is_prod ? "${local.domain_name}.python.it" : "${terraform.workspace}-${local.domain_name}.python.it" - users_service_url = local.is_prod ? "https://users-api.python.it" : "https://${terraform.workspace}-users-api.python.it" -} - -data "aws_iam_role" "lambda" { - name = "pythonit-lambda-role" -} - -module "lambda" { - source = "../../components/application_lambda" - - application = local.application - role_arn = data.aws_iam_role.lambda.arn - memory_size = 1024 - - env_vars = { - NODE_ENV = "production" - SENTRY_DSN = module.secrets.value.sentry_dsn - APOLLO_KEY = module.secrets.value.default_apollo_key - APOLLO_GRAPH_ID = "default-python-italia" - APOLLO_GRAPH_VARIANT = terraform.workspace - USERS_SERVICE = local.users_service_url - # Secrets - PASTAPORTO_SECRET = module.common_secrets.value.pastaporto_secret - IDENTITY_SECRET = module.common_secrets.value.identity_secret - SERVICE_TO_SERVICE_SECRET = module.common_secrets.value.service_to_service_secret - } -} - -module "api" { - source = "../../components/http_api_gateway" - - application = local.application - lambda_invoke_arn = module.lambda.invoke_arn - lambda_function_name = module.lambda.function_name -} - -data "aws_acm_certificate" "cert" { - domain = "*.python.it" - statuses = ["ISSUED"] - provider = aws.us -} - -module "distribution" { - source = "../../components/cloudfront" - - application = local.application - zone_name = "python.it" - domain = local.domain - certificate_arn = data.aws_acm_certificate.cert.arn - origin_url = module.api.cloudfront_friendly_endpoint -} diff --git a/infrastructure/applications/gateway/providers.tf b/infrastructure/applications/gateway/providers.tf deleted file mode 100644 index 5472a81ccf..0000000000 --- a/infrastructure/applications/gateway/providers.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "3.61.0" - configuration_aliases = [aws.us] - } - } -} diff --git a/infrastructure/applications/gateway/secrets.tf b/infrastructure/applications/gateway/secrets.tf deleted file mode 100644 index 6c90173bc5..0000000000 --- a/infrastructure/applications/gateway/secrets.tf +++ /dev/null @@ -1,8 +0,0 @@ -module "secrets" { - source = "../../components/secrets" - service = "gateway" -} - -module "common_secrets" { - source = "../../components/secrets" -} diff --git a/infrastructure/applications/gateway/variables.tf b/infrastructure/applications/gateway/variables.tf deleted file mode 100644 index 6ca68c212a..0000000000 --- a/infrastructure/applications/gateway/variables.tf +++ /dev/null @@ -1,4 +0,0 @@ -locals { - application = "gateway" - domain_name = "beri" -} diff --git a/infrastructure/applications/users_backend/main.tf b/infrastructure/applications/users_backend/main.tf deleted file mode 100644 index 9afb7be381..0000000000 --- a/infrastructure/applications/users_backend/main.tf +++ /dev/null @@ -1,90 +0,0 @@ -locals { - is_prod = terraform.workspace == "production" - domain = local.is_prod ? "${local.domain_name}.python.it" : "${terraform.workspace}-${local.domain_name}.python.it" - - # TODO: Need to coordinate between env and vercel - association_frontend_url = "https://associazione.python.it" - enable_proxy = false - - db_connection = local.enable_proxy ? "postgresql+asyncpg://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_proxy.proxy[0].endpoint}:${data.aws_db_instance.database.port}/users" : "postgresql+asyncpg://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_instance.database.address}:${data.aws_db_instance.database.port}/users" -} - -data "aws_db_instance" "database" { - db_instance_identifier = "pythonit-${terraform.workspace}" -} - -data "aws_iam_role" "lambda" { - name = "pythonit-lambda-role" -} - -data "aws_vpc" "default" { - filter { - name = "tag:Name" - values = ["pythonit-vpc"] - } -} - -data "aws_subnet_ids" "private" { - vpc_id = data.aws_vpc.default.id - - tags = { - Type = "private" - } -} - -data "aws_db_proxy" "proxy" { - count = var.enable_proxy ? 1 : 0 - name = "pythonit-${terraform.workspace}-database-proxy" -} - - -data "aws_security_group" "rds" { - name = "pythonit-rds-security-group" -} - -data "aws_security_group" "lambda" { - name = "pythonit-lambda-security-group" -} - -module "lambda" { - source = "../../components/application_lambda" - - application = local.application - role_arn = data.aws_iam_role.lambda.arn - subnet_ids = [for subnet in data.aws_subnet_ids.private.ids : subnet] - security_group_ids = [data.aws_security_group.rds.id, data.aws_security_group.lambda.id] - env_vars = { - DEBUG = "false" - SECRET_KEY = module.secrets.value.secret_key - GOOGLE_AUTH_CLIENT_ID = module.secrets.value.google_auth_client_id - GOOGLE_AUTH_CLIENT_SECRET = module.secrets.value.google_auth_client_secret - DATABASE_URL = local.db_connection - EMAIL_BACKEND = "pythonit_toolkit.emails.backends.ses.SESEmailBackend" - SENTRY_DSN = module.secrets.value.sentry_dsn - - # Services - ASSOCIATION_FRONTEND_URL = local.association_frontend_url - - # Secrets - PASTAPORTO_SECRET = module.common_secrets.value.pastaporto_secret - IDENTITY_SECRET = module.common_secrets.value.identity_secret - SERVICE_TO_SERVICE_SECRET = module.common_secrets.value.service_to_service_secret - } -} - -data "aws_acm_certificate" "cert" { - domain = "*.python.it" - statuses = ["ISSUED"] -} - -module "api" { - source = "../../components/http_api_gateway" - - application = local.application - use_domain = true - domain = local.domain - zone_name = "python.it" - certificate_arn = data.aws_acm_certificate.cert.arn - lambda_invoke_arn = module.lambda.invoke_arn - lambda_function_name = module.lambda.function_name -} diff --git a/infrastructure/applications/users_backend/secrets.tf b/infrastructure/applications/users_backend/secrets.tf deleted file mode 100644 index 835033ae53..0000000000 --- a/infrastructure/applications/users_backend/secrets.tf +++ /dev/null @@ -1,8 +0,0 @@ -module "secrets" { - source = "../../components/secrets" - service = "users-backend" -} - -module "common_secrets" { - source = "../../components/secrets" -} diff --git a/infrastructure/applications/users_backend/variables.tf b/infrastructure/applications/users_backend/variables.tf deleted file mode 100644 index 6121f2c7a8..0000000000 --- a/infrastructure/applications/users_backend/variables.tf +++ /dev/null @@ -1,6 +0,0 @@ -locals { - application = "users-backend" - domain_name = "users-api" -} - -variable "enable_proxy" {}