diff --git a/eox_tenant/api/v1/viewsets.py b/eox_tenant/api/v1/viewsets.py
index 235d08b6..e82ac7b2 100644
--- a/eox_tenant/api/v1/viewsets.py
+++ b/eox_tenant/api/v1/viewsets.py
@@ -3,6 +3,7 @@
"""
import logging
+from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from rest_framework import viewsets
from rest_framework.authentication import SessionAuthentication
from rest_framework.parsers import JSONParser
@@ -241,7 +242,7 @@ class MicrositeViewSet(AlternativeFieldLookupMixin, viewsets.ModelViewSet):
Response: No content status code 204
"""
- authentication_classes = (BearerAuthentication, SessionAuthentication)
+ authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication)
parser_classes = [JSONParser]
permission_classes = [EoxTenantAPIPermission]
serializer_class = MicrositeSerializer
@@ -439,7 +440,7 @@ class TenantConfigViewSet(AlternativeFieldLookupMixin, viewsets.ModelViewSet):
Response: No content status code 204
"""
- authentication_classes = (BearerAuthentication, SessionAuthentication)
+ authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication)
parser_classes = [JSONParser]
permission_classes = [EoxTenantAPIPermission]
serializer_class = TenantConfigSerializer
@@ -450,7 +451,7 @@ class TenantConfigViewSet(AlternativeFieldLookupMixin, viewsets.ModelViewSet):
class RouteViewSet(viewsets.ModelViewSet):
"""RouteViewSet that allows the basic API actions."""
- authentication_classes = (BearerAuthentication, SessionAuthentication)
+ authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication)
parser_classes = [JSONParser]
permission_classes = [EoxTenantAPIPermission]
serializer_class = RouteSerializer
diff --git a/eox_tenant/docs/resources/eox-tenant-test.postman_collection.json b/eox_tenant/docs/resources/eox-tenant-test.postman_collection.json
new file mode 100644
index 00000000..357e1676
--- /dev/null
+++ b/eox_tenant/docs/resources/eox-tenant-test.postman_collection.json
@@ -0,0 +1,520 @@
+{
+ "info": {
+ "_postman_id": "9f1933c5-e85b-438e-809f-151e8b160436",
+ "name": "eox-tenant API",
+ "description": "# Steps\n\n- You must create a new `access_token` using the `Authorization` endpoint. Make sure you have the correct credentials. To consume the `eox-tenant` API you should create a new application from `{domain}/admin/oauth2_provider/application/`\n- If you are using an environment within **Postman**, once you make the request the access token will be automatically added in the **Header** of all endpoints. The only thing you have to do is to use the variable access_token in the **Token** field of the **Current Token** section.\n \n- In case it doesn't work, copy the `access_token` from the response and paste it in the **Authorization** tab of this collection. In the **Current Token** section, add the access token manually, and modify the **Header Prefix** field depending on the type of token you are using.\n \n\n\n\n- Now, you can consume any endpoint without having to manually add the token on each of them.",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "_exporter_id": "27731142"
+ },
+ "item": [
+ {
+ "name": "Microsites API",
+ "item": [
+ {
+ "name": "Get MicroSites",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/microsites/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "microsites",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get MicroSite",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/microsites/1/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "microsites",
+ "1",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Create MicroSites",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"key\": \"key\",\n\t\"subdomain\": \"subdomain\",\n\t\"values\": {}\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/microsites/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "microsites",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete MicroSite",
+ "request": {
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/microsites/1/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "microsites",
+ "1",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Update MicroSite",
+ "request": {
+ "method": "PATCH",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"key\": \"key\",\n\t\"subdomain\": \"subdomain\",\n\t\"values\": {}\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/microsites/1/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "microsites",
+ "1",
+ ""
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Configs API",
+ "item": [
+ {
+ "name": "Get Configs",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/configs/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "configs",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Config",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/configs/1/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "configs",
+ "1",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Create Config",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"lms_configs\": {},\n\t\"studio_configs\": {},\n\t\"theming_configs\": {},\n\t\"meta\": \"meta\",\n\t\"external_key\": \"external-key\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/configs/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "configs",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete Config",
+ "request": {
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/configs/1/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "configs",
+ "1",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Update Config",
+ "request": {
+ "method": "PATCH",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"lms_configs\": {},\n\t\"studio_configs\": {},\n\t\"theming_configs\": {},\n\t\"meta\": \"meta\",\n\t\"external_key\": \"external-key\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/configs/1/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "configs",
+ "1",
+ ""
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Routes API",
+ "item": [
+ {
+ "name": "Get Routes",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/routes/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "routes",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Route",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/routes/1/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "routes",
+ "1",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Create Route",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"domain\": \"domain\",\n\t\"config\": 1\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/routes/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "routes",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete Route",
+ "request": {
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/routes/1/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "routes",
+ "1",
+ ""
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Update Route",
+ "request": {
+ "method": "PATCH",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"domain\": \"domain\",\n\t\"config\": 1\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{domain}}/eox-tenant/api/v1/routes/1/",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "eox-tenant",
+ "api",
+ "v1",
+ "routes",
+ "1",
+ ""
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Authorization",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "const responseJson = pm.response.json();",
+ "var bodyToken = responseJson.access_token;",
+ "pm.environment.set(\"access_token\", bodyToken)",
+ ""
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ },
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "urlencoded",
+ "urlencoded": [
+ {
+ "key": "grant_type",
+ "value": "client_credentials",
+ "description": "Can be \"client_credentails\" or \"password\"",
+ "type": "text"
+ },
+ {
+ "key": "token_type",
+ "value": "JWT",
+ "description": "Can be \"Bearer\" or \"JWT\"",
+ "type": "text"
+ },
+ {
+ "key": "client_id",
+ "value": "",
+ "description": "The client ID of the application",
+ "type": "text"
+ },
+ {
+ "key": "client_secret",
+ "value": "",
+ "description": "The client secret of the application",
+ "type": "text"
+ }
+ ]
+ },
+ "url": {
+ "raw": "{{domain}}/oauth2/access_token",
+ "host": [
+ "{{domain}}"
+ ],
+ "path": [
+ "oauth2",
+ "access_token"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "auth": {
+ "type": "oauth2",
+ "oauth2": [
+ {
+ "key": "headerPrefix",
+ "value": "JWT",
+ "type": "string"
+ },
+ {
+ "key": "addTokenTo",
+ "value": "header",
+ "type": "string"
+ }
+ ]
+ },
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "type": "text/javascript",
+ "packages": {},
+ "exec": [
+ ""
+ ]
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "type": "text/javascript",
+ "packages": {},
+ "exec": [
+ ""
+ ]
+ }
+ }
+ ],
+ "variable": [
+ {
+ "key": "domain",
+ "value": "http://local.edly.io:8000",
+ "type": "string"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/requirements/base.in b/requirements/base.in
index 5e06a789..955cdab5 100644
--- a/requirements/base.in
+++ b/requirements/base.in
@@ -10,3 +10,4 @@ jsonfield
edx-opaque-keys[django]
openedx_filters
social-auth-core
+edx-drf-extensions
diff --git a/requirements/base.txt b/requirements/base.txt
index 66e335b2..d4d1c911 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -4,76 +4,119 @@
#
# make upgrade
#
-asgiref==3.7.2
+asgiref==3.8.1
# via django
backports-zoneinfo==0.2.1
- # via django
+ # via
+ # django
+ # djangorestframework
certifi==2024.2.2
# via requests
cffi==1.16.0
- # via cryptography
+ # via
+ # cryptography
+ # pynacl
charset-normalizer==3.3.2
# via requests
-cryptography==42.0.5
- # via social-auth-core
+click==8.1.7
+ # via edx-django-utils
+cryptography==42.0.7
+ # via
+ # pyjwt
+ # social-auth-core
defusedxml==0.8.0rc2
# via
# python3-openid
# social-auth-core
-django==4.2.11
+django==4.2.13
# via
# -c requirements/constraints.txt
# -r requirements/base.in
# django-crum
# django-mysql
+ # django-waffle
# djangorestframework
+ # drf-jwt
+ # edx-django-utils
+ # edx-drf-extensions
# jsonfield
# openedx-filters
django-crum==0.7.9
+ # via
+ # -r requirements/base.in
+ # edx-django-utils
+django-mysql==4.13.0
# via -r requirements/base.in
-django-mysql==4.12.0
- # via -r requirements/base.in
-djangorestframework==3.14.0
- # via -r requirements/base.in
-edx-opaque-keys[django]==2.5.1
+django-waffle==4.1.0
+ # via
+ # edx-django-utils
+ # edx-drf-extensions
+djangorestframework==3.15.1
+ # via
+ # -r requirements/base.in
+ # drf-jwt
+ # edx-drf-extensions
+dnspython==2.6.1
+ # via pymongo
+drf-jwt==1.19.2
+ # via edx-drf-extensions
+edx-django-utils==5.14.1
+ # via edx-drf-extensions
+edx-drf-extensions==10.3.0
# via -r requirements/base.in
-idna==3.6
+edx-opaque-keys[django]==2.9.0
+ # via
+ # -r requirements/base.in
+ # edx-drf-extensions
+idna==3.7
# via requests
jsonfield==3.1.0
# via -r requirements/base.in
+newrelic==9.10.0
+ # via edx-django-utils
oauthlib==3.2.2
# via
# requests-oauthlib
# social-auth-core
-openedx-filters==1.6.0
+openedx-filters==1.8.1
# via -r requirements/base.in
pbr==6.0.0
# via stevedore
-pycparser==2.21
+psutil==5.9.8
+ # via edx-django-utils
+pycparser==2.22
# via cffi
-pyjwt==2.8.0
- # via social-auth-core
-pymongo==3.13.0
+pyjwt[crypto]==2.8.0
+ # via
+ # drf-jwt
+ # edx-drf-extensions
+ # social-auth-core
+pymongo==4.4.0
# via edx-opaque-keys
+pynacl==1.5.0
+ # via edx-django-utils
python3-openid==3.2.0
# via social-auth-core
-pytz==2024.1
- # via djangorestframework
-requests==2.31.0
+requests==2.32.3
# via
+ # edx-drf-extensions
# requests-oauthlib
# social-auth-core
-requests-oauthlib==1.3.1
+requests-oauthlib==2.0.0
# via social-auth-core
+semantic-version==2.10.0
+ # via edx-drf-extensions
six==1.16.0
# via -r requirements/base.in
-social-auth-core==4.5.3
+social-auth-core==4.5.4
# via -r requirements/base.in
-sqlparse==0.4.4
+sqlparse==0.5.0
# via django
stevedore==5.2.0
- # via edx-opaque-keys
-typing-extensions==4.10.0
+ # via
+ # edx-django-utils
+ # edx-opaque-keys
+typing-extensions==4.12.0
# via
# asgiref
# edx-opaque-keys
diff --git a/requirements/django42.txt b/requirements/django42.txt
index db03776f..092a8ec1 100644
--- a/requirements/django42.txt
+++ b/requirements/django42.txt
@@ -1 +1 @@
-django==4.2.11
+django==4.2.13
diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt
index 8528adba..8460d793 100644
--- a/requirements/pip-tools.txt
+++ b/requirements/pip-tools.txt
@@ -4,17 +4,17 @@
#
# make upgrade
#
-build==1.1.1
+build==1.2.1
# via pip-tools
click==8.1.7
# via pip-tools
-importlib-metadata==7.0.1
+importlib-metadata==7.1.0
# via build
-packaging==23.2
+packaging==24.0
# via build
-pip-tools==7.4.0
+pip-tools==7.4.1
# via -r requirements/pip-tools.in
-pyproject-hooks==1.0.0
+pyproject-hooks==1.1.0
# via
# build
# pip-tools
@@ -22,10 +22,9 @@ tomli==2.0.1
# via
# build
# pip-tools
- # pyproject-hooks
-wheel==0.42.0
+wheel==0.43.0
# via pip-tools
-zipp==3.17.0
+zipp==3.19.0
# via importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
diff --git a/requirements/test.txt b/requirements/test.txt
index 585c8651..16df1c41 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -4,16 +4,17 @@
#
# make upgrade
#
-asgiref==3.7.2
+asgiref==3.8.1
# via
# -r requirements/base.txt
# django
-astroid==3.1.0
+astroid==3.2.2
# via pylint
backports-zoneinfo==0.2.1
# via
# -r requirements/base.txt
# django
+ # djangorestframework
certifi==2024.2.2
# via
# -r requirements/base.txt
@@ -22,15 +23,21 @@ cffi==1.16.0
# via
# -r requirements/base.txt
# cryptography
+ # pynacl
charset-normalizer==3.3.2
# via
# -r requirements/base.txt
# requests
-coverage==7.4.3
+click==8.1.7
+ # via
+ # -r requirements/base.txt
+ # edx-django-utils
+coverage==7.5.3
# via -r requirements/test.in
-cryptography==42.0.5
+cryptography==42.0.7
# via
# -r requirements/base.txt
+ # pyjwt
# social-auth-core
ddt==1.7.2
# via -r requirements/test.in
@@ -46,22 +53,50 @@ dill==0.3.8
# -r requirements/base.txt
# django-crum
# django-mysql
+ # django-waffle
# djangorestframework
+ # drf-jwt
+ # edx-django-utils
+ # edx-drf-extensions
# jsonfield
# openedx-filters
django-crum==0.7.9
- # via -r requirements/base.txt
+ # via
+ # -r requirements/base.txt
+ # edx-django-utils
django-fake-model==0.1.4
# via -r requirements/test.in
-django-mysql==4.12.0
+django-mysql==4.13.0
# via -r requirements/base.txt
-djangorestframework==3.14.0
+django-waffle==4.1.0
+ # via
+ # -r requirements/base.txt
+ # edx-django-utils
+ # edx-drf-extensions
+djangorestframework==3.15.1
+ # via
+ # -r requirements/base.txt
+ # drf-jwt
+ # edx-drf-extensions
+dnspython==2.6.1
+ # via
+ # -r requirements/base.txt
+ # pymongo
+drf-jwt==1.19.2
+ # via
+ # -r requirements/base.txt
+ # edx-drf-extensions
+edx-django-utils==5.14.1
+ # via
+ # -r requirements/base.txt
+ # edx-drf-extensions
+edx-drf-extensions==10.3.0
# via -r requirements/base.txt
-edx-opaque-keys[django]==2.5.1
+edx-opaque-keys[django]==2.9.0
# via
# -r requirements/base.txt
- # edx-opaque-keys
-idna==3.6
+ # edx-drf-extensions
+idna==3.7
# via
# -r requirements/base.txt
# requests
@@ -73,14 +108,18 @@ mccabe==0.7.0
# via pylint
mock==5.1.0
# via -r requirements/test.in
+newrelic==9.10.0
+ # via
+ # -r requirements/base.txt
+ # edx-django-utils
oauthlib==3.2.2
# via
# -r requirements/base.txt
# requests-oauthlib
# social-auth-core
-openedx-filters==1.6.0
+openedx-filters==1.8.1
# via -r requirements/base.txt
-path==16.10.0
+path==16.14.0
# via path-py
path-py==12.5.0
# via -r requirements/test.in
@@ -88,62 +127,74 @@ pbr==6.0.0
# via
# -r requirements/base.txt
# stevedore
-platformdirs==4.2.0
+platformdirs==4.2.2
# via pylint
+psutil==5.9.8
+ # via
+ # -r requirements/base.txt
+ # edx-django-utils
pycodestyle==2.11.1
# via -r requirements/test.in
-pycparser==2.21
+pycparser==2.22
# via
# -r requirements/base.txt
# cffi
-pyjwt==2.8.0
+pyjwt[crypto]==2.8.0
# via
# -r requirements/base.txt
+ # drf-jwt
+ # edx-drf-extensions
# social-auth-core
-pylint==3.1.0
+pylint==3.2.2
# via -r requirements/test.in
-pymongo==3.13.0
+pymongo==4.4.0
# via
# -r requirements/base.txt
# edx-opaque-keys
-pyyaml==6.0.1
- # via -r requirements/test.in
-python3-openid==3.2.0
+pynacl==1.5.0
# via
# -r requirements/base.txt
- # social-auth-core
-pytz==2024.1
+ # edx-django-utils
+python3-openid==3.2.0
# via
# -r requirements/base.txt
- # djangorestframework
-requests==2.31.0
+ # social-auth-core
+pyyaml==6.0.1
+ # via -r requirements/test.in
+requests==2.32.3
# via
# -r requirements/base.txt
+ # edx-drf-extensions
# requests-oauthlib
# social-auth-core
-requests-oauthlib==1.3.1
+requests-oauthlib==2.0.0
# via
# -r requirements/base.txt
# social-auth-core
+semantic-version==2.10.0
+ # via
+ # -r requirements/base.txt
+ # edx-drf-extensions
six==1.16.0
# via -r requirements/base.txt
-social-auth-core==4.5.3
+social-auth-core==4.5.4
# via -r requirements/base.txt
-sqlparse==0.4.4
+sqlparse==0.5.0
# via
# -r requirements/base.txt
# django
stevedore==5.2.0
# via
# -r requirements/base.txt
+ # edx-django-utils
# edx-opaque-keys
-testfixtures==8.1.0
+testfixtures==8.2.0
# via -r requirements/test.in
tomli==2.0.1
# via pylint
-tomlkit==0.12.4
+tomlkit==0.12.5
# via pylint
-typing-extensions==4.10.0
+typing-extensions==4.12.0
# via
# -r requirements/base.txt
# asgiref
diff --git a/requirements/tox.txt b/requirements/tox.txt
index e9a64909..b38db655 100644
--- a/requirements/tox.txt
+++ b/requirements/tox.txt
@@ -12,19 +12,19 @@ colorama==0.4.6
# via tox
distlib==0.3.8
# via virtualenv
-filelock==3.13.1
+filelock==3.14.0
# via
# tox
# virtualenv
-packaging==23.2
+packaging==24.0
# via
# pyproject-api
# tox
-platformdirs==4.2.0
+platformdirs==4.2.2
# via
# tox
# virtualenv
-pluggy==1.4.0
+pluggy==1.5.0
# via tox
pyproject-api==1.6.1
# via tox
@@ -32,7 +32,7 @@ tomli==2.0.1
# via
# pyproject-api
# tox
-tox==4.13.0
+tox==4.15.0
# via -r requirements/tox.in
-virtualenv==20.25.1
+virtualenv==20.26.2
# via tox