open-api-framework 0.13.1__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.1 → open_api_framework-0.13.3}/CHANGELOG.rst +19 -0
- {open_api_framework-0.13.1/open_api_framework.egg-info → open_api_framework-0.13.3}/PKG-INFO +2 -2
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/README.rst +1 -1
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/conf/base.py +182 -137
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/conf/utils.py +55 -27
- {open_api_framework-0.13.1 → open_api_framework-0.13.3/open_api_framework.egg-info}/PKG-INFO +2 -2
- {open_api_framework-0.13.1 → 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.1 → open_api_framework-0.13.3}/tests/test_csp.py +7 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/tests/test_generate_envvar_docs.py +9 -2
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/tests/test_settings.py +4 -0
- open_api_framework-0.13.1/tests/test_config_helpers.py +0 -13
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/LICENSE +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/MANIFEST.in +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/__init__.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/admin.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/apps.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/conf/__init__.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/context_processors.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/management/__init__.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/management/commands/__init__.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/management/commands/generate_envvar_docs.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/py.typed +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/static/open_api_framework/css/admin.css +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/templates/open_api_framework/admin/base_site.html +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/templates/open_api_framework/components/environment.html +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/templates/open_api_framework/components/version.html +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/templates/open_api_framework/env_config.rst +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/templatetags/__init__.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/templatetags/doc_tags.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/templatetags/open_api_framework.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework/utils.py +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework.egg-info/SOURCES.txt +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework.egg-info/dependency_links.txt +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework.egg-info/requires.txt +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework.egg-info/top_level.txt +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/setup.cfg +0 -0
- {open_api_framework-0.13.1 → open_api_framework-0.13.3}/tests/test_admin.py +0 -0
|
@@ -1,6 +1,25 @@
|
|
|
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
|
+
|
|
15
|
+
0.13.2 (2025-11-13)
|
|
16
|
+
-------------------
|
|
17
|
+
|
|
18
|
+
**Documentation**
|
|
19
|
+
|
|
20
|
+
* Updated the help text of ``DB_POOL_ENABLED`` to indicate that connection pooling is experimental and not recommended for production use.
|
|
21
|
+
* Added a reference to the connection pooling documentation.
|
|
22
|
+
|
|
4
23
|
0.13.1 (2025-10-03)
|
|
5
24
|
-------------------
|
|
6
25
|
|
{open_api_framework-0.13.1/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
|
#
|
|
@@ -164,7 +174,12 @@ DATABASES["default"]["CONN_MAX_AGE"] = config(
|
|
|
164
174
|
DB_POOL_ENABLED = config(
|
|
165
175
|
"DB_POOL_ENABLED",
|
|
166
176
|
default=False,
|
|
167
|
-
help_text=(
|
|
177
|
+
help_text=(
|
|
178
|
+
"**Experimental:** Whether to use connection pooling. "
|
|
179
|
+
"This feature is not yet recommended for production use. "
|
|
180
|
+
"See the documentation for details: "
|
|
181
|
+
"https://open-api-framework.readthedocs.io/en/latest/connection_pooling.html"
|
|
182
|
+
),
|
|
168
183
|
group="Database",
|
|
169
184
|
)
|
|
170
185
|
|
|
@@ -302,53 +317,55 @@ if DB_POOL_ENABLED:
|
|
|
302
317
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-DEFAULT_AUTO_FIELD
|
|
303
318
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
|
304
319
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
+
)
|
|
320
336
|
|
|
321
|
-
CACHES = {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
+
},
|
|
328
345
|
},
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
+
},
|
|
336
353
|
},
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
+
},
|
|
344
361
|
},
|
|
345
|
-
}
|
|
346
|
-
|
|
362
|
+
}
|
|
363
|
+
|
|
347
364
|
|
|
348
365
|
#
|
|
349
366
|
# APPLICATIONS enabled for this project
|
|
350
367
|
#
|
|
351
|
-
INSTALLED_APPS =
|
|
368
|
+
INSTALLED_APPS = importable(
|
|
352
369
|
# Note: contenttypes should be first, see Django ticket #10827
|
|
353
370
|
"django.contrib.contenttypes",
|
|
354
371
|
"django.contrib.auth",
|
|
@@ -391,9 +408,9 @@ INSTALLED_APPS = [
|
|
|
391
408
|
PROJECT_DIRNAME,
|
|
392
409
|
# Django libraries
|
|
393
410
|
"upgrade_check",
|
|
394
|
-
|
|
411
|
+
)
|
|
395
412
|
|
|
396
|
-
MIDDLEWARE =
|
|
413
|
+
MIDDLEWARE = importable(
|
|
397
414
|
"django.middleware.security.SecurityMiddleware",
|
|
398
415
|
"sessionprofile.middleware.SessionProfileMiddleware",
|
|
399
416
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
@@ -406,7 +423,7 @@ MIDDLEWARE = [
|
|
|
406
423
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
407
424
|
"axes.middleware.AxesMiddleware",
|
|
408
425
|
"csp.contrib.rate_limiting.RateLimitedCSPMiddleware",
|
|
409
|
-
|
|
426
|
+
)
|
|
410
427
|
|
|
411
428
|
ROOT_URLCONF = f"{PROJECT_DIRNAME}.urls"
|
|
412
429
|
|
|
@@ -419,7 +436,7 @@ TEMPLATE_LOADERS = (
|
|
|
419
436
|
TEMPLATES = [
|
|
420
437
|
{
|
|
421
438
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|
422
|
-
"DIRS": [
|
|
439
|
+
"DIRS": [DJANGO_PROJECT_DIR / "templates"],
|
|
423
440
|
"APP_DIRS": False, # conflicts with explicity specifying the loaders
|
|
424
441
|
"OPTIONS": {
|
|
425
442
|
"context_processors": [
|
|
@@ -438,7 +455,7 @@ TEMPLATES = [
|
|
|
438
455
|
WSGI_APPLICATION = f"{PROJECT_DIRNAME}.wsgi.application"
|
|
439
456
|
|
|
440
457
|
# Translations
|
|
441
|
-
LOCALE_PATHS = (
|
|
458
|
+
LOCALE_PATHS = (DJANGO_PROJECT_DIR / "conf" / "locale",)
|
|
442
459
|
|
|
443
460
|
#
|
|
444
461
|
# SERVING of static and media files
|
|
@@ -446,10 +463,10 @@ LOCALE_PATHS = (Path(DJANGO_PROJECT_DIR) / "conf" / "locale",)
|
|
|
446
463
|
|
|
447
464
|
STATIC_URL = "/static/"
|
|
448
465
|
|
|
449
|
-
STATIC_ROOT =
|
|
466
|
+
STATIC_ROOT = BASE_DIR / "static"
|
|
450
467
|
|
|
451
468
|
# Additional locations of static files
|
|
452
|
-
STATICFILES_DIRS = [
|
|
469
|
+
STATICFILES_DIRS = [DJANGO_PROJECT_DIR / "static"]
|
|
453
470
|
|
|
454
471
|
# List of finder classes that know how to find static files in
|
|
455
472
|
# various locations.
|
|
@@ -458,7 +475,7 @@ STATICFILES_FINDERS = [
|
|
|
458
475
|
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
|
459
476
|
]
|
|
460
477
|
|
|
461
|
-
MEDIA_ROOT =
|
|
478
|
+
MEDIA_ROOT = BASE_DIR / "media"
|
|
462
479
|
|
|
463
480
|
MEDIA_URL = "/media/"
|
|
464
481
|
|
|
@@ -563,6 +580,7 @@ CELERY_LOGLEVEL = config(
|
|
|
563
580
|
help_text="control the verbosity of logging output for celery, independent of ``LOG_LEVEL``."
|
|
564
581
|
" Available values are ``CRITICAL``, ``ERROR``, ``WARNING``, ``INFO`` and ``DEBUG``",
|
|
565
582
|
group="Celery",
|
|
583
|
+
add_to_docs="celery",
|
|
566
584
|
)
|
|
567
585
|
|
|
568
586
|
_USE_STRUCTLOG = config("_USE_STRUCTLOG", default=False, add_to_docs=False)
|
|
@@ -573,10 +591,11 @@ ENABLE_STRUCTLOG_REQUESTS = config(
|
|
|
573
591
|
default=True,
|
|
574
592
|
help_text=("enable structured logging of requests"),
|
|
575
593
|
group="Logging",
|
|
594
|
+
add_to_docs="django_structlog",
|
|
576
595
|
)
|
|
577
596
|
|
|
578
597
|
|
|
579
|
-
LOGGING_DIR =
|
|
598
|
+
LOGGING_DIR = BASE_DIR / "log"
|
|
580
599
|
|
|
581
600
|
if _USE_STRUCTLOG:
|
|
582
601
|
import structlog
|
|
@@ -663,7 +682,7 @@ if _USE_STRUCTLOG:
|
|
|
663
682
|
"json_file": {
|
|
664
683
|
"level": LOG_LEVEL, # always debug might be better?
|
|
665
684
|
"class": "logging.handlers.RotatingFileHandler",
|
|
666
|
-
"filename":
|
|
685
|
+
"filename": LOGGING_DIR / "application.jsonl",
|
|
667
686
|
"formatter": "json",
|
|
668
687
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
669
688
|
"backupCount": 10,
|
|
@@ -671,7 +690,7 @@ if _USE_STRUCTLOG:
|
|
|
671
690
|
"performance": {
|
|
672
691
|
"level": "INFO",
|
|
673
692
|
"class": "logging.handlers.RotatingFileHandler",
|
|
674
|
-
"filename":
|
|
693
|
+
"filename": LOGGING_DIR / "performance.log",
|
|
675
694
|
"formatter": "performance",
|
|
676
695
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
677
696
|
"backupCount": 10,
|
|
@@ -679,7 +698,7 @@ if _USE_STRUCTLOG:
|
|
|
679
698
|
"requests": {
|
|
680
699
|
"level": "DEBUG",
|
|
681
700
|
"class": "logging.handlers.RotatingFileHandler",
|
|
682
|
-
"filename":
|
|
701
|
+
"filename": LOGGING_DIR / "requests.log",
|
|
683
702
|
"formatter": "timestamped",
|
|
684
703
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
685
704
|
"backupCount": 10,
|
|
@@ -820,23 +839,10 @@ else:
|
|
|
820
839
|
"class": "logging.StreamHandler",
|
|
821
840
|
"formatter": "db",
|
|
822
841
|
},
|
|
823
|
-
"celery_console": {
|
|
824
|
-
"level": CELERY_LOGLEVEL,
|
|
825
|
-
"class": "logging.StreamHandler",
|
|
826
|
-
"formatter": "timestamped",
|
|
827
|
-
},
|
|
828
|
-
"celery_file": {
|
|
829
|
-
"level": CELERY_LOGLEVEL,
|
|
830
|
-
"class": "logging.handlers.RotatingFileHandler",
|
|
831
|
-
"filename": Path(LOGGING_DIR) / "celery.log",
|
|
832
|
-
"formatter": "verbose",
|
|
833
|
-
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
834
|
-
"backupCount": 10,
|
|
835
|
-
},
|
|
836
842
|
"django": {
|
|
837
843
|
"level": LOG_LEVEL,
|
|
838
844
|
"class": "logging.handlers.RotatingFileHandler",
|
|
839
|
-
"filename":
|
|
845
|
+
"filename": LOGGING_DIR / "django.log",
|
|
840
846
|
"formatter": "verbose",
|
|
841
847
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
842
848
|
"backupCount": 10,
|
|
@@ -844,7 +850,7 @@ else:
|
|
|
844
850
|
"project": {
|
|
845
851
|
"level": LOG_LEVEL,
|
|
846
852
|
"class": "logging.handlers.RotatingFileHandler",
|
|
847
|
-
"filename":
|
|
853
|
+
"filename": LOGGING_DIR / f"{PROJECT_DIRNAME}.log",
|
|
848
854
|
"formatter": "verbose",
|
|
849
855
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
850
856
|
"backupCount": 10,
|
|
@@ -852,7 +858,7 @@ else:
|
|
|
852
858
|
"performance": {
|
|
853
859
|
"level": "INFO",
|
|
854
860
|
"class": "logging.handlers.RotatingFileHandler",
|
|
855
|
-
"filename":
|
|
861
|
+
"filename": LOGGING_DIR / "performance.log",
|
|
856
862
|
"formatter": "performance",
|
|
857
863
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
858
864
|
"backupCount": 10,
|
|
@@ -860,7 +866,7 @@ else:
|
|
|
860
866
|
"requests": {
|
|
861
867
|
"level": "DEBUG",
|
|
862
868
|
"class": "logging.handlers.RotatingFileHandler",
|
|
863
|
-
"filename":
|
|
869
|
+
"filename": LOGGING_DIR / "requests.log",
|
|
864
870
|
"formatter": "timestamped",
|
|
865
871
|
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
866
872
|
"backupCount": 10,
|
|
@@ -875,7 +881,26 @@ else:
|
|
|
875
881
|
# enabling saving to database
|
|
876
882
|
"class": "log_outgoing_requests.handlers.DatabaseOutgoingRequestsHandler",
|
|
877
883
|
},
|
|
878
|
-
}
|
|
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
|
+
),
|
|
879
904
|
"loggers": {
|
|
880
905
|
"": {
|
|
881
906
|
"handlers": logging_root_handlers,
|
|
@@ -925,18 +950,25 @@ else:
|
|
|
925
950
|
"level": "DEBUG",
|
|
926
951
|
"propagate": True,
|
|
927
952
|
},
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
"
|
|
932
|
-
|
|
933
|
-
|
|
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
|
+
),
|
|
934
965
|
}
|
|
935
966
|
|
|
936
967
|
#
|
|
937
968
|
# AUTH settings - user accounts, passwords, backends...
|
|
938
969
|
#
|
|
939
|
-
|
|
970
|
+
if find_spec(f"{PROJECT_DIRNAME}.accounts"):
|
|
971
|
+
AUTH_USER_MODEL = "accounts.User"
|
|
940
972
|
|
|
941
973
|
# Password validation
|
|
942
974
|
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
|
|
@@ -1048,7 +1080,7 @@ if subpath:
|
|
|
1048
1080
|
if "GIT_SHA" in os.environ:
|
|
1049
1081
|
GIT_SHA = config("GIT_SHA", "", add_to_docs=False)
|
|
1050
1082
|
# in docker (build) context, there is no .git directory
|
|
1051
|
-
elif (
|
|
1083
|
+
elif (BASE_DIR / ".git").exists():
|
|
1052
1084
|
try:
|
|
1053
1085
|
import git
|
|
1054
1086
|
except ImportError:
|
|
@@ -1128,6 +1160,7 @@ CORS_ALLOW_ALL_ORIGINS = config(
|
|
|
1128
1160
|
default=False,
|
|
1129
1161
|
group="Cross-Origin-Resource-Sharing",
|
|
1130
1162
|
help_text="allow cross-domain access from any client",
|
|
1163
|
+
add_to_docs="corsheaders",
|
|
1131
1164
|
)
|
|
1132
1165
|
CORS_ALLOWED_ORIGINS = config(
|
|
1133
1166
|
"CORS_ALLOWED_ORIGINS",
|
|
@@ -1138,6 +1171,7 @@ CORS_ALLOWED_ORIGINS = config(
|
|
|
1138
1171
|
"explicitly list the allowed origins for cross-domain requests. "
|
|
1139
1172
|
"Example: http://localhost:3000,https://some-app.gemeente.nl"
|
|
1140
1173
|
),
|
|
1174
|
+
add_to_docs="corsheaders",
|
|
1141
1175
|
)
|
|
1142
1176
|
CORS_ALLOWED_ORIGIN_REGEXES = config(
|
|
1143
1177
|
"CORS_ALLOWED_ORIGIN_REGEXES",
|
|
@@ -1145,7 +1179,9 @@ CORS_ALLOWED_ORIGIN_REGEXES = config(
|
|
|
1145
1179
|
default=[],
|
|
1146
1180
|
group="Cross-Origin-Resource-Sharing",
|
|
1147
1181
|
help_text="same as ``CORS_ALLOWED_ORIGINS``, but supports regular expressions",
|
|
1182
|
+
add_to_docs="corsheaders",
|
|
1148
1183
|
)
|
|
1184
|
+
|
|
1149
1185
|
# Authorization is included in default_cors_headers
|
|
1150
1186
|
CORS_ALLOW_HEADERS = (
|
|
1151
1187
|
list(default_cors_headers)
|
|
@@ -1163,8 +1199,10 @@ CORS_ALLOW_HEADERS = (
|
|
|
1163
1199
|
"By default, Authorization, Accept-Crs and Content-Crs are already included. "
|
|
1164
1200
|
"The value of this variable is added to these already included headers."
|
|
1165
1201
|
),
|
|
1202
|
+
add_to_docs="corsheaders",
|
|
1166
1203
|
)
|
|
1167
1204
|
)
|
|
1205
|
+
|
|
1168
1206
|
CORS_EXPOSE_HEADERS = [
|
|
1169
1207
|
"content-crs",
|
|
1170
1208
|
]
|
|
@@ -1185,7 +1223,7 @@ CSRF_TRUSTED_ORIGINS = config(
|
|
|
1185
1223
|
#
|
|
1186
1224
|
# DJANGO-PRIVATES -- safely serve files after authorization
|
|
1187
1225
|
#
|
|
1188
|
-
PRIVATE_MEDIA_ROOT =
|
|
1226
|
+
PRIVATE_MEDIA_ROOT = BASE_DIR / "private-media"
|
|
1189
1227
|
PRIVATE_MEDIA_URL = "/private-media/"
|
|
1190
1228
|
|
|
1191
1229
|
|
|
@@ -1387,54 +1425,72 @@ LOG_OUTGOING_REQUESTS_MAX_AGE = config(
|
|
|
1387
1425
|
|
|
1388
1426
|
def get_content_security_policy():
|
|
1389
1427
|
# ideally we'd use BASE_URI but it'd have to be lazy or cause issues
|
|
1390
|
-
|
|
1428
|
+
extra_default_src = config(
|
|
1391
1429
|
"CSP_EXTRA_DEFAULT_SRC",
|
|
1392
1430
|
default=[],
|
|
1393
1431
|
split=True,
|
|
1394
1432
|
group="Content Security Policy",
|
|
1395
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",
|
|
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",
|
|
1396
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
|
|
1397
1487
|
return {
|
|
1398
1488
|
"DIRECTIVES": {
|
|
1399
|
-
"default-src":
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
group="Content Security Policy",
|
|
1405
|
-
help_text="Extra default source URLs for CSP other than ``self``. Used for ``img-src``, ``style-src`` and ``script-src``.",
|
|
1406
|
-
),
|
|
1407
|
-
"form-action": config(
|
|
1408
|
-
"CSP_FORM_ACTION",
|
|
1409
|
-
default=["\"'self'\""]
|
|
1410
|
-
+ config(
|
|
1411
|
-
"CSP_EXTRA_FORM_ACTION",
|
|
1412
|
-
default=[],
|
|
1413
|
-
split=True,
|
|
1414
|
-
group="Content Security Policy",
|
|
1415
|
-
help_text="Additional `form-action` sources.",
|
|
1416
|
-
),
|
|
1417
|
-
split=True,
|
|
1418
|
-
group="Content Security Policy",
|
|
1419
|
-
help_text="Override the default `form-action` sources.",
|
|
1420
|
-
)
|
|
1421
|
-
+ CORS_ALLOWED_ORIGINS,
|
|
1422
|
-
"img-src": csp_default_src
|
|
1423
|
-
+ ["data:", "cdn.redoc.ly"]
|
|
1424
|
-
+ config(
|
|
1425
|
-
"CSP_EXTRA_IMG_SRC",
|
|
1426
|
-
default=[],
|
|
1427
|
-
split=True,
|
|
1428
|
-
group="Content Security Policy",
|
|
1429
|
-
help_text="Extra `img-src` sources.",
|
|
1430
|
-
),
|
|
1431
|
-
"object-src": config(
|
|
1432
|
-
"CSP_OBJECT_SRC",
|
|
1433
|
-
default=["\"'none'\""],
|
|
1434
|
-
split=True,
|
|
1435
|
-
group="Content Security Policy",
|
|
1436
|
-
help_text="`object-src` sources.",
|
|
1437
|
-
),
|
|
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,
|
|
1438
1494
|
"style-src": csp_default_src
|
|
1439
1495
|
+ [NONCE, "'unsafe-inline'", "fonts.googleapis.com"],
|
|
1440
1496
|
"script-src": csp_default_src + [NONCE, "'unsafe-inline'"],
|
|
@@ -1444,22 +1500,11 @@ def get_content_security_policy():
|
|
|
1444
1500
|
"frame-ancestors": [NONE],
|
|
1445
1501
|
"frame-src": [SELF],
|
|
1446
1502
|
"upgrade-insecure-requests": False, # Enable only in production
|
|
1447
|
-
"report-uri":
|
|
1448
|
-
"CSP_REPORT_URI",
|
|
1449
|
-
None,
|
|
1450
|
-
group="Content Security Policy",
|
|
1451
|
-
help_text="URI for CSP report-uri directive.",
|
|
1452
|
-
),
|
|
1503
|
+
"report-uri": report_uri,
|
|
1453
1504
|
},
|
|
1454
1505
|
# Envvar used for django-csp==3.8 was a float between 0 and 1, while django-csp==4.0
|
|
1455
1506
|
# expects a percentage (between 0 and 100)
|
|
1456
|
-
"REPORT_PERCENTAGE":
|
|
1457
|
-
"CSP_REPORT_PERCENTAGE",
|
|
1458
|
-
0.0,
|
|
1459
|
-
group="Content Security Policy",
|
|
1460
|
-
help_text="Fraction (between 0 and 1) of requests to include report-uri directive.",
|
|
1461
|
-
)
|
|
1462
|
-
* 100,
|
|
1507
|
+
"REPORT_PERCENTAGE": report_percentage * 100,
|
|
1463
1508
|
}
|
|
1464
1509
|
|
|
1465
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.1 → 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
|
|
@@ -34,7 +35,7 @@ Database
|
|
|
34
35
|
* ``DB_HOST``: hostname of the PostgreSQL database. Defaults to ``db`` for the docker environment, otherwise defaults to ``localhost``.
|
|
35
36
|
* ``DB_PORT``: port number of the database. Defaults to: ``5432``.
|
|
36
37
|
* ``DB_CONN_MAX_AGE``: The lifetime of a database connection, as an integer of seconds. Use 0 to close database connections at the end of each request — Django’s historical behavior. This setting is ignored if connection pooling is used. Defaults to: ``60``.
|
|
37
|
-
* ``DB_POOL_ENABLED``: Whether to use connection pooling. Defaults to: ``False``.
|
|
38
|
+
* ``DB_POOL_ENABLED``: **Experimental:** Whether to use connection pooling. This feature is not yet recommended for production use. See the documentation for details: https://open-api-framework.readthedocs.io/en/latest/connection_pooling.html. Defaults to: ``False``.
|
|
38
39
|
* ``DB_POOL_MIN_SIZE``: The minimum number of connection the pool will hold. The pool will actively try to create new connections if some are lost (closed, broken) and will try to never go below min_size. Defaults to: ``4``.
|
|
39
40
|
* ``DB_POOL_MAX_SIZE``: The maximum number of connections the pool will hold. If None, or equal to min_size, the pool will not grow or shrink. If larger than min_size, the pool can grow if more than min_size connections are requested at the same time and will shrink back after the extra connections have been unused for more than max_idle seconds. Defaults to: ``None``.
|
|
40
41
|
* ``DB_POOL_TIMEOUT``: The default maximum time in seconds that a client can wait to receive a connection from the pool (using connection() or getconn()). Note that these methods allow to override the timeout default. Defaults to: ``30``.
|
|
@@ -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.1 → open_api_framework-0.13.3}/open_api_framework/context_processors.py
RENAMED
|
File without changes
|
{open_api_framework-0.13.1 → 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.1 → open_api_framework-0.13.3}/open_api_framework/templatetags/__init__.py
RENAMED
|
File without changes
|
{open_api_framework-0.13.1 → 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.1 → 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.1 → open_api_framework-0.13.3}/open_api_framework.egg-info/requires.txt
RENAMED
|
File without changes
|
{open_api_framework-0.13.1 → open_api_framework-0.13.3}/open_api_framework.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|