open-api-framework 0.13.2__tar.gz → 0.13.3__tar.gz
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.
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/CHANGELOG.rst +11 -0
- {open_api_framework-0.13.2/open_api_framework.egg-info → open_api_framework-0.13.3}/PKG-INFO +2 -2
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/README.rst +1 -1
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/conf/base.py +176 -136
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/conf/utils.py +55 -27
- {open_api_framework-0.13.2 → open_api_framework-0.13.3/open_api_framework.egg-info}/PKG-INFO +2 -2
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/pyproject.toml +2 -2
- open_api_framework-0.13.3/tests/test_config_helpers.py +50 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/tests/test_csp.py +7 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/tests/test_generate_envvar_docs.py +8 -1
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/tests/test_settings.py +4 -0
- open_api_framework-0.13.2/tests/test_config_helpers.py +0 -13
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/LICENSE +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/MANIFEST.in +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/__init__.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/admin.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/apps.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/conf/__init__.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/context_processors.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/management/__init__.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/management/commands/__init__.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/management/commands/generate_envvar_docs.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/py.typed +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/static/open_api_framework/css/admin.css +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/templates/open_api_framework/admin/base_site.html +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/templates/open_api_framework/components/environment.html +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/templates/open_api_framework/components/version.html +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/templates/open_api_framework/env_config.rst +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/templatetags/__init__.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/templatetags/doc_tags.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/templatetags/open_api_framework.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/utils.py +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework.egg-info/SOURCES.txt +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework.egg-info/dependency_links.txt +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework.egg-info/requires.txt +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework.egg-info/top_level.txt +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/setup.cfg +0 -0
- {open_api_framework-0.13.2 → open_api_framework-0.13.3}/tests/test_admin.py +0 -0
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
0.13.3 (2026-01-28)
|
|
5
|
+
-------------------
|
|
6
|
+
|
|
7
|
+
**Bugfixes**
|
|
8
|
+
|
|
9
|
+
* [#196] Fix errors raised when using ``open-api-framework`` without optional dependencies
|
|
10
|
+
|
|
11
|
+
**Project maintenance**
|
|
12
|
+
|
|
13
|
+
* Remove Django 4.2 from CI (pyproject.toml enforces >=5.2)
|
|
14
|
+
|
|
4
15
|
0.13.2 (2025-11-13)
|
|
5
16
|
-------------------
|
|
6
17
|
|
{open_api_framework-0.13.2/open_api_framework.egg-info → open_api_framework-0.13.3}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: open-api-framework
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.3
|
|
4
4
|
Summary: A metapackage for registration components, that bundles the dependencies shared between these components and provides generic settings
|
|
5
5
|
Author-email: Maykin Media <support@maykinmedia.nl>
|
|
6
6
|
License: Copyright © Maykin 2024
|
|
@@ -96,7 +96,7 @@ Dynamic: license-file
|
|
|
96
96
|
Open API Framework
|
|
97
97
|
==================
|
|
98
98
|
|
|
99
|
-
:Version: 0.13.
|
|
99
|
+
:Version: 0.13.3
|
|
100
100
|
:Source: https://github.com/maykinmedia/open-api-framework
|
|
101
101
|
:Keywords: metapackage, dependencies
|
|
102
102
|
|
|
@@ -1,30 +1,40 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import os
|
|
3
3
|
import warnings
|
|
4
|
-
from
|
|
4
|
+
from contextlib import suppress
|
|
5
|
+
from importlib.util import find_spec
|
|
5
6
|
|
|
6
7
|
from django.urls import reverse_lazy
|
|
7
8
|
|
|
8
9
|
import sentry_sdk
|
|
9
|
-
from corsheaders.defaults import default_headers as default_cors_headers
|
|
10
|
-
from csp.constants import NONCE, NONE, SELF
|
|
11
10
|
from log_outgoing_requests.formatters import HttpFormatter
|
|
12
|
-
from notifications_api_common.settings import * # noqa
|
|
13
11
|
|
|
14
12
|
from .utils import (
|
|
15
13
|
config,
|
|
16
14
|
get_django_project_dir,
|
|
17
15
|
get_project_dirname,
|
|
18
16
|
get_sentry_integrations,
|
|
17
|
+
importable,
|
|
19
18
|
strip_protocol_from_origin,
|
|
20
19
|
)
|
|
21
20
|
|
|
21
|
+
# optional requirements
|
|
22
|
+
default_cors_headers = []
|
|
23
|
+
with suppress(ImportError):
|
|
24
|
+
from corsheaders.defaults import default_headers as default_cors_headers
|
|
25
|
+
|
|
26
|
+
csp_installed = False
|
|
27
|
+
with suppress(ImportError):
|
|
28
|
+
from csp.constants import NONCE, NONE, SELF
|
|
29
|
+
|
|
30
|
+
csp_installed = True
|
|
31
|
+
|
|
22
32
|
PROJECT_DIRNAME = get_project_dirname()
|
|
23
33
|
|
|
24
34
|
# Build paths inside the project, so further paths can be defined relative to
|
|
25
35
|
# the code root.
|
|
26
36
|
DJANGO_PROJECT_DIR = get_django_project_dir()
|
|
27
|
-
BASE_DIR =
|
|
37
|
+
BASE_DIR = DJANGO_PROJECT_DIR.resolve().parents[1]
|
|
28
38
|
|
|
29
39
|
|
|
30
40
|
#
|
|
@@ -307,53 +317,55 @@ if DB_POOL_ENABLED:
|
|
|
307
317
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-DEFAULT_AUTO_FIELD
|
|
308
318
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
|
309
319
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
320
|
+
if find_spec("django_redis"):
|
|
321
|
+
CACHE_DEFAULT = config(
|
|
322
|
+
"CACHE_DEFAULT",
|
|
323
|
+
"localhost:6379/0",
|
|
324
|
+
help_text="redis cache address for the default cache (this **MUST** be set when using Docker)",
|
|
325
|
+
group="Required",
|
|
326
|
+
)
|
|
327
|
+
CACHE_AXES = config(
|
|
328
|
+
"CACHE_AXES",
|
|
329
|
+
"localhost:6379/0",
|
|
330
|
+
help_text=(
|
|
331
|
+
"redis cache address for the brute force login protection cache "
|
|
332
|
+
"(this **MUST** be set when using Docker)"
|
|
333
|
+
),
|
|
334
|
+
group="Required",
|
|
335
|
+
)
|
|
325
336
|
|
|
326
|
-
CACHES = {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
337
|
+
CACHES = {
|
|
338
|
+
"default": {
|
|
339
|
+
"BACKEND": "django_redis.cache.RedisCache",
|
|
340
|
+
"LOCATION": f"redis://{CACHE_DEFAULT}",
|
|
341
|
+
"OPTIONS": {
|
|
342
|
+
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
343
|
+
"IGNORE_EXCEPTIONS": True,
|
|
344
|
+
},
|
|
333
345
|
},
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
346
|
+
"axes": {
|
|
347
|
+
"BACKEND": "django_redis.cache.RedisCache",
|
|
348
|
+
"LOCATION": f"redis://{CACHE_AXES}",
|
|
349
|
+
"OPTIONS": {
|
|
350
|
+
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
351
|
+
"IGNORE_EXCEPTIONS": True,
|
|
352
|
+
},
|
|
341
353
|
},
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
354
|
+
"oidc": {
|
|
355
|
+
"BACKEND": "django_redis.cache.RedisCache",
|
|
356
|
+
"LOCATION": f"redis://{CACHE_DEFAULT}",
|
|
357
|
+
"OPTIONS": {
|
|
358
|
+
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
359
|
+
"IGNORE_EXCEPTIONS": True,
|
|
360
|
+
},
|
|
349
361
|
},
|
|
350
|
-
}
|
|
351
|
-
|
|
362
|
+
}
|
|
363
|
+
|
|
352
364
|
|
|
353
365
|
#
|
|
354
366
|
# APPLICATIONS enabled for this project
|
|
355
367
|
#
|
|
356
|
-
INSTALLED_APPS =
|
|
368
|
+
INSTALLED_APPS = importable(
|
|
357
369
|
# Note: contenttypes should be first, see Django ticket #10827
|
|
358
370
|
"django.contrib.contenttypes",
|
|
359
371
|
"django.contrib.auth",
|
|
@@ -396,9 +408,9 @@ INSTALLED_APPS = [
|
|
|
396
408
|
PROJECT_DIRNAME,
|
|
397
409
|
# Django libraries
|
|
398
410
|
"upgrade_check",
|
|
399
|
-
|
|
411
|
+
)
|
|
400
412
|
|
|
401
|
-
MIDDLEWARE =
|
|
413
|
+
MIDDLEWARE = importable(
|
|
402
414
|
"django.middleware.security.SecurityMiddleware",
|
|
403
415
|
"sessionprofile.middleware.SessionProfileMiddleware",
|
|
404
416
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
@@ -411,7 +423,7 @@ MIDDLEWARE = [
|
|
|
411
423
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
412
424
|
"axes.middleware.AxesMiddleware",
|
|
413
425
|
"csp.contrib.rate_limiting.RateLimitedCSPMiddleware",
|
|
414
|
-
|
|
426
|
+
)
|
|
415
427
|
|
|
416
428
|
ROOT_URLCONF = f"{PROJECT_DIRNAME}.urls"
|
|
417
429
|
|
|
@@ -424,7 +436,7 @@ TEMPLATE_LOADERS = (
|
|
|
424
436
|
TEMPLATES = [
|
|
425
437
|
{
|
|
426
438
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|
427
|
-
"DIRS": [
|
|
439
|
+
"DIRS": [DJANGO_PROJECT_DIR / "templates"],
|
|
428
440
|
"APP_DIRS": False, # conflicts with explicity specifying the loaders
|
|
429
441
|
"OPTIONS": {
|
|
430
442
|
"context_processors": [
|
|
@@ -443,7 +455,7 @@ TEMPLATES = [
|
|
|
443
455
|
WSGI_APPLICATION = f"{PROJECT_DIRNAME}.wsgi.application"
|
|
444
456
|
|
|
445
457
|
# Translations
|
|
446
|
-
LOCALE_PATHS = (
|
|
458
|
+
LOCALE_PATHS = (DJANGO_PROJECT_DIR / "conf" / "locale",)
|
|
447
459
|
|
|
448
460
|
#
|
|
449
461
|
# SERVING of static and media files
|
|
@@ -451,10 +463,10 @@ LOCALE_PATHS = (Path(DJANGO_PROJECT_DIR) / "conf" / "locale",)
|
|
|
451
463
|
|
|
452
464
|
STATIC_URL = "/static/"
|
|
453
465
|
|
|
454
|
-
STATIC_ROOT =
|
|
466
|
+
STATIC_ROOT = BASE_DIR / "static"
|
|
455
467
|
|
|
456
468
|
# Additional locations of static files
|
|
457
|
-
STATICFILES_DIRS = [
|
|
469
|
+
STATICFILES_DIRS = [DJANGO_PROJECT_DIR / "static"]
|
|
458
470
|
|
|
459
471
|
# List of finder classes that know how to find static files in
|
|
460
472
|
# various locations.
|
|
@@ -463,7 +475,7 @@ STATICFILES_FINDERS = [
|
|
|
463
475
|
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
|
464
476
|
]
|
|
465
477
|
|
|
466
|
-
MEDIA_ROOT =
|
|
478
|
+
MEDIA_ROOT = BASE_DIR / "media"
|
|
467
479
|
|
|
468
480
|
MEDIA_URL = "/media/"
|
|
469
481
|
|
|
@@ -568,6 +580,7 @@ CELERY_LOGLEVEL = config(
|
|
|
568
580
|
help_text="control the verbosity of logging output for celery, independent of ``LOG_LEVEL``."
|
|
569
581
|
" Available values are ``CRITICAL``, ``ERROR``, ``WARNING``, ``INFO`` and ``DEBUG``",
|
|
570
582
|
group="Celery",
|
|
583
|
+
add_to_docs="celery",
|
|
571
584
|
)
|
|
572
585
|
|
|
573
586
|
_USE_STRUCTLOG = config("_USE_STRUCTLOG", default=False, add_to_docs=False)
|
|
@@ -578,10 +591,11 @@ ENABLE_STRUCTLOG_REQUESTS = config(
|
|
|
578
591
|
default=True,
|
|
579
592
|
help_text=("enable structured logging of requests"),
|
|
580
593
|
group="Logging",
|
|
594
|
+
add_to_docs="django_structlog",
|
|
581
595
|
)
|
|
582
596
|
|
|
583
597
|
|
|
584
|
-
LOGGING_DIR =
|
|
598
|
+
LOGGING_DIR = BASE_DIR / "log"
|
|
585
599
|
|
|
586
600
|
if _USE_STRUCTLOG:
|
|
587
601
|
import structlog
|
|
@@ -668,7 +682,7 @@ if _USE_STRUCTLOG:
|
|
|
668
682
|
"json_file": {
|
|
669
683
|
"level": LOG_LEVEL, # always debug might be better?
|
|
670
684
|
"class": "logging.handlers.RotatingFileHandler",
|
|
671
|
-
"filename":
|
|
685
|
+
"filename": LOGGING_DIR / "application.jsonl",
|
|
672
686
|
"formatter": "json",
|
|
673
687
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
674
688
|
"backupCount": 10,
|
|
@@ -676,7 +690,7 @@ if _USE_STRUCTLOG:
|
|
|
676
690
|
"performance": {
|
|
677
691
|
"level": "INFO",
|
|
678
692
|
"class": "logging.handlers.RotatingFileHandler",
|
|
679
|
-
"filename":
|
|
693
|
+
"filename": LOGGING_DIR / "performance.log",
|
|
680
694
|
"formatter": "performance",
|
|
681
695
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
682
696
|
"backupCount": 10,
|
|
@@ -684,7 +698,7 @@ if _USE_STRUCTLOG:
|
|
|
684
698
|
"requests": {
|
|
685
699
|
"level": "DEBUG",
|
|
686
700
|
"class": "logging.handlers.RotatingFileHandler",
|
|
687
|
-
"filename":
|
|
701
|
+
"filename": LOGGING_DIR / "requests.log",
|
|
688
702
|
"formatter": "timestamped",
|
|
689
703
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
690
704
|
"backupCount": 10,
|
|
@@ -825,23 +839,10 @@ else:
|
|
|
825
839
|
"class": "logging.StreamHandler",
|
|
826
840
|
"formatter": "db",
|
|
827
841
|
},
|
|
828
|
-
"celery_console": {
|
|
829
|
-
"level": CELERY_LOGLEVEL,
|
|
830
|
-
"class": "logging.StreamHandler",
|
|
831
|
-
"formatter": "timestamped",
|
|
832
|
-
},
|
|
833
|
-
"celery_file": {
|
|
834
|
-
"level": CELERY_LOGLEVEL,
|
|
835
|
-
"class": "logging.handlers.RotatingFileHandler",
|
|
836
|
-
"filename": Path(LOGGING_DIR) / "celery.log",
|
|
837
|
-
"formatter": "verbose",
|
|
838
|
-
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
839
|
-
"backupCount": 10,
|
|
840
|
-
},
|
|
841
842
|
"django": {
|
|
842
843
|
"level": LOG_LEVEL,
|
|
843
844
|
"class": "logging.handlers.RotatingFileHandler",
|
|
844
|
-
"filename":
|
|
845
|
+
"filename": LOGGING_DIR / "django.log",
|
|
845
846
|
"formatter": "verbose",
|
|
846
847
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
847
848
|
"backupCount": 10,
|
|
@@ -849,7 +850,7 @@ else:
|
|
|
849
850
|
"project": {
|
|
850
851
|
"level": LOG_LEVEL,
|
|
851
852
|
"class": "logging.handlers.RotatingFileHandler",
|
|
852
|
-
"filename":
|
|
853
|
+
"filename": LOGGING_DIR / f"{PROJECT_DIRNAME}.log",
|
|
853
854
|
"formatter": "verbose",
|
|
854
855
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
855
856
|
"backupCount": 10,
|
|
@@ -857,7 +858,7 @@ else:
|
|
|
857
858
|
"performance": {
|
|
858
859
|
"level": "INFO",
|
|
859
860
|
"class": "logging.handlers.RotatingFileHandler",
|
|
860
|
-
"filename":
|
|
861
|
+
"filename": LOGGING_DIR / "performance.log",
|
|
861
862
|
"formatter": "performance",
|
|
862
863
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
863
864
|
"backupCount": 10,
|
|
@@ -865,7 +866,7 @@ else:
|
|
|
865
866
|
"requests": {
|
|
866
867
|
"level": "DEBUG",
|
|
867
868
|
"class": "logging.handlers.RotatingFileHandler",
|
|
868
|
-
"filename":
|
|
869
|
+
"filename": LOGGING_DIR / "requests.log",
|
|
869
870
|
"formatter": "timestamped",
|
|
870
871
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
871
872
|
"backupCount": 10,
|
|
@@ -880,7 +881,26 @@ else:
|
|
|
880
881
|
# enabling saving to database
|
|
881
882
|
"class": "log_outgoing_requests.handlers.DatabaseOutgoingRequestsHandler",
|
|
882
883
|
},
|
|
883
|
-
}
|
|
884
|
+
}
|
|
885
|
+
| ( # celery dependant handlers
|
|
886
|
+
{
|
|
887
|
+
"celery_console": {
|
|
888
|
+
"level": CELERY_LOGLEVEL,
|
|
889
|
+
"class": "logging.StreamHandler",
|
|
890
|
+
"formatter": "timestamped",
|
|
891
|
+
},
|
|
892
|
+
"celery_file": {
|
|
893
|
+
"level": CELERY_LOGLEVEL,
|
|
894
|
+
"class": "logging.handlers.RotatingFileHandler",
|
|
895
|
+
"filename": LOGGING_DIR / "celery.log",
|
|
896
|
+
"formatter": "verbose",
|
|
897
|
+
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
898
|
+
"backupCount": 10,
|
|
899
|
+
},
|
|
900
|
+
}
|
|
901
|
+
if find_spec("celery")
|
|
902
|
+
else {}
|
|
903
|
+
),
|
|
884
904
|
"loggers": {
|
|
885
905
|
"": {
|
|
886
906
|
"handlers": logging_root_handlers,
|
|
@@ -930,18 +950,25 @@ else:
|
|
|
930
950
|
"level": "DEBUG",
|
|
931
951
|
"propagate": True,
|
|
932
952
|
},
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
"
|
|
937
|
-
|
|
938
|
-
|
|
953
|
+
}
|
|
954
|
+
| (
|
|
955
|
+
{
|
|
956
|
+
"celery": {
|
|
957
|
+
"handlers": ["celery_console"] if LOG_STDOUT else ["celery_file"],
|
|
958
|
+
"level": CELERY_LOGLEVEL,
|
|
959
|
+
"propagate": True,
|
|
960
|
+
},
|
|
961
|
+
}
|
|
962
|
+
if find_spec("celery")
|
|
963
|
+
else {}
|
|
964
|
+
),
|
|
939
965
|
}
|
|
940
966
|
|
|
941
967
|
#
|
|
942
968
|
# AUTH settings - user accounts, passwords, backends...
|
|
943
969
|
#
|
|
944
|
-
|
|
970
|
+
if find_spec(f"{PROJECT_DIRNAME}.accounts"):
|
|
971
|
+
AUTH_USER_MODEL = "accounts.User"
|
|
945
972
|
|
|
946
973
|
# Password validation
|
|
947
974
|
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
|
|
@@ -1053,7 +1080,7 @@ if subpath:
|
|
|
1053
1080
|
if "GIT_SHA" in os.environ:
|
|
1054
1081
|
GIT_SHA = config("GIT_SHA", "", add_to_docs=False)
|
|
1055
1082
|
# in docker (build) context, there is no .git directory
|
|
1056
|
-
elif (
|
|
1083
|
+
elif (BASE_DIR / ".git").exists():
|
|
1057
1084
|
try:
|
|
1058
1085
|
import git
|
|
1059
1086
|
except ImportError:
|
|
@@ -1133,6 +1160,7 @@ CORS_ALLOW_ALL_ORIGINS = config(
|
|
|
1133
1160
|
default=False,
|
|
1134
1161
|
group="Cross-Origin-Resource-Sharing",
|
|
1135
1162
|
help_text="allow cross-domain access from any client",
|
|
1163
|
+
add_to_docs="corsheaders",
|
|
1136
1164
|
)
|
|
1137
1165
|
CORS_ALLOWED_ORIGINS = config(
|
|
1138
1166
|
"CORS_ALLOWED_ORIGINS",
|
|
@@ -1143,6 +1171,7 @@ CORS_ALLOWED_ORIGINS = config(
|
|
|
1143
1171
|
"explicitly list the allowed origins for cross-domain requests. "
|
|
1144
1172
|
"Example: http://localhost:3000,https://some-app.gemeente.nl"
|
|
1145
1173
|
),
|
|
1174
|
+
add_to_docs="corsheaders",
|
|
1146
1175
|
)
|
|
1147
1176
|
CORS_ALLOWED_ORIGIN_REGEXES = config(
|
|
1148
1177
|
"CORS_ALLOWED_ORIGIN_REGEXES",
|
|
@@ -1150,7 +1179,9 @@ CORS_ALLOWED_ORIGIN_REGEXES = config(
|
|
|
1150
1179
|
default=[],
|
|
1151
1180
|
group="Cross-Origin-Resource-Sharing",
|
|
1152
1181
|
help_text="same as ``CORS_ALLOWED_ORIGINS``, but supports regular expressions",
|
|
1182
|
+
add_to_docs="corsheaders",
|
|
1153
1183
|
)
|
|
1184
|
+
|
|
1154
1185
|
# Authorization is included in default_cors_headers
|
|
1155
1186
|
CORS_ALLOW_HEADERS = (
|
|
1156
1187
|
list(default_cors_headers)
|
|
@@ -1168,8 +1199,10 @@ CORS_ALLOW_HEADERS = (
|
|
|
1168
1199
|
"By default, Authorization, Accept-Crs and Content-Crs are already included. "
|
|
1169
1200
|
"The value of this variable is added to these already included headers."
|
|
1170
1201
|
),
|
|
1202
|
+
add_to_docs="corsheaders",
|
|
1171
1203
|
)
|
|
1172
1204
|
)
|
|
1205
|
+
|
|
1173
1206
|
CORS_EXPOSE_HEADERS = [
|
|
1174
1207
|
"content-crs",
|
|
1175
1208
|
]
|
|
@@ -1190,7 +1223,7 @@ CSRF_TRUSTED_ORIGINS = config(
|
|
|
1190
1223
|
#
|
|
1191
1224
|
# DJANGO-PRIVATES -- safely serve files after authorization
|
|
1192
1225
|
#
|
|
1193
|
-
PRIVATE_MEDIA_ROOT =
|
|
1226
|
+
PRIVATE_MEDIA_ROOT = BASE_DIR / "private-media"
|
|
1194
1227
|
PRIVATE_MEDIA_URL = "/private-media/"
|
|
1195
1228
|
|
|
1196
1229
|
|
|
@@ -1392,54 +1425,72 @@ LOG_OUTGOING_REQUESTS_MAX_AGE = config(
|
|
|
1392
1425
|
|
|
1393
1426
|
def get_content_security_policy():
|
|
1394
1427
|
# ideally we'd use BASE_URI but it'd have to be lazy or cause issues
|
|
1395
|
-
|
|
1428
|
+
extra_default_src = config(
|
|
1396
1429
|
"CSP_EXTRA_DEFAULT_SRC",
|
|
1397
1430
|
default=[],
|
|
1398
1431
|
split=True,
|
|
1399
1432
|
group="Content Security Policy",
|
|
1400
1433
|
help_text="Extra default source URLs for CSP other than ``self``. Used for ``img-src``, ``style-src`` and ``script-src``.",
|
|
1434
|
+
add_to_docs="csp",
|
|
1435
|
+
)
|
|
1436
|
+
extra_form_action = config(
|
|
1437
|
+
"CSP_EXTRA_FORM_ACTION",
|
|
1438
|
+
default=[],
|
|
1439
|
+
split=True,
|
|
1440
|
+
group="Content Security Policy",
|
|
1441
|
+
help_text="Additional `form-action` sources.",
|
|
1442
|
+
add_to_docs="csp",
|
|
1401
1443
|
)
|
|
1444
|
+
form_action = config(
|
|
1445
|
+
"CSP_FORM_ACTION",
|
|
1446
|
+
default=["\"'self'\""] + extra_form_action,
|
|
1447
|
+
split=True,
|
|
1448
|
+
group="Content Security Policy",
|
|
1449
|
+
help_text="Override the default `form-action` sources.",
|
|
1450
|
+
add_to_docs="csp",
|
|
1451
|
+
)
|
|
1452
|
+
extra_img_src = config(
|
|
1453
|
+
"CSP_EXTRA_IMG_SRC",
|
|
1454
|
+
default=[],
|
|
1455
|
+
split=True,
|
|
1456
|
+
group="Content Security Policy",
|
|
1457
|
+
help_text="Extra `img-src` sources.",
|
|
1458
|
+
add_to_docs="csp",
|
|
1459
|
+
)
|
|
1460
|
+
object_src = config(
|
|
1461
|
+
"CSP_OBJECT_SRC",
|
|
1462
|
+
default=["\"'none'\""],
|
|
1463
|
+
split=True,
|
|
1464
|
+
group="Content Security Policy",
|
|
1465
|
+
help_text="`object-src` sources.",
|
|
1466
|
+
add_to_docs="csp",
|
|
1467
|
+
)
|
|
1468
|
+
report_uri = config(
|
|
1469
|
+
"CSP_REPORT_URI",
|
|
1470
|
+
None,
|
|
1471
|
+
group="Content Security Policy",
|
|
1472
|
+
help_text="URI for CSP report-uri directive.",
|
|
1473
|
+
add_to_docs="csp",
|
|
1474
|
+
)
|
|
1475
|
+
report_percentage = config(
|
|
1476
|
+
"CSP_REPORT_PERCENTAGE",
|
|
1477
|
+
0.0,
|
|
1478
|
+
group="Content Security Policy",
|
|
1479
|
+
help_text="Fraction (between 0 and 1) of requests to include report-uri directive.",
|
|
1480
|
+
add_to_docs="csp",
|
|
1481
|
+
)
|
|
1482
|
+
|
|
1483
|
+
if not csp_installed:
|
|
1484
|
+
return {}
|
|
1485
|
+
|
|
1486
|
+
csp_default_src = [SELF] + extra_default_src
|
|
1402
1487
|
return {
|
|
1403
1488
|
"DIRECTIVES": {
|
|
1404
|
-
"default-src":
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
group="Content Security Policy",
|
|
1410
|
-
help_text="Extra default source URLs for CSP other than ``self``. Used for ``img-src``, ``style-src`` and ``script-src``.",
|
|
1411
|
-
),
|
|
1412
|
-
"form-action": config(
|
|
1413
|
-
"CSP_FORM_ACTION",
|
|
1414
|
-
default=["\"'self'\""]
|
|
1415
|
-
+ config(
|
|
1416
|
-
"CSP_EXTRA_FORM_ACTION",
|
|
1417
|
-
default=[],
|
|
1418
|
-
split=True,
|
|
1419
|
-
group="Content Security Policy",
|
|
1420
|
-
help_text="Additional `form-action` sources.",
|
|
1421
|
-
),
|
|
1422
|
-
split=True,
|
|
1423
|
-
group="Content Security Policy",
|
|
1424
|
-
help_text="Override the default `form-action` sources.",
|
|
1425
|
-
)
|
|
1426
|
-
+ CORS_ALLOWED_ORIGINS,
|
|
1427
|
-
"img-src": csp_default_src
|
|
1428
|
-
+ ["data:", "cdn.redoc.ly"]
|
|
1429
|
-
+ config(
|
|
1430
|
-
"CSP_EXTRA_IMG_SRC",
|
|
1431
|
-
default=[],
|
|
1432
|
-
split=True,
|
|
1433
|
-
group="Content Security Policy",
|
|
1434
|
-
help_text="Extra `img-src` sources.",
|
|
1435
|
-
),
|
|
1436
|
-
"object-src": config(
|
|
1437
|
-
"CSP_OBJECT_SRC",
|
|
1438
|
-
default=["\"'none'\""],
|
|
1439
|
-
split=True,
|
|
1440
|
-
group="Content Security Policy",
|
|
1441
|
-
help_text="`object-src` sources.",
|
|
1442
|
-
),
|
|
1489
|
+
"default-src": csp_default_src,
|
|
1490
|
+
"form-action": form_action
|
|
1491
|
+
+ CORS_ALLOWED_ORIGINS, # XXX: not passed as default to prevent misconfig??
|
|
1492
|
+
"img-src": csp_default_src + ["data:", "cdn.redoc.ly"] + extra_img_src,
|
|
1493
|
+
"object-src": object_src,
|
|
1443
1494
|
"style-src": csp_default_src
|
|
1444
1495
|
+ [NONCE, "'unsafe-inline'", "fonts.googleapis.com"],
|
|
1445
1496
|
"script-src": csp_default_src + [NONCE, "'unsafe-inline'"],
|
|
@@ -1449,22 +1500,11 @@ def get_content_security_policy():
|
|
|
1449
1500
|
"frame-ancestors": [NONE],
|
|
1450
1501
|
"frame-src": [SELF],
|
|
1451
1502
|
"upgrade-insecure-requests": False, # Enable only in production
|
|
1452
|
-
"report-uri":
|
|
1453
|
-
"CSP_REPORT_URI",
|
|
1454
|
-
None,
|
|
1455
|
-
group="Content Security Policy",
|
|
1456
|
-
help_text="URI for CSP report-uri directive.",
|
|
1457
|
-
),
|
|
1503
|
+
"report-uri": report_uri,
|
|
1458
1504
|
},
|
|
1459
1505
|
# Envvar used for django-csp==3.8 was a float between 0 and 1, while django-csp==4.0
|
|
1460
1506
|
# expects a percentage (between 0 and 100)
|
|
1461
|
-
"REPORT_PERCENTAGE":
|
|
1462
|
-
"CSP_REPORT_PERCENTAGE",
|
|
1463
|
-
0.0,
|
|
1464
|
-
group="Content Security Policy",
|
|
1465
|
-
help_text="Fraction (between 0 and 1) of requests to include report-uri directive.",
|
|
1466
|
-
)
|
|
1467
|
-
* 100,
|
|
1507
|
+
"REPORT_PERCENTAGE": report_percentage * 100,
|
|
1468
1508
|
}
|
|
1469
1509
|
|
|
1470
1510
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import logging # noqa: TID251
|
|
2
2
|
import sys
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
+
from importlib.util import find_spec
|
|
4
5
|
from pathlib import Path
|
|
5
|
-
from typing import Any, Optional
|
|
6
|
+
from typing import Any, Optional, TypeVar, assert_never
|
|
6
7
|
from urllib.parse import urlparse
|
|
8
|
+
from warnings import warn
|
|
7
9
|
|
|
8
10
|
from decouple import Csv, Undefined, config as _config, undefined
|
|
9
|
-
from sentry_sdk.integrations import DidNotEnable, django, redis
|
|
11
|
+
from sentry_sdk.integrations import DidNotEnable, Integration, django, redis
|
|
10
12
|
from sentry_sdk.integrations.logging import LoggingIntegration
|
|
11
13
|
|
|
12
14
|
|
|
@@ -30,17 +32,19 @@ class EnvironmentVariable:
|
|
|
30
32
|
|
|
31
33
|
ENVVAR_REGISTRY = []
|
|
32
34
|
|
|
35
|
+
_T = TypeVar("_T")
|
|
36
|
+
|
|
33
37
|
|
|
34
38
|
def config(
|
|
35
39
|
option: str,
|
|
36
|
-
default:
|
|
40
|
+
default: _T = undefined,
|
|
37
41
|
help_text="",
|
|
38
42
|
group=None,
|
|
39
|
-
add_to_docs=True,
|
|
43
|
+
add_to_docs: str | bool = True,
|
|
40
44
|
auto_display_default=True,
|
|
41
45
|
*args,
|
|
42
46
|
**kwargs,
|
|
43
|
-
):
|
|
47
|
+
) -> _T:
|
|
44
48
|
"""
|
|
45
49
|
An override of ``decouple.config``, with custom options to construct documentation
|
|
46
50
|
for environment variables.
|
|
@@ -59,18 +63,21 @@ def config(
|
|
|
59
63
|
:param help_text: The help text to be displayed for this variable in the documentation. Default `""`
|
|
60
64
|
:param group: The name of the section under which this variable will be grouped. Default ``None``
|
|
61
65
|
:param add_to_docs: Whether or not this variable will be displayed in the documentation. Default ``True``
|
|
66
|
+
If a string is passed, it will only be displayed if it is importable as a module,
|
|
67
|
+
and will raise a Warning when it is still passed in from the environment.
|
|
62
68
|
:param auto_display_default: Whether or not the passed ``default`` value is displayed in the docs, this can be
|
|
63
69
|
set to ``False`` in case a default needs more explanation that can be added to the ``help_text``
|
|
64
70
|
(e.g. if it is computed or based on another variable). Default ``True``
|
|
65
71
|
"""
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
variable = EnvironmentVariable(
|
|
73
|
+
name=option,
|
|
74
|
+
default=default,
|
|
75
|
+
help_text=help_text,
|
|
76
|
+
group=group,
|
|
77
|
+
auto_display_default=auto_display_default,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def document():
|
|
74
81
|
if variable not in ENVVAR_REGISTRY:
|
|
75
82
|
ENVVAR_REGISTRY.append(variable)
|
|
76
83
|
else:
|
|
@@ -85,19 +92,44 @@ def config(
|
|
|
85
92
|
|
|
86
93
|
if default is not undefined and default is not None:
|
|
87
94
|
kwargs.setdefault("cast", type(default))
|
|
88
|
-
|
|
95
|
+
|
|
96
|
+
value = _config(option, default=default, *args, **kwargs)
|
|
97
|
+
|
|
98
|
+
match add_to_docs:
|
|
99
|
+
case str(module) if find_spec(module):
|
|
100
|
+
document()
|
|
101
|
+
case str(module):
|
|
102
|
+
if value is not default:
|
|
103
|
+
warn(
|
|
104
|
+
f"{variable.name} found, but required {add_to_docs} is not installed",
|
|
105
|
+
RuntimeWarning,
|
|
106
|
+
)
|
|
107
|
+
case True:
|
|
108
|
+
document()
|
|
109
|
+
case False:
|
|
110
|
+
pass
|
|
111
|
+
case _:
|
|
112
|
+
assert_never(add_to_docs)
|
|
113
|
+
|
|
114
|
+
return value # type: ignore
|
|
89
115
|
|
|
90
116
|
|
|
91
|
-
def
|
|
117
|
+
def importable(*items: str) -> list[str]:
|
|
118
|
+
"Return the dotted paths that start from an installed package"
|
|
119
|
+
|
|
120
|
+
split_items = (item.split(".") for item in items)
|
|
121
|
+
return [".".join(item) for item in split_items if find_spec(item[0])]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_sentry_integrations() -> list[Integration]:
|
|
92
125
|
"""
|
|
93
126
|
Determine which Sentry SDK integrations to enable.
|
|
94
127
|
"""
|
|
95
|
-
default = [
|
|
96
|
-
django.DjangoIntegration(),
|
|
97
|
-
redis.RedisIntegration(),
|
|
98
|
-
]
|
|
99
128
|
extra = []
|
|
100
129
|
|
|
130
|
+
if find_spec("redis"): # does not raise DidNotEnable if redis is not installed
|
|
131
|
+
extra.append(redis.RedisIntegration())
|
|
132
|
+
|
|
101
133
|
try:
|
|
102
134
|
from sentry_sdk.integrations import celery
|
|
103
135
|
except DidNotEnable: # happens if the celery import fails by the integration
|
|
@@ -105,11 +137,7 @@ def get_sentry_integrations() -> list:
|
|
|
105
137
|
else:
|
|
106
138
|
extra.append(celery.CeleryIntegration())
|
|
107
139
|
|
|
108
|
-
|
|
109
|
-
import structlog # type: ignore # noqa
|
|
110
|
-
except ImportError:
|
|
111
|
-
pass
|
|
112
|
-
else:
|
|
140
|
+
if find_spec("structlog"):
|
|
113
141
|
extra.append(
|
|
114
142
|
LoggingIntegration(
|
|
115
143
|
level=logging.INFO, # breadcrumbs
|
|
@@ -119,7 +147,7 @@ def get_sentry_integrations() -> list:
|
|
|
119
147
|
),
|
|
120
148
|
)
|
|
121
149
|
|
|
122
|
-
return [
|
|
150
|
+
return [django.DjangoIntegration(), *extra]
|
|
123
151
|
|
|
124
152
|
|
|
125
153
|
def strip_protocol_from_origin(origin: str) -> str:
|
|
@@ -131,10 +159,10 @@ def get_project_dirname() -> str:
|
|
|
131
159
|
return config("DJANGO_SETTINGS_MODULE", add_to_docs=False).split(".")[0]
|
|
132
160
|
|
|
133
161
|
|
|
134
|
-
def get_django_project_dir() ->
|
|
162
|
+
def get_django_project_dir() -> Path:
|
|
135
163
|
# Get the path of the importing module
|
|
136
164
|
base_dirname = get_project_dirname()
|
|
137
|
-
return Path(sys.modules[base_dirname].__file__).parent
|
|
165
|
+
return Path(sys.modules[base_dirname].__file__).parent # pyright: ignore[reportArgumentType]
|
|
138
166
|
|
|
139
167
|
|
|
140
168
|
def mute_logging(config: dict) -> None: # pragma: no cover
|
{open_api_framework-0.13.2 → open_api_framework-0.13.3/open_api_framework.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: open-api-framework
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.3
|
|
4
4
|
Summary: A metapackage for registration components, that bundles the dependencies shared between these components and provides generic settings
|
|
5
5
|
Author-email: Maykin Media <support@maykinmedia.nl>
|
|
6
6
|
License: Copyright © Maykin 2024
|
|
@@ -96,7 +96,7 @@ Dynamic: license-file
|
|
|
96
96
|
Open API Framework
|
|
97
97
|
==================
|
|
98
98
|
|
|
99
|
-
:Version: 0.13.
|
|
99
|
+
:Version: 0.13.3
|
|
100
100
|
:Source: https://github.com/maykinmedia/open-api-framework
|
|
101
101
|
:Keywords: metapackage, dependencies
|
|
102
102
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "open-api-framework"
|
|
7
|
-
version = "0.13.
|
|
7
|
+
version = "0.13.3"
|
|
8
8
|
description = "A metapackage for registration components, that bundles the dependencies shared between these components and provides generic settings"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Maykin Media", email = "support@maykinmedia.nl"}
|
|
@@ -129,7 +129,7 @@ env = [
|
|
|
129
129
|
]
|
|
130
130
|
|
|
131
131
|
[tool.bumpversion]
|
|
132
|
-
current_version = "0.13.
|
|
132
|
+
current_version = "0.13.3"
|
|
133
133
|
files = [
|
|
134
134
|
{filename = "pyproject.toml"},
|
|
135
135
|
{filename = "README.rst"},
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from open_api_framework.conf.utils import (
|
|
6
|
+
ENVVAR_REGISTRY,
|
|
7
|
+
config,
|
|
8
|
+
get_django_project_dir,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_empty_list_as_default():
|
|
13
|
+
value = config("SOME_TEST_ENVVAR", split=True, default=[], add_to_docs=False)
|
|
14
|
+
|
|
15
|
+
assert value == []
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_non_empty_list_as_default():
|
|
19
|
+
value = config("SOME_TEST_ENVVAR", split=True, default=["foo"], add_to_docs=False)
|
|
20
|
+
|
|
21
|
+
assert value == ["foo"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_string_list_from_env(monkeypatch):
|
|
25
|
+
monkeypatch.setenv("SOME_TEST_ENVVAR", "foo,bar")
|
|
26
|
+
|
|
27
|
+
value = config("SOME_TEST_ENVVAR", split=True, default=["foo"], add_to_docs=False)
|
|
28
|
+
|
|
29
|
+
assert value == ["foo", "bar"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_it_raises_warning_if_add_to_docs_module_is_not_present(monkeypatch):
|
|
33
|
+
monkeypatch.setenv("FOO_TEST_ENVVAR", "value")
|
|
34
|
+
with pytest.warns() as warnings:
|
|
35
|
+
value = config("FOO_TEST_ENVVAR", default="value", add_to_docs="foo_module")
|
|
36
|
+
assert value == "value"
|
|
37
|
+
assert not any(var.name == "FOO_TEST_VAR" for var in ENVVAR_REGISTRY)
|
|
38
|
+
|
|
39
|
+
# warning mentions key actionable info
|
|
40
|
+
assert "FOO_TEST_ENVVAR" in str(warnings[0])
|
|
41
|
+
assert "foo_module" in str(warnings[0])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_get_django_project_dir():
|
|
45
|
+
project_path = get_django_project_dir()
|
|
46
|
+
assert project_path.parts[-1] == "testapp"
|
|
47
|
+
|
|
48
|
+
# still compatible with os.path.join
|
|
49
|
+
settings = os.path.join(project_path, "settings.py")
|
|
50
|
+
assert os.path.exists(settings)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from importlib.util import find_spec
|
|
1
2
|
from unittest.mock import mock_open, patch
|
|
2
3
|
|
|
3
4
|
from django.core.management import call_command
|
|
@@ -160,6 +161,8 @@ to define the envvars. The component will pick them up out of the box.
|
|
|
160
161
|
|
|
161
162
|
def test_generate_envvar_docs():
|
|
162
163
|
mock_file = mock_open()
|
|
164
|
+
extras_installed = bool(find_spec("csp"))
|
|
165
|
+
|
|
163
166
|
with patch(
|
|
164
167
|
"open_api_framework.management.commands.generate_envvar_docs.open", mock_file
|
|
165
168
|
):
|
|
@@ -174,4 +177,8 @@ def test_generate_envvar_docs():
|
|
|
174
177
|
# Check the entire content written to the mock file
|
|
175
178
|
written_content = "".join(call.args[0] for call in handle.write.call_args_list)
|
|
176
179
|
|
|
177
|
-
|
|
180
|
+
if extras_installed:
|
|
181
|
+
assert written_content == EXPECTED_OUTPUT
|
|
182
|
+
else:
|
|
183
|
+
assert "Cross-Origin-Resource-Sharing" not in written_content
|
|
184
|
+
assert "Content Security Policy" not in written_content
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
from importlib.util import find_spec
|
|
2
|
+
|
|
1
3
|
from django.conf import settings
|
|
2
4
|
from django.urls import reverse
|
|
3
5
|
|
|
6
|
+
import pytest
|
|
4
7
|
from django_webtest import WebTest
|
|
5
8
|
|
|
6
9
|
|
|
10
|
+
@pytest.mark.skipif(not find_spec("sentry_sdk"), reason="No sentry installed")
|
|
7
11
|
def test_sentry_settings():
|
|
8
12
|
"""
|
|
9
13
|
test that sentry settings are initialized
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
from open_api_framework.conf.utils import config
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def test_empty_list_as_default():
|
|
5
|
-
value = config("SOME_TEST_ENVVAR", split=True, default=[], add_to_docs=False)
|
|
6
|
-
|
|
7
|
-
assert value == []
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def test_non_empty_list_as_default():
|
|
11
|
-
value = config("SOME_TEST_ENVVAR", split=True, default=["foo"], add_to_docs=False)
|
|
12
|
-
|
|
13
|
-
assert value == ["foo"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/context_processors.py
RENAMED
|
File without changes
|
{open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/management/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/templatetags/__init__.py
RENAMED
|
File without changes
|
{open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework/templatetags/doc_tags.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework.egg-info/requires.txt
RENAMED
|
File without changes
|
{open_api_framework-0.13.2 → open_api_framework-0.13.3}/open_api_framework.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|