django-esi 6.0.0__py3-none-any.whl → 7.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-esi might be problematic. Click here for more details.
- {django_esi-6.0.0.dist-info → django_esi-7.0.0b1.dist-info}/METADATA +21 -19
- {django_esi-6.0.0.dist-info → django_esi-7.0.0b1.dist-info}/RECORD +17 -16
- {django_esi-6.0.0.dist-info → django_esi-7.0.0b1.dist-info}/WHEEL +1 -1
- esi/__init__.py +3 -3
- esi/admin.py +0 -1
- esi/apps.py +1 -1
- esi/checks.py +23 -0
- esi/clients.py +62 -32
- esi/decorators.py +2 -1
- esi/migrations/0013_squashed_0012_fix_token_type_choices.py +57 -0
- esi/models.py +8 -7
- esi/tasks.py +29 -2
- esi/templates/esi/select_token.html +9 -9
- esi/tests/factories.py +1 -1
- esi/tests/test_clients.py +122 -98
- esi/tests/test_tasks.py +59 -4
- {django_esi-6.0.0.dist-info → django_esi-7.0.0b1.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: django-esi
|
|
3
|
-
Version:
|
|
3
|
+
Version: 7.0.0b1
|
|
4
4
|
Summary: Django app for accessing the EVE Swagger Interface (ESI).
|
|
5
5
|
Author-email: Alliance Auth <adarnof@gmail.com>
|
|
6
6
|
Requires-Python: >=3.8
|
|
@@ -8,8 +8,8 @@ Description-Content-Type: text/markdown
|
|
|
8
8
|
Classifier: Environment :: Web Environment
|
|
9
9
|
Classifier: Framework :: Django
|
|
10
10
|
Classifier: Framework :: Django :: 4.2
|
|
11
|
-
Classifier: Framework :: Django :: 5.0
|
|
12
11
|
Classifier: Framework :: Django :: 5.1
|
|
12
|
+
Classifier: Framework :: Django :: 5.2
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
@@ -20,12 +20,14 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
24
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
24
25
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
25
|
-
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: bravado>=10.6,<12
|
|
26
28
|
Requires-Dist: brotli
|
|
27
29
|
Requires-Dist: celery>=4.0.2
|
|
28
|
-
Requires-Dist: django>=4.2,<
|
|
30
|
+
Requires-Dist: django>=4.2,<6
|
|
29
31
|
Requires-Dist: jsonschema<4
|
|
30
32
|
Requires-Dist: python-jose>=3.3
|
|
31
33
|
Requires-Dist: requests>=2.26
|
|
@@ -34,7 +36,7 @@ Requires-Dist: tqdm>=4.62.3
|
|
|
34
36
|
Requires-Dist: myst-parser ; extra == "docs"
|
|
35
37
|
Requires-Dist: sphinx ; extra == "docs"
|
|
36
38
|
Requires-Dist: sphinx-copybutton ; extra == "docs"
|
|
37
|
-
Requires-Dist: sphinx-rtd-theme
|
|
39
|
+
Requires-Dist: sphinx-rtd-theme>=3,<4 ; extra == "docs"
|
|
38
40
|
Requires-Dist: sphinx-tabs ; extra == "docs"
|
|
39
41
|
Requires-Dist: sphinxcontrib-django ; extra == "docs"
|
|
40
42
|
Requires-Dist: coverage ; extra == "test"
|
|
@@ -47,27 +49,27 @@ Project-URL: Tracker, https://gitlab.com/allianceauth/django-esi/-/issues
|
|
|
47
49
|
Provides-Extra: docs
|
|
48
50
|
Provides-Extra: test
|
|
49
51
|
|
|
50
|
-
#
|
|
52
|
+
# Django-ESI
|
|
51
53
|
|
|
52
54
|
Django app for easy access to the EVE Swagger Interface (ESI)
|
|
53
55
|
|
|
54
|
-
[](https://pypi.org/project/django-esi/)
|
|
57
|
+
[](https://pypi.org/project/django-esi/)
|
|
58
|
+
[](https://pypi.org/project/django-esi/)
|
|
59
|
+
[](https://pypi.org/project/django-esi/)
|
|
60
|
+
[](https://gitlab.com/allianceauth/django-esi/pipelines)
|
|
61
|
+
[](https://gitlab.com/allianceauth/django-esi/pipelines)
|
|
60
62
|
[](https://django-esi.readthedocs.io/en/latest/?badge=latest)
|
|
61
63
|
[](https://github.com/pre-commit/pre-commit)
|
|
62
64
|
[](https://discord.gg/fjnHAmk)
|
|
63
65
|
|
|
64
66
|
## Overview
|
|
65
67
|
|
|
66
|
-
Django-
|
|
68
|
+
Django-ESI is a Django app that provides an interface for easy access to the EVE Swagger Interface (ESI), the official API for the game [EVE Online](https://www.eveonline.com/).
|
|
67
69
|
|
|
68
|
-
It is
|
|
70
|
+
It is built upon [Bravado](https://github.com/Yelp/bravado) - a python client library for Swagger 2.0 services.
|
|
69
71
|
|
|
70
|
-
Django-
|
|
72
|
+
Django-ESI adds the following main functionalities to a Django site:
|
|
71
73
|
|
|
72
74
|
- Dynamically generated client for interacting with public and private ESI endpoints
|
|
73
75
|
- Support for adding EVE SSO to authenticate characters and retrieve tokens
|
|
@@ -75,8 +77,8 @@ Django-esi adds the following main functionalities to a Django site:
|
|
|
75
77
|
|
|
76
78
|
## Python Support
|
|
77
79
|
|
|
78
|
-
Django-
|
|
79
|
-
<https://docs.djangoproject.com/en/5.
|
|
80
|
+
Django-ESI follows the Django Python support schedule, The supported version of Python will differ based on the version of Django used.
|
|
81
|
+
<https://docs.djangoproject.com/en/5.2/faq/install/#what-python-version-can-i-use-with-django>
|
|
80
82
|
|
|
81
83
|
## History of this app
|
|
82
84
|
|
|
@@ -84,5 +86,5 @@ This app is a fork from [adarnauth-esi](https://gitlab.com/Adarnof/adarnauth-esi
|
|
|
84
86
|
|
|
85
87
|
## Documentation
|
|
86
88
|
|
|
87
|
-
For all details on how to install and use
|
|
89
|
+
For all details on how to install and use Django-ESI please see the [Documentation](https://django-esi.readthedocs.io/en/latest/).
|
|
88
90
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
esi/__init__.py,sha256=
|
|
2
|
-
esi/admin.py,sha256=
|
|
1
|
+
esi/__init__.py,sha256=H-zKZVSyUvnk6nsG5H5S1P1GjtLAitPB2NqcBd1oHoM,169
|
|
2
|
+
esi/admin.py,sha256=glkzGf8Z76mEd6BGpsMC0B5LLH5--MWlfOCEkSBL7mI,1110
|
|
3
3
|
esi/app_settings.py,sha256=O8TkKonK2zdF7RjMem1vMhEMulAtjvksxKgchrI8jME,3783
|
|
4
|
-
esi/apps.py,sha256=
|
|
5
|
-
esi/checks.py,sha256=
|
|
6
|
-
esi/clients.py,sha256=
|
|
7
|
-
esi/decorators.py,sha256=
|
|
4
|
+
esi/apps.py,sha256=HIu1niTkOXYmCzMVAjYcaFhHrrXeBbHvui6I44OCHXw,280
|
|
5
|
+
esi/checks.py,sha256=Aa3qiEK86w-dhJ4_OfepUxxxjiySMdR-e2fFKGYA9ss,1912
|
|
6
|
+
esi/clients.py,sha256=hQ8LMfqMh5DnwomatIzCWaDeDOP76-9M6glFLZbNrDE,23529
|
|
7
|
+
esi/decorators.py,sha256=HEaOHv-stiyU-AL4nv24kRNzUbN5TMvT282WA6ZC8pY,8128
|
|
8
8
|
esi/errors.py,sha256=KfFtgX8Mys4uMoQtco0n_pQeaM83yVrRSyW6StXuUjo,308
|
|
9
9
|
esi/managers.py,sha256=0g3J2jgoX-BviisjDEEAypkTayVBY-G2I_ZuskTDUqg,11350
|
|
10
|
-
esi/models.py,sha256=
|
|
11
|
-
esi/tasks.py,sha256=
|
|
10
|
+
esi/models.py,sha256=bc8DtvX4cTgLK92O-mgNGXtHIyT_TbH3eefVDGoS8O8,11507
|
|
11
|
+
esi/tasks.py,sha256=AdX30ADt1OK6alTmVW014aDlDs7GEkTYGpQdx2cYQBY,2659
|
|
12
12
|
esi/urls.py,sha256=23dY6ciekJ5wfwzo5LzLp0LFvWudrEAGICpDrscTeD8,152
|
|
13
13
|
esi/views.py,sha256=ytfCILjz1rzITGgAGPyCY9bkFpE3KUfnii2UpHu6Bmc,3916
|
|
14
14
|
esi/locale/de/LC_MESSAGES/django.mo,sha256=9_Fc_R8oZpT_CojH8gBxdSqj2K84EMEMn4wkvmOr7Dg,820
|
|
@@ -43,33 +43,34 @@ esi/migrations/0009_set_old_tokens_to_sso_v1.py,sha256=8NqOauSw3_bU12MKyHEJOXe2u
|
|
|
43
43
|
esi/migrations/0010_set_new_tokens_to_sso_v2.py,sha256=d0YlpAWE6rQm7FQ9h39fF7eJTuOIgBLIouPKF7hj0gU,421
|
|
44
44
|
esi/migrations/0011_add_token_indices.py,sha256=xPvfdIUJ3ewc4zv-7oWRe2nWBL9k5VI74C5sVvQJDD8,1016
|
|
45
45
|
esi/migrations/0012_fix_token_type_choices.py,sha256=QyQs8pi5KqDtHUsbA7w7nH-AGda3GPvyUQs74RXEiGY,525
|
|
46
|
+
esi/migrations/0013_squashed_0012_fix_token_type_choices.py,sha256=eZnYDNevow9XrqlYEtSeGQy_PYDbxHU-GS_oRumGlrA,3951
|
|
46
47
|
esi/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
48
|
esi/static/esi/img/EVE_SSO_Login_Buttons_Large_Black.png,sha256=XjjhXr6EIum1ZsmXPXFOMDdYcezbkoAFmDjwC7fpi6E,2308
|
|
48
49
|
esi/static/esi/img/EVE_SSO_Login_Buttons_Large_White.png,sha256=FUsOnYwHvYTiLZN5dN-_nPgOfLfZmmuFpVLbpcnOXE8,2248
|
|
49
50
|
esi/static/esi/img/EVE_SSO_Login_Buttons_Small_Black.png,sha256=l-YlGVyegcTrfMRhdxDLorRAlkz5xMKiRR5LYvjEZYE,1622
|
|
50
51
|
esi/static/esi/img/EVE_SSO_Login_Buttons_Small_White.png,sha256=al3NRjIngZtrNzExqBqPFDI4d7oSRp6LSdT3p5o9GOU,1546
|
|
51
|
-
esi/templates/esi/select_token.html,sha256=
|
|
52
|
+
esi/templates/esi/select_token.html,sha256=iBdVN0EYuha9CIWG2FgbTbyhJR_HcurDjt3NZy2xQxg,6941
|
|
52
53
|
esi/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
54
|
esi/templatetags/scope_tags.py,sha256=5-7vEwe-B91xo7oSnn-tSuudHSJCoGIhbddDpNrqUnw,167
|
|
54
55
|
esi/tests/__init__.py,sha256=1ymL-74_4alt8AQNs7cFiXO5MmQKlIeOcA4OPOdAitM,3092
|
|
55
56
|
esi/tests/client_authed_pilot.py,sha256=GN0J59TcscHM9HZH6OaPODM9Zel3mvObjJ4Dmmzc2j8,1622
|
|
56
57
|
esi/tests/client_public_pilot.py,sha256=u6P-qbdYSvQ6A_MJqt8Jrkswt-u-FlvYDKtCvA5-PMo,1252
|
|
57
|
-
esi/tests/factories.py,sha256=
|
|
58
|
+
esi/tests/factories.py,sha256=2pjkSYKDfO50bzX1nNcUHz_FAAJs7k07u7R8om6AlP0,1375
|
|
58
59
|
esi/tests/factories_2.py,sha256=25NqVe9zzHkl4U1EN3R7pjdN2ahIBXxrrJhQzZhvw58,1972
|
|
59
60
|
esi/tests/jwt_factory.py,sha256=1Ilg99V7Nc_jtUFrYVNm6GfnJ2f5ev0vhpnDhRur8zo,4944
|
|
60
61
|
esi/tests/test_checks.py,sha256=ntq2ijuJ-6pxGruNh21rM4GlXDLVm-o3sy8DrFbjwEM,1846
|
|
61
|
-
esi/tests/test_clients.py,sha256=
|
|
62
|
+
esi/tests/test_clients.py,sha256=1JWTBV83nZbt82e37DAnJ7h4gm9m_xU1ZDvEjt0sHsc,41363
|
|
62
63
|
esi/tests/test_decorators.py,sha256=I5MhcLKhN5xD4PxgEQqOWQ2kiXquDqQIEve9dbTOsho,15069
|
|
63
64
|
esi/tests/test_management_command.py,sha256=mtxfBtG6CHP1bTy1tJEO-djX4SQwMT3T_mqGnUIM6qs,9863
|
|
64
65
|
esi/tests/test_managers.py,sha256=CEpjXSXyVY6xxgrs0f9FnEh-KY9JPaKjKYiutXLLShE,24309
|
|
65
66
|
esi/tests/test_models.py,sha256=lDj5IcYgXHeOFTHXTsWGg13CSooFaxD0c51kJn_ytAc,13748
|
|
66
67
|
esi/tests/test_swagger.json,sha256=HOrPgbvwm5N521QNcE3baWcZJkSjmuN_VWrR06wEQoo,17241
|
|
67
68
|
esi/tests/test_swagger_full.json,sha256=JCEAZNMFhkdZhquTx4lDhrqGgCrzzzlYU64HdbEv8E4,2548369
|
|
68
|
-
esi/tests/test_tasks.py,sha256=
|
|
69
|
+
esi/tests/test_tasks.py,sha256=nIvfXax_8nQCxNQeT-4TJ84i0__t1qWe155z5rnsktQ,4399
|
|
69
70
|
esi/tests/test_templatetags.py,sha256=b68JWE3HvOlr2aUisJHsTsDS4e7IMjDeqTuzMqC7Re4,517
|
|
70
71
|
esi/tests/test_views.py,sha256=Kj_f2yIpmPG0kx-lAX_sfkaHlIpgbkm02ieA1V3o-k4,13073
|
|
71
72
|
esi/tests/threading_pilot.py,sha256=ax_dEdnTNibA-UQHqbZle_2dh_3jcHKRyrYSOKuE_6U,1931
|
|
72
|
-
django_esi-
|
|
73
|
-
django_esi-
|
|
74
|
-
django_esi-
|
|
75
|
-
django_esi-
|
|
73
|
+
django_esi-7.0.0b1.dist-info/licenses/LICENSE,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
|
|
74
|
+
django_esi-7.0.0b1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
75
|
+
django_esi-7.0.0b1.dist-info/METADATA,sha256=9gZR6sDOlFinhRO_9WFmuFJpiHpPsPM94u54rPY1l5U,4747
|
|
76
|
+
django_esi-7.0.0b1.dist-info/RECORD,,
|
esi/__init__.py
CHANGED
esi/admin.py
CHANGED
|
@@ -23,7 +23,6 @@ class TokenAdmin(admin.ModelAdmin):
|
|
|
23
23
|
def get_scopes(self, obj):
|
|
24
24
|
return ", ".join([x.name for x in obj.scopes.all()])
|
|
25
25
|
|
|
26
|
-
|
|
27
26
|
User = get_user_model()
|
|
28
27
|
list_display = ('user', 'character_name', 'get_scopes')
|
|
29
28
|
search_fields = ['user__%s' % User.USERNAME_FIELD, 'character_name', 'scopes__name']
|
esi/apps.py
CHANGED
esi/checks.py
CHANGED
|
@@ -4,7 +4,9 @@ from django.conf import settings
|
|
|
4
4
|
|
|
5
5
|
@register(Tags.security)
|
|
6
6
|
def check_sso_application_settings(*args, **kwargs):
|
|
7
|
+
|
|
7
8
|
errors = []
|
|
9
|
+
|
|
8
10
|
if (
|
|
9
11
|
not hasattr(settings, "ESI_SSO_CLIENT_ID")
|
|
10
12
|
or not hasattr(settings, "ESI_SSO_CLIENT_SECRET")
|
|
@@ -30,4 +32,25 @@ def check_sso_application_settings(*args, **kwargs):
|
|
|
30
32
|
id='esi.E001'
|
|
31
33
|
)
|
|
32
34
|
)
|
|
35
|
+
|
|
36
|
+
# Check for ESI_USER_CONTACT_EMAIL
|
|
37
|
+
if hasattr(settings, "ESI_USER_CONTACT_EMAIL"):
|
|
38
|
+
# Check if ESI_USER_CONTACT_EMAIL is empty
|
|
39
|
+
if settings.ESI_USER_CONTACT_EMAIL == "":
|
|
40
|
+
errors.append(
|
|
41
|
+
Error(
|
|
42
|
+
msg="'ESI_USER_CONTACT_EMAIL' is empty. A valid email is required as maintainer contact for CCP.",
|
|
43
|
+
hint="",
|
|
44
|
+
id="esi.E002",
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
# ESI_USER_CONTACT_EMAIL not found
|
|
48
|
+
else:
|
|
49
|
+
errors.append(
|
|
50
|
+
Error(
|
|
51
|
+
msg="No 'ESI_USER_CONTACT_EMAIL' found is settings. A valid email is required as maintainer contact for CCP.",
|
|
52
|
+
hint="",
|
|
53
|
+
id="esi.E003",
|
|
54
|
+
)
|
|
55
|
+
)
|
|
33
56
|
return errors
|
esi/clients.py
CHANGED
|
@@ -5,6 +5,7 @@ import logging
|
|
|
5
5
|
from time import sleep
|
|
6
6
|
from urllib import parse as urlparse
|
|
7
7
|
from typing import Any, Union, Tuple
|
|
8
|
+
import warnings
|
|
8
9
|
|
|
9
10
|
from bravado.client import SwaggerClient
|
|
10
11
|
from bravado import requests_client
|
|
@@ -20,7 +21,7 @@ from requests.adapters import HTTPAdapter
|
|
|
20
21
|
from django.core.cache import cache
|
|
21
22
|
|
|
22
23
|
from .errors import TokenExpiredError
|
|
23
|
-
from . import app_settings, __version__, __title__
|
|
24
|
+
from . import app_settings, __version__, __title__, __url__
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
logger = logging.getLogger(__name__)
|
|
@@ -438,13 +439,9 @@ def read_spec(path, http_client=None):
|
|
|
438
439
|
|
|
439
440
|
|
|
440
441
|
def esi_client_factory(
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
version: str = None,
|
|
445
|
-
app_info_text: str = None,
|
|
446
|
-
**kwargs
|
|
447
|
-
) -> SwaggerClient:
|
|
442
|
+
token=None, datasource: str = None, spec_file: str = None, version: str = None,
|
|
443
|
+
app_info_text: str = None, # Deprecate in favour of the following variables
|
|
444
|
+
ua_appname: str = None, ua_version: str = None, ua_url: str = None, **kwargs) -> SwaggerClient:
|
|
448
445
|
"""Generate a new ESI client.
|
|
449
446
|
|
|
450
447
|
Args:
|
|
@@ -452,10 +449,9 @@ def esi_client_factory(
|
|
|
452
449
|
datasource: Name of the ESI datasource to access.
|
|
453
450
|
spec_file: Absolute path to a swagger spec file to load.
|
|
454
451
|
version: Base ESI API version. Accepted values are 'legacy', 'latest',
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
Note that spaces are used as delimiter.
|
|
452
|
+
ua_appname: Name of the App for generating a User-Agent,
|
|
453
|
+
ua_version: Version of the App for generating a User-Agent,
|
|
454
|
+
ua_url: (optional) URL To the Source Code or Documentation for generating a User-Agent,
|
|
459
455
|
kwargs: Explicit resource versions to build, in the form Character='v4'. \
|
|
460
456
|
Same values accepted as version.
|
|
461
457
|
|
|
@@ -466,15 +462,40 @@ def esi_client_factory(
|
|
|
466
462
|
Returns:
|
|
467
463
|
New ESI client
|
|
468
464
|
"""
|
|
465
|
+
|
|
466
|
+
if app_info_text is not None:
|
|
467
|
+
warnings.warn(
|
|
468
|
+
"The 'app_info_text' parameter is deprecated and will be removed in a future release. "
|
|
469
|
+
"Use 'ua_appname', 'ua_version', and `ua_url` to dynamically build a User-Agent instead",
|
|
470
|
+
DeprecationWarning,
|
|
471
|
+
stacklevel=2
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
if ua_appname is None or ua_version is None:
|
|
475
|
+
warnings.warn(
|
|
476
|
+
"Applications must define their own 'ua_appname' and 'ua_version' to generate a User-Agent",
|
|
477
|
+
DeprecationWarning,
|
|
478
|
+
stacklevel=2
|
|
479
|
+
)
|
|
480
|
+
|
|
469
481
|
if app_settings.ESI_INFO_LOGGING_ENABLED:
|
|
470
482
|
logger.info('Generating an ESI client...')
|
|
471
483
|
|
|
472
484
|
client = RequestsClientPlus()
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
user_agent
|
|
485
|
+
|
|
486
|
+
if app_info_text:
|
|
487
|
+
# app_info_text (email@example) Django-ESI/1.2.3 (+https://gitlab.com/allianceauth/django-esi)
|
|
488
|
+
# Deprecated
|
|
489
|
+
user_agent = f"{app_info_text} ({app_settings.ESI_USER_CONTACT_EMAIL}) {__title__}/{__version__} (+{__url__})"
|
|
490
|
+
elif ua_appname is None or ua_version is None:
|
|
491
|
+
# Django-ESI/1.2.3 () (email@example; +https://gitlab.com/allianceauth/django-esi)
|
|
492
|
+
# Deprecated
|
|
493
|
+
user_agent = f"{__title__}/{__version__} ({app_settings.ESI_USER_CONTACT_EMAIL}; +{__url__})"
|
|
494
|
+
else:
|
|
495
|
+
# AppName/1.2.3 (email@example.com) Django-ESI/1.2.3 (+https://gitlab.com/allianceauth/django-esi)
|
|
496
|
+
# or AppName/1.2.3 (email@example.com; +https://gitlab.com/) Django-ESI/1.2.3 (+https://gitlab.com/allianceauth/django-esi) (+https://gitlab.com/allianceauth/django-esi)
|
|
497
|
+
# Preferred
|
|
498
|
+
user_agent = f"{ua_appname}/{ua_version} ({app_settings.ESI_USER_CONTACT_EMAIL}{f'; +{ua_url})' if ua_url else ')'} {__title__}/{__version__} (+{__url__})"
|
|
478
499
|
|
|
479
500
|
client.user_agent = user_agent
|
|
480
501
|
|
|
@@ -535,10 +556,9 @@ class EsiClientProvider:
|
|
|
535
556
|
datasource: Name of the ESI datasource to access.
|
|
536
557
|
spec_file: Absolute path to a swagger spec file to load.
|
|
537
558
|
version: Base ESI API version. Accepted values are 'legacy', 'latest',
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
Note that spaces are used as delimiter.
|
|
559
|
+
ua_appname: Name of the App for generating a User-Agent,
|
|
560
|
+
ua_version: Version of the App for generating a User-Agent,
|
|
561
|
+
ua_url: (optional) URL To the Source Code or Documentation for generating a User-Agent,
|
|
542
562
|
kwargs: Explicit resource versions to build, in the form Character='v4'. \
|
|
543
563
|
Same values accepted as version.
|
|
544
564
|
|
|
@@ -550,30 +570,40 @@ class EsiClientProvider:
|
|
|
550
570
|
_client = None
|
|
551
571
|
|
|
552
572
|
def __init__(
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
version=None,
|
|
557
|
-
app_info_text=None,
|
|
558
|
-
**kwargs
|
|
559
|
-
):
|
|
573
|
+
self, datasource=None, spec_file=None, version=None,
|
|
574
|
+
app_info_text=None, # Deprecate in favour of the following variables
|
|
575
|
+
ua_appname: str = None, ua_version: str = None, ua_url: str = None, **kwargs) -> None:
|
|
560
576
|
self._datasource = datasource
|
|
561
577
|
self._spec_file = spec_file
|
|
562
578
|
self._version = version
|
|
563
|
-
self._app_text = app_info_text
|
|
579
|
+
self._app_text = app_info_text # Deprecate in favour of the following variables
|
|
580
|
+
self._ua_appname = ua_appname
|
|
581
|
+
self._ua_version = ua_version
|
|
582
|
+
self._ua_url = ua_url
|
|
564
583
|
self._kwargs = kwargs
|
|
565
584
|
|
|
585
|
+
if app_info_text is not None:
|
|
586
|
+
warnings.warn(
|
|
587
|
+
"The 'app_info_text' parameter is deprecated and will be removed in a future release. "
|
|
588
|
+
"Use 'ua_appname', 'ua_version', and `ua_url` to dynamically build a User-Agent instead",
|
|
589
|
+
DeprecationWarning,
|
|
590
|
+
stacklevel=2
|
|
591
|
+
)
|
|
592
|
+
|
|
566
593
|
@property
|
|
567
|
-
def client(self):
|
|
594
|
+
def client(self) -> SwaggerClient:
|
|
568
595
|
if self._client is None:
|
|
569
596
|
self._client = esi_client_factory(
|
|
570
597
|
datasource=self._datasource,
|
|
571
598
|
spec_file=self._spec_file,
|
|
572
599
|
version=self._version,
|
|
573
|
-
app_info_text=self._app_text,
|
|
600
|
+
app_info_text=self._app_text, # Deprecate in favour of the following variables
|
|
601
|
+
ua_appname=self._ua_appname,
|
|
602
|
+
ua_version=self._ua_version,
|
|
603
|
+
ua_url=self._ua_url,
|
|
574
604
|
**self._kwargs,
|
|
575
605
|
)
|
|
576
606
|
return self._client
|
|
577
607
|
|
|
578
|
-
def __str__(self):
|
|
608
|
+
def __str__(self) -> str:
|
|
579
609
|
return 'EsiClientProvider'
|
esi/decorators.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from functools import wraps
|
|
3
|
+
from typing import Union
|
|
3
4
|
|
|
4
5
|
from .models import Token, CallbackRedirect
|
|
5
6
|
|
|
@@ -7,7 +8,7 @@ from .models import Token, CallbackRedirect
|
|
|
7
8
|
logger = logging.getLogger(__name__)
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
def _check_callback(request):
|
|
11
|
+
def _check_callback(request) -> Union[Token, None]:
|
|
11
12
|
# ensure session installed in database
|
|
12
13
|
if not request.session.exists(request.session.session_key):
|
|
13
14
|
logger.debug("Creating new session for %s", request.user)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Generated by Django 4.2 on 2025-04-04 01:28
|
|
2
|
+
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
import django.db.models.deletion
|
|
6
|
+
|
|
7
|
+
# Squashmigration imported a RunPython, not needed
|
|
8
|
+
# Manually optimized
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Migration(migrations.Migration):
|
|
12
|
+
|
|
13
|
+
replaces = [('esi', '0001_initial'), ('esi', '0002_scopes_20161208'), ('esi', '0003_hide_tokens_from_admin_site'), ('esi', '0004_remove_unique_access_token'), ('esi', '0005_remove_token_length_limit'), ('esi', '0006_remove_url_length_limit'), ('esi', '0007_fix_mysql_8_migration'), ('esi', '0008_nullable_refresh_token'), ('esi', '0009_set_old_tokens_to_sso_v1'), ('esi', '0010_set_new_tokens_to_sso_v2'), ('esi', '0011_add_token_indices'), ('esi', '0012_fix_token_type_choices')]
|
|
14
|
+
|
|
15
|
+
initial = True
|
|
16
|
+
|
|
17
|
+
dependencies = [
|
|
18
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
operations = [
|
|
22
|
+
migrations.CreateModel(
|
|
23
|
+
name='Scope',
|
|
24
|
+
fields=[
|
|
25
|
+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
26
|
+
('name', models.CharField(help_text='The official EVE name for the scope.', max_length=100, unique=True)),
|
|
27
|
+
('help_text', models.TextField(help_text='The official EVE description of the scope.')),
|
|
28
|
+
],
|
|
29
|
+
),
|
|
30
|
+
migrations.CreateModel(
|
|
31
|
+
name='Token',
|
|
32
|
+
fields=[
|
|
33
|
+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
34
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
|
35
|
+
('access_token', models.TextField(editable=False, help_text='The access token granted by SSO.')),
|
|
36
|
+
('refresh_token', models.TextField(editable=False, help_text='A re-usable token to generate new access tokens upon expiry.', null=True)),
|
|
37
|
+
('character_id', models.IntegerField(db_index=True, help_text='The ID of the EVE character who authenticated by SSO.')),
|
|
38
|
+
('character_name', models.CharField(db_index=True, help_text='The name of the EVE character who authenticated by SSO.', max_length=100)),
|
|
39
|
+
('token_type', models.CharField(choices=[('character', 'Character'), ('corporation', 'Corporation')], default='character', help_text='The applicable range of the token.', max_length=100)),
|
|
40
|
+
('character_owner_hash', models.CharField(db_index=True, help_text='The unique string identifying this character and its owning EVE account. Changes if the owning account changes.', max_length=254)),
|
|
41
|
+
('scopes', models.ManyToManyField(blank=True, help_text='The access scopes granted by this token.', to='esi.scope')),
|
|
42
|
+
('user', models.ForeignKey(blank=True, help_text='The user to whom this token belongs.', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
|
43
|
+
('sso_version', models.IntegerField(default=2, help_text='EVE SSO Version.')),
|
|
44
|
+
],
|
|
45
|
+
),
|
|
46
|
+
migrations.CreateModel(
|
|
47
|
+
name='CallbackRedirect',
|
|
48
|
+
fields=[
|
|
49
|
+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
50
|
+
('url', models.TextField(default='/', help_text='The internal URL to redirect this callback towards.')),
|
|
51
|
+
('session_key', models.CharField(help_text='Session key identifying the session this redirect was created for.', max_length=254, unique=True)),
|
|
52
|
+
('state', models.CharField(help_text='OAuth2 state string representing this session.', max_length=128)),
|
|
53
|
+
('created', models.DateTimeField(auto_now_add=True)),
|
|
54
|
+
('token', models.ForeignKey(blank=True, help_text='Token generated by a completed code exchange from callback processing.', null=True, on_delete=django.db.models.deletion.CASCADE, to='esi.token')),
|
|
55
|
+
],
|
|
56
|
+
),
|
|
57
|
+
]
|
esi/models.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import re
|
|
3
3
|
import logging
|
|
4
|
+
from typing import ClassVar
|
|
4
5
|
|
|
5
6
|
from bravado.client import SwaggerClient
|
|
6
7
|
from requests.auth import HTTPBasicAuth
|
|
@@ -54,7 +55,7 @@ class Scope(models.Model):
|
|
|
54
55
|
except IndexError:
|
|
55
56
|
return name
|
|
56
57
|
|
|
57
|
-
def __str__(self):
|
|
58
|
+
def __str__(self) -> str:
|
|
58
59
|
return self.name
|
|
59
60
|
|
|
60
61
|
|
|
@@ -121,16 +122,16 @@ class Token(models.Model):
|
|
|
121
122
|
default=2
|
|
122
123
|
)
|
|
123
124
|
|
|
124
|
-
objects = TokenManager()
|
|
125
|
+
objects: ClassVar[TokenManager] = TokenManager()
|
|
125
126
|
|
|
126
|
-
def __str__(self):
|
|
127
|
+
def __str__(self) -> str:
|
|
127
128
|
try:
|
|
128
129
|
scopes = sorted(s.name for s in self.scopes.all())
|
|
129
130
|
except ValueError:
|
|
130
131
|
scopes = []
|
|
131
132
|
return f'{self.character_name} - {", ".join(scopes)}'
|
|
132
133
|
|
|
133
|
-
def __repr__(self):
|
|
134
|
+
def __repr__(self) -> str:
|
|
134
135
|
return "<{}(id={}): {}, {}>".format(
|
|
135
136
|
self.__class__.__name__,
|
|
136
137
|
self.pk,
|
|
@@ -248,7 +249,7 @@ class Token(models.Model):
|
|
|
248
249
|
logger.debug("Not a refreshable token.")
|
|
249
250
|
raise NotRefreshableTokenError()
|
|
250
251
|
|
|
251
|
-
def refresh_or_delete(self):
|
|
252
|
+
def refresh_or_delete(self) -> None:
|
|
252
253
|
"""Refresh this token or delete it if it can not be refreshed."""
|
|
253
254
|
try:
|
|
254
255
|
self.refresh()
|
|
@@ -345,10 +346,10 @@ class CallbackRedirect(models.Model):
|
|
|
345
346
|
)
|
|
346
347
|
)
|
|
347
348
|
|
|
348
|
-
def __str__(self):
|
|
349
|
+
def __str__(self) -> str:
|
|
349
350
|
return f"{self.session_key}: {self.url}"
|
|
350
351
|
|
|
351
|
-
def __repr__(self):
|
|
352
|
+
def __repr__(self) -> str:
|
|
352
353
|
return "<{}(pk={}): {} to {}>".format(
|
|
353
354
|
self.__class__.__name__, self.pk,
|
|
354
355
|
self.session_key, self.url
|
esi/tasks.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from datetime import timedelta
|
|
2
2
|
import logging
|
|
3
|
+
from math import ceil
|
|
3
4
|
|
|
4
5
|
from celery import shared_task
|
|
5
6
|
|
|
@@ -26,14 +27,15 @@ def cleanup_callbackredirect(max_age=300):
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
@shared_task
|
|
29
|
-
def cleanup_token():
|
|
30
|
+
def cleanup_token() -> None:
|
|
30
31
|
"""
|
|
31
|
-
Delete expired :model:`esi.Token` models.
|
|
32
|
+
Delete Orphaned Tokens, then refresh or delete expired :model:`esi.Token` models.
|
|
32
33
|
"""
|
|
33
34
|
orphaned_tokens = Token.objects.filter(user__isnull=True)
|
|
34
35
|
if orphaned_tokens.exists():
|
|
35
36
|
logger.info("Deleting %d orphaned tokens.", orphaned_tokens.count())
|
|
36
37
|
orphaned_tokens.delete()
|
|
38
|
+
|
|
37
39
|
expired_tokens = Token.objects.exclude(user__isnull=True).get_expired()
|
|
38
40
|
if expired_tokens.exists():
|
|
39
41
|
logger.info(
|
|
@@ -46,6 +48,31 @@ def cleanup_token():
|
|
|
46
48
|
refresh_or_delete_token.apply_async(args=[token_pk], priority=8)
|
|
47
49
|
|
|
48
50
|
|
|
51
|
+
@shared_task
|
|
52
|
+
def cleanup_token_subset(fraction: int = 48) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Delete Orphaned Tokens, then refresh or delete a subset of expired :model:`esi.Token` models.
|
|
55
|
+
|
|
56
|
+
This task operates on 1/fraction of the oldest tokens and can be called on a more regular schedule
|
|
57
|
+
"""
|
|
58
|
+
orphaned_tokens = Token.objects.filter(user__isnull=True)
|
|
59
|
+
if orphaned_tokens.exists():
|
|
60
|
+
logger.info("Deleting %d orphaned tokens.", orphaned_tokens.count())
|
|
61
|
+
orphaned_tokens.delete()
|
|
62
|
+
|
|
63
|
+
expired_tokens = Token.objects.exclude(user__isnull=True).get_expired()
|
|
64
|
+
expired_tokens_subset = expired_tokens.filter(
|
|
65
|
+
refresh_token__isnull=False
|
|
66
|
+
).order_by("created")[:ceil(expired_tokens.count() / fraction)]
|
|
67
|
+
|
|
68
|
+
if expired_tokens.exists():
|
|
69
|
+
logger.info(
|
|
70
|
+
f"Triggering bulk refresh of subset/possible {expired_tokens_subset.count()}/{expired_tokens.count()} expired tokens."
|
|
71
|
+
)
|
|
72
|
+
for token_pk in expired_tokens_subset.values_list("pk", flat=True):
|
|
73
|
+
refresh_or_delete_token.apply_async(args=[token_pk], priority=8)
|
|
74
|
+
|
|
75
|
+
|
|
49
76
|
@shared_task
|
|
50
77
|
def refresh_or_delete_token(token_pk: int):
|
|
51
78
|
token = Token.objects.get(pk=token_pk)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<meta name="description" content="">
|
|
11
11
|
<meta name="author" content="">
|
|
12
12
|
|
|
13
|
-
<title>{%
|
|
13
|
+
<title>{% translate "ESI Token Selection" %}</title>
|
|
14
14
|
|
|
15
15
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.1/css/bootstrap.min.css" integrity="sha512-T584yQ/tdRR5QwOpfvDfVQUidzfgc2339Lc8uBDtcp/wYu80d7jwBgAxbyMh0a9YM9F8N3tdErpFI8iaGx6x5g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
|
16
16
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.slim.min.js" integrity="sha512-6ORWJX/LrnSjBzwefdNUyLCMTIsGoNP6NftMy2UAm1JBm6PRZCO1d7OHBStWpVFZLO+RerTvqX/Z9mBFfCJZ4A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
</head>
|
|
42
42
|
<body class="bg-dark">
|
|
43
43
|
<div class="container-fluid mt-5">
|
|
44
|
-
<h3 class="text-light font-weight-light text-center">{%
|
|
44
|
+
<h3 class="text-light font-weight-light text-center">{% translate "Select Character" %}</h3><br>
|
|
45
45
|
<div class="mt-2">
|
|
46
46
|
<div class="col-lg-10 offset-lg-1 col-md-12" style="padding-left: 0 !important; padding-right: 0 !important;">
|
|
47
47
|
<div class="card text-white bg-secondary">
|
|
48
|
-
<div class="card-header text-center">{%
|
|
48
|
+
<div class="card-header text-center">{% translate "Scopes Requested" %}</div>
|
|
49
49
|
<div class="card-body align-middle text-center">
|
|
50
50
|
{% for scope in scopes %}<span class="badge badge-dark">{{ scope|scope_friendly_name }}</span> {% endfor %}
|
|
51
51
|
</div>
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
{% if tokens|length > 14 %}
|
|
59
59
|
<form method="post">
|
|
60
60
|
<div class="card text-white bg-secondary m-2 sso-card">
|
|
61
|
-
<div class="card-header text-center">{%
|
|
61
|
+
<div class="card-header text-center">{% translate "New Character" %}</div>
|
|
62
62
|
<div class="card-body text-center d-none d-sm-block">
|
|
63
63
|
<input type="image" formmethod="post" class="ra-avatar img-responsive character-image rounded-lg"
|
|
64
64
|
src="https://images.evetech.net/characters/1/portrait?size=256">
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
<input type="hidden" name="_add" value="True">
|
|
69
69
|
<a href="#" type="submit" class="m-1">
|
|
70
70
|
<input type="image" name="go" src="{% static 'esi\img\EVE_SSO_Login_Buttons_Small_Black.png'%}"
|
|
71
|
-
alt="{%
|
|
71
|
+
alt="{% translate "Add Token" %}">
|
|
72
72
|
</a>
|
|
73
73
|
</div>
|
|
74
74
|
</div>
|
|
@@ -85,8 +85,8 @@
|
|
|
85
85
|
</div>
|
|
86
86
|
<div class="card-footer">
|
|
87
87
|
<input type="hidden" name="_token" value="{{ token.pk }}">
|
|
88
|
-
<button class="btn btn-success w-100" type="submit" title="{%
|
|
89
|
-
{%
|
|
88
|
+
<button class="btn btn-success w-100" type="submit" title="{% translate "Select" %}" formmethod="post">
|
|
89
|
+
{% translate "Select" %}
|
|
90
90
|
</button>
|
|
91
91
|
</div>
|
|
92
92
|
</div>
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
<form method="post">
|
|
96
96
|
{% csrf_token %}
|
|
97
97
|
<div class="card text-white bg-secondary m-2 sso-card">
|
|
98
|
-
<div class="card-header text-center">{%
|
|
98
|
+
<div class="card-header text-center">{% translate "New Character" %}</div>
|
|
99
99
|
<div class="card-body text-center d-none d-sm-block">
|
|
100
100
|
<input type="image" formmethod="post" class="ra-avatar img-responsive character-image rounded-lg"
|
|
101
101
|
src="https://images.evetech.net/characters/1/portrait?size=256">
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
<input type="hidden" name="_add" value="True">
|
|
105
105
|
<a href="#" type="submit" class="m-1">
|
|
106
106
|
<input type="image" name="go" src="{% static 'esi\img\EVE_SSO_Login_Buttons_Small_Black.png'%}"
|
|
107
|
-
alt="{%
|
|
107
|
+
alt="{% translate "Add Token" %}">
|
|
108
108
|
</a>
|
|
109
109
|
</div>
|
|
110
110
|
</div>
|
esi/tests/factories.py
CHANGED
esi/tests/test_clients.py
CHANGED
|
@@ -716,8 +716,8 @@ class TestClientResult2(NoSocketsTestCase):
|
|
|
716
716
|
def setUpClass(cls) -> None:
|
|
717
717
|
super().setUpClass()
|
|
718
718
|
spec = _load_json_file(SWAGGER_SPEC_PATH_MINIMAL)
|
|
719
|
-
with patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL",
|
|
720
|
-
MODULE_PATH + ".__title__", "
|
|
719
|
+
with patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com"), patch(
|
|
720
|
+
MODULE_PATH + ".__title__", "Django-ESI"
|
|
721
721
|
), patch(MODULE_PATH + ".__version__", "1.0.0"):
|
|
722
722
|
with requests_mock.Mocker() as requests_mocker:
|
|
723
723
|
requests_mocker.register_uri(
|
|
@@ -733,7 +733,7 @@ class TestClientResult2(NoSocketsTestCase):
|
|
|
733
733
|
# then
|
|
734
734
|
self.assertTrue(requests_mocker.called)
|
|
735
735
|
request = requests_mocker.last_request
|
|
736
|
-
self.assertEqual(request._request.headers["User-Agent"], "
|
|
736
|
+
self.assertEqual(request._request.headers["User-Agent"], "Django-ESI/1.0.0 (email@example.com; +https://gitlab.com/allianceauth/django-esi)")
|
|
737
737
|
|
|
738
738
|
def test_existing_headers(self, requests_mocker):
|
|
739
739
|
# given
|
|
@@ -743,7 +743,7 @@ class TestClientResult2(NoSocketsTestCase):
|
|
|
743
743
|
# then
|
|
744
744
|
self.assertTrue(requests_mocker.called)
|
|
745
745
|
request = requests_mocker.last_request
|
|
746
|
-
self.assertEqual(request._request.headers["User-Agent"], "
|
|
746
|
+
self.assertEqual(request._request.headers["User-Agent"], "Django-ESI/1.0.0 (email@example.com; +https://gitlab.com/allianceauth/django-esi)")
|
|
747
747
|
|
|
748
748
|
|
|
749
749
|
class TestRequestsClientPlus(NoSocketsTestCase):
|
|
@@ -764,11 +764,11 @@ class TestRequestsClientPlus(NoSocketsTestCase):
|
|
|
764
764
|
{
|
|
765
765
|
"method": "GET",
|
|
766
766
|
"url": "https://esi.evetech.net/v1/status/",
|
|
767
|
-
"headers": {"From": "
|
|
767
|
+
"headers": {"From": "email@example.com"},
|
|
768
768
|
}
|
|
769
769
|
)
|
|
770
770
|
self.assertEqual(result.future.request.headers["User-Agent"], "abc")
|
|
771
|
-
self.assertEqual(result.future.request.headers["From"], "
|
|
771
|
+
self.assertEqual(result.future.request.headers["From"], "email@example.com")
|
|
772
772
|
|
|
773
773
|
def test_no_user_agent(self):
|
|
774
774
|
"""When no user agent is defined, leave the existing header intact"""
|
|
@@ -777,14 +777,14 @@ class TestRequestsClientPlus(NoSocketsTestCase):
|
|
|
777
777
|
{
|
|
778
778
|
"method": "GET",
|
|
779
779
|
"url": "https://esi.evetech.net/v1/status/",
|
|
780
|
-
"headers": {"From": "
|
|
780
|
+
"headers": {"From": "email@example.com"},
|
|
781
781
|
}
|
|
782
782
|
)
|
|
783
|
-
self.assertEqual(result.future.request.headers["From"], "
|
|
783
|
+
self.assertEqual(result.future.request.headers["From"], "email@example.com")
|
|
784
784
|
self.assertNotIn("User-Agent", result.future.request.headers)
|
|
785
785
|
|
|
786
786
|
|
|
787
|
-
@patch(MODULE_PATH + ".__title__", "
|
|
787
|
+
@patch(MODULE_PATH + ".__title__", "Django-ESI")
|
|
788
788
|
@patch(MODULE_PATH + ".__version__", "1.0.0")
|
|
789
789
|
@requests_mock.Mocker()
|
|
790
790
|
class TestEsiClientFactoryAppText(NoSocketsTestCase):
|
|
@@ -799,88 +799,81 @@ class TestEsiClientFactoryAppText(NoSocketsTestCase):
|
|
|
799
799
|
}
|
|
800
800
|
|
|
801
801
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", None)
|
|
802
|
-
def test_defaults(self, requests_mocker):
|
|
802
|
+
def test_defaults(self, requests_mocker) -> None:
|
|
803
|
+
# This test is not expected to hit given that ESI_USER_CONTACT_EMAIL must be set
|
|
804
|
+
# But here it is for completeness
|
|
805
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
806
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
807
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
808
|
+
client = esi_client_factory()
|
|
809
|
+
# when
|
|
810
|
+
operation = client.Status.get_status()
|
|
811
|
+
# then
|
|
812
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "Django-ESI/1.0.0 (None; +https://gitlab.com/allianceauth/django-esi)")
|
|
813
|
+
|
|
814
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
815
|
+
def test_defaults_email(self, requests_mocker) -> None:
|
|
803
816
|
# given
|
|
804
|
-
requests_mocker.register_uri(
|
|
805
|
-
|
|
806
|
-
)
|
|
807
|
-
requests_mocker.register_uri(
|
|
808
|
-
"GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec
|
|
809
|
-
)
|
|
810
|
-
requests_mocker.register_uri(
|
|
811
|
-
"GET", url="https://esi.evetech.net/v1/status/", json=self.status_response
|
|
812
|
-
)
|
|
817
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
818
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
819
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
813
820
|
client = esi_client_factory()
|
|
814
821
|
# when
|
|
815
822
|
operation = client.Status.get_status()
|
|
816
823
|
# then
|
|
817
|
-
self.assertEqual(
|
|
818
|
-
operation.future.request.headers["User-Agent"], "django-esi v1.0.0"
|
|
819
|
-
)
|
|
824
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "Django-ESI/1.0.0 (email@example.com; +https://gitlab.com/allianceauth/django-esi)")
|
|
820
825
|
|
|
821
826
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", None)
|
|
822
|
-
def test_app_text(self, requests_mocker):
|
|
827
|
+
def test_app_text(self, requests_mocker) -> None:
|
|
828
|
+
# Deprecated
|
|
829
|
+
# This test is not expected to hit given that ESI_USER_CONTACT_EMAIL must be set
|
|
823
830
|
# given
|
|
824
|
-
requests_mocker.register_uri(
|
|
825
|
-
|
|
826
|
-
)
|
|
827
|
-
requests_mocker.register_uri(
|
|
828
|
-
"GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec
|
|
829
|
-
)
|
|
830
|
-
requests_mocker.register_uri(
|
|
831
|
-
"GET", url="https://esi.evetech.net/v1/status/", json=self.status_response
|
|
832
|
-
)
|
|
831
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
832
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
833
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
833
834
|
client = esi_client_factory(app_info_text="my-app v1.0.0")
|
|
834
835
|
# when
|
|
835
836
|
operation = client.Status.get_status()
|
|
836
837
|
# then
|
|
837
|
-
self.assertEqual(
|
|
838
|
-
operation.future.request.headers["User-Agent"], "my-app v1.0.0"
|
|
839
|
-
)
|
|
838
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "my-app v1.0.0 (None) Django-ESI/1.0.0 (+https://gitlab.com/allianceauth/django-esi)",)
|
|
840
839
|
|
|
841
|
-
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "
|
|
840
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
842
841
|
def test_app_text_with_email(self, requests_mocker):
|
|
842
|
+
# Deprecated
|
|
843
843
|
# given
|
|
844
|
-
requests_mocker.register_uri(
|
|
845
|
-
|
|
846
|
-
)
|
|
847
|
-
requests_mocker.register_uri(
|
|
848
|
-
"GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec
|
|
849
|
-
)
|
|
850
|
-
requests_mocker.register_uri(
|
|
851
|
-
"GET", url="https://esi.evetech.net/v1/status/", json=self.status_response
|
|
852
|
-
)
|
|
844
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
845
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
846
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
853
847
|
client = esi_client_factory(app_info_text="my-app v1.0.0")
|
|
854
848
|
# when
|
|
855
849
|
operation = client.Status.get_status()
|
|
856
850
|
# then
|
|
857
|
-
self.assertEqual(
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
requests_mocker.register_uri(
|
|
866
|
-
"GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec
|
|
867
|
-
)
|
|
868
|
-
requests_mocker.register_uri(
|
|
869
|
-
"GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec
|
|
870
|
-
)
|
|
871
|
-
requests_mocker.register_uri(
|
|
872
|
-
"GET", url="https://esi.evetech.net/v1/status/", json=self.status_response
|
|
873
|
-
)
|
|
874
|
-
client = esi_client_factory()
|
|
851
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "my-app v1.0.0 (email@example.com) Django-ESI/1.0.0 (+https://gitlab.com/allianceauth/django-esi)",)
|
|
852
|
+
|
|
853
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
854
|
+
def test_ua_generator(self, requests_mocker):
|
|
855
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
856
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
857
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
858
|
+
client = esi_client_factory(ua_appname="MyApp", ua_version="1.0.0")
|
|
875
859
|
# when
|
|
876
860
|
operation = client.Status.get_status()
|
|
877
861
|
# then
|
|
878
|
-
self.assertEqual(
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
862
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "MyApp/1.0.0 (email@example.com) Django-ESI/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
|
863
|
+
|
|
864
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
865
|
+
def test_ua_generator_with_url(self, requests_mocker):
|
|
866
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
867
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
868
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
869
|
+
client = esi_client_factory(ua_appname="MyApp", ua_version="1.0.0", ua_url="https://example.com")
|
|
870
|
+
# when
|
|
871
|
+
operation = client.Status.get_status()
|
|
872
|
+
# then
|
|
873
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "MyApp/1.0.0 (email@example.com; +https://example.com) Django-ESI/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
|
883
874
|
|
|
875
|
+
@patch(MODULE_PATH + ".__title__", "Django-ESI")
|
|
876
|
+
@patch(MODULE_PATH + ".__version__", "1.0.0")
|
|
884
877
|
@requests_mock.Mocker()
|
|
885
878
|
class TestEsiClientProviderAppText(NoSocketsTestCase):
|
|
886
879
|
@classmethod
|
|
@@ -893,45 +886,76 @@ class TestEsiClientProviderAppText(NoSocketsTestCase):
|
|
|
893
886
|
"start_time": "2017-01-02T12:34:56Z",
|
|
894
887
|
}
|
|
895
888
|
|
|
896
|
-
@patch(MODULE_PATH + ".__title__", "django-esi")
|
|
897
|
-
@patch(MODULE_PATH + ".__version__", "1.0.0")
|
|
898
889
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", None)
|
|
899
|
-
def test_defaults(self, requests_mocker):
|
|
890
|
+
def test_defaults(self, requests_mocker) -> None:
|
|
891
|
+
# This test is not expected to hit given that ESI_USER_CONTACT_EMAIL must be set
|
|
892
|
+
# But here it is for completeness
|
|
893
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
894
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
895
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
896
|
+
client = EsiClientProvider().client
|
|
897
|
+
# when
|
|
898
|
+
operation = client.Status.get_status()
|
|
899
|
+
# then
|
|
900
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "Django-ESI/1.0.0 (None; +https://gitlab.com/allianceauth/django-esi)")
|
|
901
|
+
|
|
902
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
903
|
+
def test_defaults_email(self, requests_mocker) -> None:
|
|
900
904
|
# given
|
|
901
|
-
requests_mocker.register_uri(
|
|
902
|
-
|
|
903
|
-
)
|
|
904
|
-
requests_mocker.register_uri(
|
|
905
|
-
"GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec
|
|
906
|
-
)
|
|
907
|
-
requests_mocker.register_uri(
|
|
908
|
-
"GET", url="https://esi.evetech.net/v1/status/", json=self.status_response
|
|
909
|
-
)
|
|
905
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
906
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
907
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
910
908
|
client = EsiClientProvider().client
|
|
911
|
-
#
|
|
909
|
+
# when
|
|
912
910
|
operation = client.Status.get_status()
|
|
913
911
|
# then
|
|
914
|
-
self.assertEqual(
|
|
915
|
-
operation.future.request.headers["User-Agent"], "django-esi v1.0.0"
|
|
916
|
-
)
|
|
912
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "Django-ESI/1.0.0 (email@example.com; +https://gitlab.com/allianceauth/django-esi)")
|
|
917
913
|
|
|
918
|
-
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL",
|
|
914
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", None)
|
|
915
|
+
def test_app_text(self, requests_mocker) -> None:
|
|
916
|
+
# Deprecated
|
|
917
|
+
# This test is not expected to hit given that ESI_USER_CONTACT_EMAIL must be set
|
|
918
|
+
# given
|
|
919
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
920
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
921
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
922
|
+
client = EsiClientProvider(app_info_text="my-app v1.0.0").client
|
|
923
|
+
# when
|
|
924
|
+
operation = client.Status.get_status()
|
|
925
|
+
# then
|
|
926
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "my-app v1.0.0 (None) Django-ESI/1.0.0 (+https://gitlab.com/allianceauth/django-esi)",)
|
|
927
|
+
|
|
928
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
919
929
|
def test_app_text_with_email(self, requests_mocker):
|
|
930
|
+
# Deprecated
|
|
920
931
|
# given
|
|
921
|
-
requests_mocker.register_uri(
|
|
922
|
-
|
|
923
|
-
)
|
|
924
|
-
requests_mocker.register_uri(
|
|
925
|
-
"GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec
|
|
926
|
-
)
|
|
927
|
-
requests_mocker.register_uri(
|
|
928
|
-
"GET", url="https://esi.evetech.net/v1/status/", json=self.status_response
|
|
929
|
-
)
|
|
932
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
933
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
934
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
930
935
|
client = EsiClientProvider(app_info_text="my-app v1.0.0").client
|
|
931
936
|
# when
|
|
932
937
|
operation = client.Status.get_status()
|
|
933
938
|
# then
|
|
934
|
-
self.assertEqual(
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
939
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "my-app v1.0.0 (email@example.com) Django-ESI/1.0.0 (+https://gitlab.com/allianceauth/django-esi)",)
|
|
940
|
+
|
|
941
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
942
|
+
def test_ua_generator(self, requests_mocker):
|
|
943
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
944
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
945
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
946
|
+
client = EsiClientProvider(ua_appname="MyApp", ua_version="1.0.0").client
|
|
947
|
+
# when
|
|
948
|
+
operation = client.Status.get_status()
|
|
949
|
+
# then
|
|
950
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "MyApp/1.0.0 (email@example.com) Django-ESI/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
|
951
|
+
|
|
952
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
953
|
+
def test_ua_generator_with_url(self, requests_mocker):
|
|
954
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
955
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
956
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
957
|
+
client = EsiClientProvider(ua_appname="MyApp", ua_version="1.0.0", ua_url="https://example.com").client
|
|
958
|
+
# when
|
|
959
|
+
operation = client.Status.get_status()
|
|
960
|
+
# then
|
|
961
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], "MyApp/1.0.0 (email@example.com; +https://example.com) Django-ESI/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
esi/tests/test_tasks.py
CHANGED
|
@@ -5,13 +5,16 @@ from celery import current_app as celery_app
|
|
|
5
5
|
from django.utils.timezone import now
|
|
6
6
|
|
|
7
7
|
from esi.models import CallbackRedirect, Token
|
|
8
|
-
from esi.tasks import cleanup_callbackredirect, cleanup_token
|
|
8
|
+
from esi.tasks import cleanup_callbackredirect, cleanup_token, cleanup_token_subset
|
|
9
9
|
|
|
10
10
|
from . import NoSocketsTestCase
|
|
11
11
|
from .factories_2 import TokenFactory, CallbackRedirectFactory
|
|
12
12
|
|
|
13
|
+
from math import ceil
|
|
14
|
+
|
|
13
15
|
MANAGERS_PATH = "esi.managers"
|
|
14
16
|
MODELS_PATH = "esi.models"
|
|
17
|
+
TASKS_PATH = "esi.tasks"
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
class CeleryTestCase(NoSocketsTestCase):
|
|
@@ -23,7 +26,7 @@ class CeleryTestCase(NoSocketsTestCase):
|
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
class TestCleanupCallbackredirect(CeleryTestCase):
|
|
26
|
-
def test_should_remove_expired(self):
|
|
29
|
+
def test_should_remove_expired(self) -> None:
|
|
27
30
|
# given
|
|
28
31
|
cb_valid = CallbackRedirectFactory()
|
|
29
32
|
with patch("django.utils.timezone.now") as m:
|
|
@@ -39,7 +42,7 @@ class TestCleanupCallbackredirect(CeleryTestCase):
|
|
|
39
42
|
@patch(MANAGERS_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 120)
|
|
40
43
|
@patch(MODELS_PATH + '.Token.refresh', spec=True)
|
|
41
44
|
class TestCleanupToken(CeleryTestCase):
|
|
42
|
-
def test_should_delete_orphaned_tokens(self, mock_token_refresh):
|
|
45
|
+
def test_should_delete_orphaned_tokens(self, mock_token_refresh) -> None:
|
|
43
46
|
# given
|
|
44
47
|
token_1 = TokenFactory(user=None)
|
|
45
48
|
token_2 = TokenFactory()
|
|
@@ -49,7 +52,7 @@ class TestCleanupToken(CeleryTestCase):
|
|
|
49
52
|
self.assertFalse(Token.objects.filter(pk=token_1.pk).exists())
|
|
50
53
|
self.assertTrue(Token.objects.filter(pk=token_2.pk).exists())
|
|
51
54
|
|
|
52
|
-
def test_should_refresh_expired_tokens_only(self, mock_token_refresh):
|
|
55
|
+
def test_should_refresh_expired_tokens_only(self, mock_token_refresh) -> None:
|
|
53
56
|
# given
|
|
54
57
|
TokenFactory()
|
|
55
58
|
with patch("django.utils.timezone.now") as m:
|
|
@@ -59,3 +62,55 @@ class TestCleanupToken(CeleryTestCase):
|
|
|
59
62
|
cleanup_token.delay()
|
|
60
63
|
# then
|
|
61
64
|
self.assertEqual(mock_token_refresh.call_count, 1)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TestCleanupTokenSubset(CeleryTestCase):
|
|
68
|
+
@patch(MANAGERS_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 120)
|
|
69
|
+
@patch(TASKS_PATH + ".refresh_or_delete_token.apply_async")
|
|
70
|
+
def test_should_delete_orphaned_tokens(self, mock_token_refresh) -> None:
|
|
71
|
+
# given
|
|
72
|
+
orphaned = TokenFactory(user=None)
|
|
73
|
+
valid = TokenFactory()
|
|
74
|
+
# when
|
|
75
|
+
cleanup_token_subset.delay()
|
|
76
|
+
# then
|
|
77
|
+
self.assertFalse(Token.objects.filter(pk=orphaned.pk).exists())
|
|
78
|
+
self.assertTrue(Token.objects.filter(pk=valid.pk).exists())
|
|
79
|
+
|
|
80
|
+
@patch(MANAGERS_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 1)
|
|
81
|
+
@patch(TASKS_PATH + ".refresh_or_delete_token.apply_async")
|
|
82
|
+
def test_should_refresh_fraction_of_expired_tokens(self, mock_token_refresh) -> None:
|
|
83
|
+
# given
|
|
84
|
+
num_expired = 100
|
|
85
|
+
for _ in range(num_expired):
|
|
86
|
+
TokenFactory(refresh_token="some_token")
|
|
87
|
+
# patch time so all tokens are expired
|
|
88
|
+
with patch("django.utils.timezone.now") as m:
|
|
89
|
+
m.return_value = now() + timedelta(minutes=5)
|
|
90
|
+
# when
|
|
91
|
+
cleanup_token_subset.delay(fraction=10)
|
|
92
|
+
# then
|
|
93
|
+
expected_count = ceil(num_expired / 10)
|
|
94
|
+
self.assertEqual(mock_token_refresh.call_count, expected_count)
|
|
95
|
+
|
|
96
|
+
@patch(MANAGERS_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 1)
|
|
97
|
+
@patch("esi.tasks.refresh_or_delete_token.apply_async")
|
|
98
|
+
def test_should_refresh_expired_tokens_only(self, mock_token_refresh) -> None:
|
|
99
|
+
# given
|
|
100
|
+
TokenFactory()
|
|
101
|
+
with patch("django.utils.timezone.now") as m:
|
|
102
|
+
m.return_value = now() - timedelta(minutes=3)
|
|
103
|
+
TokenFactory()
|
|
104
|
+
# when
|
|
105
|
+
cleanup_token_subset.delay()
|
|
106
|
+
# then
|
|
107
|
+
self.assertEqual(mock_token_refresh.call_count, 1)
|
|
108
|
+
|
|
109
|
+
@patch(MANAGERS_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 1)
|
|
110
|
+
@patch(TASKS_PATH + ".refresh_or_delete_token.apply_async")
|
|
111
|
+
def test_should_log_and_exit_gracefully_with_no_tokens(self, mock_token_refresh) -> None:
|
|
112
|
+
# when
|
|
113
|
+
cleanup_token_subset.delay()
|
|
114
|
+
# then
|
|
115
|
+
self.assertEqual(Token.objects.count(), 0)
|
|
116
|
+
mock_token_refresh.assert_not_called()
|
|
File without changes
|