open-api-framework 0.2.0__tar.gz → 0.3.0__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.3.0/CHANGELOG.rst +26 -0
- {open_api_framework-0.2.0/open_api_framework.egg-info → open_api_framework-0.3.0}/PKG-INFO +6 -5
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/README.rst +2 -2
- open_api_framework-0.3.0/open_api_framework/conf/base.py +650 -0
- open_api_framework-0.3.0/open_api_framework/conf/utils.py +63 -0
- open_api_framework-0.3.0/open_api_framework/py.typed +0 -0
- {open_api_framework-0.2.0 → open_api_framework-0.3.0/open_api_framework.egg-info}/PKG-INFO +6 -5
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/open_api_framework.egg-info/SOURCES.txt +5 -1
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/open_api_framework.egg-info/requires.txt +1 -0
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/pyproject.toml +5 -4
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/setup.cfg +1 -1
- open_api_framework-0.3.0/tests/test_config_helpers.py +13 -0
- open_api_framework-0.2.0/tests/test_dummy.py +0 -2
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/LICENSE +0 -0
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/MANIFEST.in +0 -0
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/open_api_framework/__init__.py +0 -0
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/open_api_framework/apps.py +0 -0
- /open_api_framework-0.2.0/open_api_framework/py.typed → /open_api_framework-0.3.0/open_api_framework/conf/__init__.py +0 -0
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/open_api_framework.egg-info/dependency_links.txt +0 -0
- {open_api_framework-0.2.0 → open_api_framework-0.3.0}/open_api_framework.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Changelog
|
|
2
|
+
=========
|
|
3
|
+
|
|
4
|
+
0.3.0 (2024-05-17)
|
|
5
|
+
------------------
|
|
6
|
+
|
|
7
|
+
**New features**
|
|
8
|
+
|
|
9
|
+
* [#14] Add django-log-outgoing-requests to deps
|
|
10
|
+
* [open-zaak/open-zaak#1629] Add generic base settings file
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
0.2.0 (2024-03-22)
|
|
14
|
+
------------------
|
|
15
|
+
|
|
16
|
+
**New features**
|
|
17
|
+
|
|
18
|
+
* Add support for python 3.10
|
|
19
|
+
* Upgrade to Django 4.2
|
|
20
|
+
* Add maykin-2fa
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
0.1.0 (2024-01-30)
|
|
24
|
+
------------------
|
|
25
|
+
|
|
26
|
+
* Initial release as a metapackage to pin several dependencies
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: open_api_framework
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.3.0
|
|
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 2024 Maykin Media
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ Project-URL: Homepage, https://github.com/maykinmedia/open_api_framework
|
|
|
15
15
|
Project-URL: Documentation, http://open_api_framework.readthedocs.io/en/latest/
|
|
16
16
|
Project-URL: Bug Tracker, https://github.com/maykinmedia/open_api_framework/issues
|
|
17
17
|
Project-URL: Source Code, https://github.com/maykinmedia/open_api_framework
|
|
18
|
-
Keywords:
|
|
18
|
+
Keywords: metapackage,dependencies,settings
|
|
19
19
|
Classifier: Development Status :: 3 - Alpha
|
|
20
20
|
Classifier: Framework :: Django
|
|
21
21
|
Classifier: Framework :: Django :: 4.2
|
|
@@ -35,6 +35,7 @@ Requires-Dist: django-axes>=6.3.0
|
|
|
35
35
|
Requires-Dist: django-cors-headers>=4.3.1
|
|
36
36
|
Requires-Dist: django-markup>=1.8.1
|
|
37
37
|
Requires-Dist: django-jsonform>=2.21.4
|
|
38
|
+
Requires-Dist: django-log-outgoing-requests>=0.6.1
|
|
38
39
|
Requires-Dist: django-admin-index>=3.1.0
|
|
39
40
|
Requires-Dist: django-redis>=5.4.0
|
|
40
41
|
Requires-Dist: djangorestframework>=3.12.4
|
|
@@ -77,7 +78,7 @@ Requires-Dist: twine; extra == "release"
|
|
|
77
78
|
Welcome to open-api-framework's documentation!
|
|
78
79
|
=================================================
|
|
79
80
|
|
|
80
|
-
:Version: 0.
|
|
81
|
+
:Version: 0.3.0
|
|
81
82
|
:Source: https://github.com/maykinmedia/open-api-framework
|
|
82
83
|
:Keywords: metapackage, dependencies
|
|
83
84
|
|
|
@@ -116,7 +117,7 @@ Install
|
|
|
116
117
|
Local development
|
|
117
118
|
=================
|
|
118
119
|
|
|
119
|
-
To install and develop the library locally, use
|
|
120
|
+
To install and develop the library locally, use:
|
|
120
121
|
|
|
121
122
|
.. code-block:: bash
|
|
122
123
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Welcome to open-api-framework's documentation!
|
|
4
4
|
=================================================
|
|
5
5
|
|
|
6
|
-
:Version: 0.
|
|
6
|
+
:Version: 0.3.0
|
|
7
7
|
:Source: https://github.com/maykinmedia/open-api-framework
|
|
8
8
|
:Keywords: metapackage, dependencies
|
|
9
9
|
|
|
@@ -42,7 +42,7 @@ Install
|
|
|
42
42
|
Local development
|
|
43
43
|
=================
|
|
44
44
|
|
|
45
|
-
To install and develop the library locally, use
|
|
45
|
+
To install and develop the library locally, use:
|
|
46
46
|
|
|
47
47
|
.. code-block:: bash
|
|
48
48
|
|
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import warnings
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
from django.urls import reverse_lazy
|
|
7
|
+
|
|
8
|
+
import sentry_sdk
|
|
9
|
+
from corsheaders.defaults import default_headers as default_cors_headers
|
|
10
|
+
from log_outgoing_requests.formatters import HttpFormatter
|
|
11
|
+
from notifications_api_common.settings import * # noqa
|
|
12
|
+
|
|
13
|
+
from .utils import (
|
|
14
|
+
config,
|
|
15
|
+
get_django_project_dir,
|
|
16
|
+
get_project_dirname,
|
|
17
|
+
get_sentry_integrations,
|
|
18
|
+
strip_protocol_from_origin,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
PROJECT_DIRNAME = get_project_dirname()
|
|
22
|
+
|
|
23
|
+
# Build paths inside the project, so further paths can be defined relative to
|
|
24
|
+
# the code root.
|
|
25
|
+
DJANGO_PROJECT_DIR = get_django_project_dir()
|
|
26
|
+
BASE_DIR = Path(DJANGO_PROJECT_DIR).resolve().parents[1]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Core Django settings
|
|
31
|
+
#
|
|
32
|
+
SITE_ID = config("SITE_ID", default=1)
|
|
33
|
+
|
|
34
|
+
# SECURITY WARNING: keep the secret key used in production secret!
|
|
35
|
+
SECRET_KEY = config("SECRET_KEY")
|
|
36
|
+
|
|
37
|
+
# NEVER run with DEBUG=True in production-like environments
|
|
38
|
+
DEBUG = config("DEBUG", default=False)
|
|
39
|
+
|
|
40
|
+
# = domains we're running on
|
|
41
|
+
ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="", split=True)
|
|
42
|
+
USE_X_FORWARDED_HOST = config("USE_X_FORWARDED_HOST", default=False)
|
|
43
|
+
|
|
44
|
+
IS_HTTPS = config("IS_HTTPS", default=not DEBUG)
|
|
45
|
+
|
|
46
|
+
# Internationalization
|
|
47
|
+
# https://docs.djangoproject.com/en/2.0/topics/i18n/
|
|
48
|
+
|
|
49
|
+
LANGUAGE_CODE = "nl-nl"
|
|
50
|
+
|
|
51
|
+
TIME_ZONE = "UTC" # note: this *may* affect the output of DRF datetimes
|
|
52
|
+
|
|
53
|
+
USE_I18N = True
|
|
54
|
+
|
|
55
|
+
USE_L10N = True
|
|
56
|
+
|
|
57
|
+
USE_TZ = True
|
|
58
|
+
|
|
59
|
+
USE_THOUSAND_SEPARATOR = True
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# DATABASE and CACHING setup
|
|
63
|
+
#
|
|
64
|
+
|
|
65
|
+
DATABASES = {
|
|
66
|
+
"default": {
|
|
67
|
+
"ENGINE": "django.db.backends.postgresql",
|
|
68
|
+
"NAME": config("DB_NAME", PROJECT_DIRNAME),
|
|
69
|
+
"USER": config("DB_USER", PROJECT_DIRNAME),
|
|
70
|
+
"PASSWORD": config("DB_PASSWORD", PROJECT_DIRNAME),
|
|
71
|
+
"HOST": config("DB_HOST", "localhost"),
|
|
72
|
+
"PORT": config("DB_PORT", 5432),
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# keep the current schema for now and deal with migrating to BigAutoField later, see
|
|
77
|
+
# https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-DEFAULT_AUTO_FIELD
|
|
78
|
+
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
|
79
|
+
|
|
80
|
+
CACHES = {
|
|
81
|
+
"default": {
|
|
82
|
+
"BACKEND": "django_redis.cache.RedisCache",
|
|
83
|
+
"LOCATION": f"redis://{config('CACHE_DEFAULT', 'localhost:6379/0')}",
|
|
84
|
+
"OPTIONS": {
|
|
85
|
+
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
86
|
+
"IGNORE_EXCEPTIONS": True,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
"axes": {
|
|
90
|
+
"BACKEND": "django_redis.cache.RedisCache",
|
|
91
|
+
"LOCATION": f"redis://{config('CACHE_AXES', 'localhost:6379/0')}",
|
|
92
|
+
"OPTIONS": {
|
|
93
|
+
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
94
|
+
"IGNORE_EXCEPTIONS": True,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
"oidc": {
|
|
98
|
+
"BACKEND": "django_redis.cache.RedisCache",
|
|
99
|
+
"LOCATION": f"redis://{config('CACHE_DEFAULT', 'localhost:6379/0')}",
|
|
100
|
+
"OPTIONS": {
|
|
101
|
+
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
102
|
+
"IGNORE_EXCEPTIONS": True,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#
|
|
108
|
+
# APPLICATIONS enabled for this project
|
|
109
|
+
#
|
|
110
|
+
INSTALLED_APPS = [
|
|
111
|
+
# Note: contenttypes should be first, see Django ticket #10827
|
|
112
|
+
"django.contrib.contenttypes",
|
|
113
|
+
"django.contrib.auth",
|
|
114
|
+
"django.contrib.sessions",
|
|
115
|
+
# Note: If enabled, at least one Site object is required
|
|
116
|
+
"django.contrib.sites",
|
|
117
|
+
"django.contrib.messages",
|
|
118
|
+
"django.contrib.staticfiles",
|
|
119
|
+
# Optional applications.
|
|
120
|
+
"django_admin_index",
|
|
121
|
+
"django.contrib.admin",
|
|
122
|
+
# External applications.
|
|
123
|
+
"axes",
|
|
124
|
+
"django_filters",
|
|
125
|
+
"corsheaders",
|
|
126
|
+
"vng_api_common",
|
|
127
|
+
"notifications_api_common",
|
|
128
|
+
"drf_spectacular",
|
|
129
|
+
"rest_framework",
|
|
130
|
+
"django_markup",
|
|
131
|
+
"solo",
|
|
132
|
+
# Two-factor authentication in the Django admin, enforced.
|
|
133
|
+
"django_otp",
|
|
134
|
+
"django_otp.plugins.otp_static",
|
|
135
|
+
"django_otp.plugins.otp_totp",
|
|
136
|
+
"two_factor",
|
|
137
|
+
"maykin_2fa",
|
|
138
|
+
"privates",
|
|
139
|
+
"django_jsonform",
|
|
140
|
+
"simple_certmanager",
|
|
141
|
+
"zgw_consumers",
|
|
142
|
+
"mozilla_django_oidc",
|
|
143
|
+
"mozilla_django_oidc_db",
|
|
144
|
+
"log_outgoing_requests",
|
|
145
|
+
"django_setup_configuration",
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
MIDDLEWARE = [
|
|
149
|
+
"django.middleware.security.SecurityMiddleware",
|
|
150
|
+
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
151
|
+
"corsheaders.middleware.CorsMiddleware",
|
|
152
|
+
"django.middleware.common.CommonMiddleware",
|
|
153
|
+
"django.middleware.csrf.CsrfViewMiddleware",
|
|
154
|
+
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
155
|
+
"maykin_2fa.middleware.OTPMiddleware",
|
|
156
|
+
"mozilla_django_oidc_db.middleware.SessionRefresh",
|
|
157
|
+
"django.contrib.messages.middleware.MessageMiddleware",
|
|
158
|
+
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
159
|
+
"axes.middleware.AxesMiddleware",
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
ROOT_URLCONF = f"{PROJECT_DIRNAME}.urls"
|
|
163
|
+
|
|
164
|
+
# List of callables that know how to import templates from various sources.
|
|
165
|
+
TEMPLATE_LOADERS = (
|
|
166
|
+
"django.template.loaders.filesystem.Loader",
|
|
167
|
+
"django.template.loaders.app_directories.Loader",
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
TEMPLATES = [
|
|
171
|
+
{
|
|
172
|
+
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|
173
|
+
"DIRS": [Path(DJANGO_PROJECT_DIR) / "templates"],
|
|
174
|
+
"APP_DIRS": False, # conflicts with explicity specifying the loaders
|
|
175
|
+
"OPTIONS": {
|
|
176
|
+
"context_processors": [
|
|
177
|
+
"django.template.context_processors.debug",
|
|
178
|
+
"django.template.context_processors.request",
|
|
179
|
+
"django.contrib.auth.context_processors.auth",
|
|
180
|
+
"django.contrib.messages.context_processors.messages",
|
|
181
|
+
f"{PROJECT_DIRNAME}.utils.context_processors.settings",
|
|
182
|
+
],
|
|
183
|
+
"loaders": TEMPLATE_LOADERS,
|
|
184
|
+
},
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
WSGI_APPLICATION = f"{PROJECT_DIRNAME}.wsgi.application"
|
|
189
|
+
|
|
190
|
+
# Translations
|
|
191
|
+
LOCALE_PATHS = (Path(DJANGO_PROJECT_DIR) / "conf" / "locale",)
|
|
192
|
+
|
|
193
|
+
#
|
|
194
|
+
# SERVING of static and media files
|
|
195
|
+
#
|
|
196
|
+
|
|
197
|
+
STATIC_URL = "/static/"
|
|
198
|
+
|
|
199
|
+
STATIC_ROOT = Path(BASE_DIR) / "static"
|
|
200
|
+
|
|
201
|
+
# Additional locations of static files
|
|
202
|
+
STATICFILES_DIRS = [Path(DJANGO_PROJECT_DIR) / "static"]
|
|
203
|
+
|
|
204
|
+
# List of finder classes that know how to find static files in
|
|
205
|
+
# various locations.
|
|
206
|
+
STATICFILES_FINDERS = [
|
|
207
|
+
"django.contrib.staticfiles.finders.FileSystemFinder",
|
|
208
|
+
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
MEDIA_ROOT = Path(BASE_DIR) / "media"
|
|
212
|
+
|
|
213
|
+
MEDIA_URL = "/media/"
|
|
214
|
+
|
|
215
|
+
FILE_UPLOAD_PERMISSIONS = 0o644
|
|
216
|
+
|
|
217
|
+
FIXTURE_DIRS = (Path(DJANGO_PROJECT_DIR) / "fixtures",)
|
|
218
|
+
|
|
219
|
+
#
|
|
220
|
+
# Sending EMAIL
|
|
221
|
+
#
|
|
222
|
+
EMAIL_HOST = config("EMAIL_HOST", default="localhost")
|
|
223
|
+
EMAIL_PORT = config(
|
|
224
|
+
"EMAIL_PORT", default=25
|
|
225
|
+
) # disabled on Google Cloud, use 487 instead
|
|
226
|
+
EMAIL_HOST_USER = config("EMAIL_HOST_USER", default="")
|
|
227
|
+
EMAIL_HOST_PASSWORD = config("EMAIL_HOST_PASSWORD", default="")
|
|
228
|
+
EMAIL_USE_TLS = config("EMAIL_USE_TLS", default=False)
|
|
229
|
+
EMAIL_TIMEOUT = 10
|
|
230
|
+
|
|
231
|
+
DEFAULT_FROM_EMAIL = config("DEFAULT_FROM_EMAIL", f"{PROJECT_DIRNAME}@example.com")
|
|
232
|
+
|
|
233
|
+
#
|
|
234
|
+
# LOGGING
|
|
235
|
+
#
|
|
236
|
+
LOG_STDOUT = config("LOG_STDOUT", default=False)
|
|
237
|
+
LOG_LEVEL = config("LOG_LEVEL", default="WARNING")
|
|
238
|
+
LOG_QUERIES = config("LOG_QUERIES", default=False)
|
|
239
|
+
LOG_REQUESTS = config("LOG_REQUESTS", default=False)
|
|
240
|
+
if LOG_QUERIES and not DEBUG:
|
|
241
|
+
warnings.warn(
|
|
242
|
+
"Requested LOG_QUERIES=1 but DEBUG is false, no query logs will be emited.",
|
|
243
|
+
RuntimeWarning,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
LOGGING_DIR = Path(BASE_DIR) / "log"
|
|
247
|
+
|
|
248
|
+
logging_root_handlers = ["console"] if LOG_STDOUT else ["project"]
|
|
249
|
+
logging_django_handlers = ["console"] if LOG_STDOUT else ["django"]
|
|
250
|
+
|
|
251
|
+
LOGGING = {
|
|
252
|
+
"version": 1,
|
|
253
|
+
"disable_existing_loggers": False,
|
|
254
|
+
"formatters": {
|
|
255
|
+
"verbose": {
|
|
256
|
+
"format": "%(asctime)s %(levelname)s %(name)s %(module)s %(process)d %(thread)d %(message)s"
|
|
257
|
+
},
|
|
258
|
+
"timestamped": {"format": "%(asctime)s %(levelname)s %(name)s %(message)s"},
|
|
259
|
+
"simple": {"format": "%(levelname)s %(message)s"},
|
|
260
|
+
"performance": {"format": "%(asctime)s %(process)d | %(thread)d | %(message)s"},
|
|
261
|
+
"db": {"format": "%(asctime)s | %(message)s"},
|
|
262
|
+
"outgoing_requests": {"()": HttpFormatter},
|
|
263
|
+
},
|
|
264
|
+
"filters": {
|
|
265
|
+
"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"},
|
|
266
|
+
},
|
|
267
|
+
"handlers": {
|
|
268
|
+
"mail_admins": {
|
|
269
|
+
"level": "ERROR",
|
|
270
|
+
"filters": ["require_debug_false"],
|
|
271
|
+
"class": "django.utils.log.AdminEmailHandler",
|
|
272
|
+
},
|
|
273
|
+
"null": {"level": "DEBUG", "class": "logging.NullHandler"},
|
|
274
|
+
"console": {
|
|
275
|
+
"level": LOG_LEVEL,
|
|
276
|
+
"class": "logging.StreamHandler",
|
|
277
|
+
"formatter": "timestamped",
|
|
278
|
+
},
|
|
279
|
+
"console_db": {
|
|
280
|
+
"level": "DEBUG",
|
|
281
|
+
"class": "logging.StreamHandler",
|
|
282
|
+
"formatter": "db",
|
|
283
|
+
},
|
|
284
|
+
"django": {
|
|
285
|
+
"level": LOG_LEVEL,
|
|
286
|
+
"class": "logging.handlers.RotatingFileHandler",
|
|
287
|
+
"filename": Path(LOGGING_DIR) / "django.log",
|
|
288
|
+
"formatter": "verbose",
|
|
289
|
+
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
290
|
+
"backupCount": 10,
|
|
291
|
+
},
|
|
292
|
+
"project": {
|
|
293
|
+
"level": LOG_LEVEL,
|
|
294
|
+
"class": "logging.handlers.RotatingFileHandler",
|
|
295
|
+
"filename": Path(LOGGING_DIR) / f"{PROJECT_DIRNAME}.log",
|
|
296
|
+
"formatter": "verbose",
|
|
297
|
+
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
298
|
+
"backupCount": 10,
|
|
299
|
+
},
|
|
300
|
+
"performance": {
|
|
301
|
+
"level": "INFO",
|
|
302
|
+
"class": "logging.handlers.RotatingFileHandler",
|
|
303
|
+
"filename": Path(LOGGING_DIR) / "performance.log",
|
|
304
|
+
"formatter": "performance",
|
|
305
|
+
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
306
|
+
"backupCount": 10,
|
|
307
|
+
},
|
|
308
|
+
"requests": {
|
|
309
|
+
"level": "DEBUG",
|
|
310
|
+
"class": "logging.handlers.RotatingFileHandler",
|
|
311
|
+
"filename": Path(LOGGING_DIR) / "requests.log",
|
|
312
|
+
"formatter": "timestamped",
|
|
313
|
+
"maxBytes": 1024 * 1024 * 10, # 10 MB
|
|
314
|
+
"backupCount": 10,
|
|
315
|
+
},
|
|
316
|
+
"log_outgoing_requests": {
|
|
317
|
+
"level": "DEBUG",
|
|
318
|
+
"formatter": "outgoing_requests",
|
|
319
|
+
"class": "logging.StreamHandler", # to write to stdout
|
|
320
|
+
},
|
|
321
|
+
"save_outgoing_requests": {
|
|
322
|
+
"level": "DEBUG",
|
|
323
|
+
# enabling saving to database
|
|
324
|
+
"class": "log_outgoing_requests.handlers.DatabaseOutgoingRequestsHandler",
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
"loggers": {
|
|
328
|
+
"": {
|
|
329
|
+
"handlers": logging_root_handlers,
|
|
330
|
+
"level": "ERROR",
|
|
331
|
+
"propagate": False,
|
|
332
|
+
},
|
|
333
|
+
PROJECT_DIRNAME: {
|
|
334
|
+
"handlers": logging_root_handlers,
|
|
335
|
+
"level": LOG_LEVEL,
|
|
336
|
+
"propagate": True,
|
|
337
|
+
},
|
|
338
|
+
"mozilla_django_oidc": {
|
|
339
|
+
"handlers": logging_root_handlers,
|
|
340
|
+
"level": LOG_LEVEL,
|
|
341
|
+
},
|
|
342
|
+
f"{PROJECT_DIRNAME}.utils.middleware": {
|
|
343
|
+
"handlers": logging_root_handlers,
|
|
344
|
+
"level": LOG_LEVEL,
|
|
345
|
+
"propagate": False,
|
|
346
|
+
},
|
|
347
|
+
"vng_api_common": {
|
|
348
|
+
"handlers": ["console"],
|
|
349
|
+
"level": LOG_LEVEL,
|
|
350
|
+
"propagate": True,
|
|
351
|
+
},
|
|
352
|
+
"django.db.backends": {
|
|
353
|
+
"handlers": ["console_db"] if LOG_QUERIES else [],
|
|
354
|
+
"level": "DEBUG",
|
|
355
|
+
"propagate": False,
|
|
356
|
+
},
|
|
357
|
+
"django.request": {
|
|
358
|
+
"handlers": logging_django_handlers,
|
|
359
|
+
"level": LOG_LEVEL,
|
|
360
|
+
"propagate": True,
|
|
361
|
+
},
|
|
362
|
+
"django.template": {
|
|
363
|
+
"handlers": ["console"],
|
|
364
|
+
"level": "INFO",
|
|
365
|
+
"propagate": False,
|
|
366
|
+
},
|
|
367
|
+
"log_outgoing_requests": {
|
|
368
|
+
"handlers": (
|
|
369
|
+
["log_outgoing_requests", "save_outgoing_requests"]
|
|
370
|
+
if LOG_REQUESTS
|
|
371
|
+
else []
|
|
372
|
+
),
|
|
373
|
+
"level": "DEBUG",
|
|
374
|
+
"propagate": True,
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
#
|
|
380
|
+
# AUTH settings - user accounts, passwords, backends...
|
|
381
|
+
#
|
|
382
|
+
AUTH_USER_MODEL = "accounts.User"
|
|
383
|
+
|
|
384
|
+
# Password validation
|
|
385
|
+
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
|
|
386
|
+
AUTH_PASSWORD_VALIDATORS = [
|
|
387
|
+
{
|
|
388
|
+
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
|
|
389
|
+
},
|
|
390
|
+
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
|
391
|
+
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
|
392
|
+
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
|
393
|
+
]
|
|
394
|
+
|
|
395
|
+
# Allow logging in with both username+password and email+password
|
|
396
|
+
AUTHENTICATION_BACKENDS = [
|
|
397
|
+
"axes.backends.AxesBackend",
|
|
398
|
+
f"{PROJECT_DIRNAME}.accounts.backends.UserModelEmailBackend",
|
|
399
|
+
"django.contrib.auth.backends.ModelBackend",
|
|
400
|
+
"mozilla_django_oidc_db.backends.OIDCAuthenticationBackend",
|
|
401
|
+
]
|
|
402
|
+
|
|
403
|
+
SESSION_COOKIE_NAME = f"{PROJECT_DIRNAME}_sessionid"
|
|
404
|
+
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
|
405
|
+
|
|
406
|
+
LOGIN_URL = reverse_lazy("admin:login")
|
|
407
|
+
LOGIN_REDIRECT_URL = reverse_lazy("admin:index")
|
|
408
|
+
LOGOUT_REDIRECT_URL = reverse_lazy("admin:index")
|
|
409
|
+
|
|
410
|
+
#
|
|
411
|
+
# SECURITY settings
|
|
412
|
+
#
|
|
413
|
+
SESSION_COOKIE_SECURE = IS_HTTPS
|
|
414
|
+
SESSION_COOKIE_HTTPONLY = True
|
|
415
|
+
|
|
416
|
+
CSRF_COOKIE_SECURE = IS_HTTPS
|
|
417
|
+
|
|
418
|
+
X_FRAME_OPTIONS = "DENY"
|
|
419
|
+
|
|
420
|
+
#
|
|
421
|
+
# Silenced checks
|
|
422
|
+
#
|
|
423
|
+
SILENCED_SYSTEM_CHECKS = [
|
|
424
|
+
"rest_framework.W001",
|
|
425
|
+
"debug_toolbar.W006",
|
|
426
|
+
]
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
#
|
|
430
|
+
# Increase number of parameters for GET/POST requests
|
|
431
|
+
#
|
|
432
|
+
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10000
|
|
433
|
+
|
|
434
|
+
#
|
|
435
|
+
# Custom settings
|
|
436
|
+
#
|
|
437
|
+
ENVIRONMENT = config("ENVIRONMENT", "")
|
|
438
|
+
ENVIRONMENT_SHOWN_IN_ADMIN = True
|
|
439
|
+
|
|
440
|
+
# Generating the schema, depending on the component
|
|
441
|
+
subpath = config("SUBPATH", None)
|
|
442
|
+
if subpath:
|
|
443
|
+
if not subpath.startswith("/"):
|
|
444
|
+
subpath = f"/{subpath}"
|
|
445
|
+
SUBPATH = subpath
|
|
446
|
+
|
|
447
|
+
if "GIT_SHA" in os.environ:
|
|
448
|
+
GIT_SHA = config("GIT_SHA", "")
|
|
449
|
+
# in docker (build) context, there is no .git directory
|
|
450
|
+
elif (Path(BASE_DIR) / ".git").exists():
|
|
451
|
+
try:
|
|
452
|
+
import git
|
|
453
|
+
except ImportError:
|
|
454
|
+
GIT_SHA = None
|
|
455
|
+
else:
|
|
456
|
+
repo = git.Repo(search_parent_directories=True)
|
|
457
|
+
GIT_SHA = repo.head.object.hexsha
|
|
458
|
+
else:
|
|
459
|
+
GIT_SHA = None
|
|
460
|
+
|
|
461
|
+
RELEASE = config("RELEASE", GIT_SHA)
|
|
462
|
+
|
|
463
|
+
NUM_PROXIES = config( # TODO: this also is relevant for DRF settings if/when we have rate-limited endpoints
|
|
464
|
+
"NUM_PROXIES",
|
|
465
|
+
default=1,
|
|
466
|
+
cast=lambda val: int(val) if val is not None else None,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
##############################
|
|
470
|
+
# #
|
|
471
|
+
# 3RD PARTY LIBRARY SETTINGS #
|
|
472
|
+
# #
|
|
473
|
+
##############################
|
|
474
|
+
|
|
475
|
+
#
|
|
476
|
+
# DJANGO-AXES (6.0+)
|
|
477
|
+
#
|
|
478
|
+
AXES_CACHE = "axes" # refers to CACHES setting
|
|
479
|
+
# The number of login attempts allowed before a record is created for the
|
|
480
|
+
# failed logins. Default: 3
|
|
481
|
+
AXES_FAILURE_LIMIT = 10
|
|
482
|
+
# If set, defines a period of inactivity after which old failed login attempts
|
|
483
|
+
# will be forgotten. Can be set to a python timedelta object or an integer. If
|
|
484
|
+
# an integer, will be interpreted as a number of hours. Default: None
|
|
485
|
+
AXES_COOLOFF_TIME = 1
|
|
486
|
+
# The number of reverse proxies
|
|
487
|
+
AXES_IPWARE_PROXY_COUNT = NUM_PROXIES - 1 if NUM_PROXIES else None
|
|
488
|
+
# If set, specifies a template to render when a user is locked out. Template
|
|
489
|
+
# receives cooloff_time and failure_limit as context variables. Default: None
|
|
490
|
+
AXES_LOCKOUT_TEMPLATE = "account_blocked.html"
|
|
491
|
+
AXES_LOCKOUT_PARAMETERS = [["ip_address", "user_agent", "username"]]
|
|
492
|
+
AXES_BEHIND_REVERSE_PROXY = IS_HTTPS
|
|
493
|
+
# By default, Axes obfuscates values for formfields named "password", but the admin
|
|
494
|
+
# interface login formfield name is "auth-password", so we want to obfuscate that
|
|
495
|
+
AXES_SENSITIVE_PARAMETERS = ["auth-password"] # nosec
|
|
496
|
+
|
|
497
|
+
# The default meta precedence order
|
|
498
|
+
IPWARE_META_PRECEDENCE_ORDER = (
|
|
499
|
+
"HTTP_X_FORWARDED_FOR",
|
|
500
|
+
"X_FORWARDED_FOR", # <client>, <proxy1>, <proxy2>
|
|
501
|
+
"HTTP_CLIENT_IP",
|
|
502
|
+
"HTTP_X_REAL_IP",
|
|
503
|
+
"HTTP_X_FORWARDED",
|
|
504
|
+
"HTTP_X_CLUSTER_CLIENT_IP",
|
|
505
|
+
"HTTP_FORWARDED_FOR",
|
|
506
|
+
"HTTP_FORWARDED",
|
|
507
|
+
"HTTP_VIA",
|
|
508
|
+
"REMOTE_ADDR",
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
#
|
|
512
|
+
# DJANGO-CORS-MIDDLEWARE
|
|
513
|
+
#
|
|
514
|
+
CORS_ALLOW_ALL_ORIGINS = config("CORS_ALLOW_ALL_ORIGINS", default=False)
|
|
515
|
+
CORS_ALLOWED_ORIGINS = config("CORS_ALLOWED_ORIGINS", split=True, default=[])
|
|
516
|
+
CORS_ALLOWED_ORIGIN_REGEXES = config(
|
|
517
|
+
"CORS_ALLOWED_ORIGIN_REGEXES", split=True, default=[]
|
|
518
|
+
)
|
|
519
|
+
# Authorization is included in default_cors_headers
|
|
520
|
+
CORS_ALLOW_HEADERS = (
|
|
521
|
+
list(default_cors_headers)
|
|
522
|
+
+ [
|
|
523
|
+
"accept-crs",
|
|
524
|
+
"content-crs",
|
|
525
|
+
]
|
|
526
|
+
+ config("CORS_EXTRA_ALLOW_HEADERS", split=True, default=[])
|
|
527
|
+
)
|
|
528
|
+
CORS_EXPOSE_HEADERS = [
|
|
529
|
+
"content-crs",
|
|
530
|
+
]
|
|
531
|
+
# Django's SESSION_COOKIE_SAMESITE = "Lax" prevents session cookies from being sent
|
|
532
|
+
# cross-domain. There is no need for these cookies to be sent, since the API itself
|
|
533
|
+
# uses Bearer Authentication.
|
|
534
|
+
# we can't easily derive this from django-cors-headers, see also
|
|
535
|
+
# https://pypi.org/project/django-cors-headers/#csrf-integration
|
|
536
|
+
#
|
|
537
|
+
# So we do a best effort attempt at re-using configuration parameters, with an escape
|
|
538
|
+
# hatch to override it.
|
|
539
|
+
CSRF_TRUSTED_ORIGINS = config(
|
|
540
|
+
"CSRF_TRUSTED_ORIGINS",
|
|
541
|
+
split=True,
|
|
542
|
+
default=[strip_protocol_from_origin(origin) for origin in CORS_ALLOWED_ORIGINS],
|
|
543
|
+
)
|
|
544
|
+
#
|
|
545
|
+
# DJANGO-PRIVATES -- safely serve files after authorization
|
|
546
|
+
#
|
|
547
|
+
PRIVATE_MEDIA_ROOT = Path(BASE_DIR) / "private-media"
|
|
548
|
+
PRIVATE_MEDIA_URL = "/private-media/"
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
#
|
|
552
|
+
# NOTIFICATIONS-API-COMMON
|
|
553
|
+
#
|
|
554
|
+
NOTIFICATIONS_DISABLED = config("NOTIFICATIONS_DISABLED", default=False)
|
|
555
|
+
|
|
556
|
+
#
|
|
557
|
+
# SENTRY - error monitoring
|
|
558
|
+
#
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def init_sentry(before_send: Callable | None = None):
|
|
562
|
+
SENTRY_DSN = config("SENTRY_DSN", None)
|
|
563
|
+
|
|
564
|
+
if SENTRY_DSN:
|
|
565
|
+
SENTRY_CONFIG = {
|
|
566
|
+
"dsn": SENTRY_DSN,
|
|
567
|
+
"release": RELEASE or "RELEASE not set",
|
|
568
|
+
"environment": ENVIRONMENT,
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
# Allow projects to define their own before_send filters
|
|
572
|
+
if before_send:
|
|
573
|
+
SENTRY_CONFIG["before_send"] = before_send
|
|
574
|
+
|
|
575
|
+
sentry_sdk.init(
|
|
576
|
+
**SENTRY_CONFIG,
|
|
577
|
+
integrations=get_sentry_integrations(),
|
|
578
|
+
send_default_pii=True,
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
#
|
|
583
|
+
# CELERY
|
|
584
|
+
#
|
|
585
|
+
CELERY_BROKER_URL = config("CELERY_RESULT_BACKEND", "redis://localhost:6379/1")
|
|
586
|
+
CELERY_RESULT_BACKEND = config("CELERY_RESULT_BACKEND", "redis://localhost:6379/1")
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
#
|
|
590
|
+
# DJANGO-ADMIN-INDEX
|
|
591
|
+
#
|
|
592
|
+
ADMIN_INDEX_SHOW_REMAINING_APPS_TO_SUPERUSERS = False
|
|
593
|
+
ADMIN_INDEX_AUTO_CREATE_APP_GROUP = False
|
|
594
|
+
|
|
595
|
+
#
|
|
596
|
+
# Mozilla Django OIDC DB settings
|
|
597
|
+
#
|
|
598
|
+
OIDC_AUTHENTICATE_CLASS = "mozilla_django_oidc_db.views.OIDCAuthenticationRequestView"
|
|
599
|
+
MOZILLA_DJANGO_OIDC_DB_CACHE = "oidc"
|
|
600
|
+
MOZILLA_DJANGO_OIDC_DB_CACHE_TIMEOUT = 5 * 60
|
|
601
|
+
|
|
602
|
+
#
|
|
603
|
+
# Elastic APM
|
|
604
|
+
#
|
|
605
|
+
ELASTIC_APM_SERVER_URL = config("ELASTIC_APM_SERVER_URL", None)
|
|
606
|
+
ELASTIC_APM = {
|
|
607
|
+
# FIXME this does change the default service name, because PROJECT_DIRNAME != PROJECT_NAME
|
|
608
|
+
"SERVICE_NAME": config(
|
|
609
|
+
"ELASTIC_APM_SERVICE_NAME", f"{PROJECT_DIRNAME} - {ENVIRONMENT}"
|
|
610
|
+
),
|
|
611
|
+
"SECRET_TOKEN": config("ELASTIC_APM_SECRET_TOKEN", "default"),
|
|
612
|
+
"SERVER_URL": ELASTIC_APM_SERVER_URL,
|
|
613
|
+
}
|
|
614
|
+
if not ELASTIC_APM_SERVER_URL:
|
|
615
|
+
ELASTIC_APM["ENABLED"] = False
|
|
616
|
+
ELASTIC_APM["SERVER_URL"] = "http://localhost:8200"
|
|
617
|
+
else:
|
|
618
|
+
MIDDLEWARE = ["elasticapm.contrib.django.middleware.TracingMiddleware"] + MIDDLEWARE
|
|
619
|
+
INSTALLED_APPS = INSTALLED_APPS + [
|
|
620
|
+
"elasticapm.contrib.django",
|
|
621
|
+
]
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
#
|
|
625
|
+
# MAYKIN-2FA
|
|
626
|
+
# Uses django-two-factor-auth under the hood, so relevant upstream package settings
|
|
627
|
+
# apply too.
|
|
628
|
+
#
|
|
629
|
+
|
|
630
|
+
# we run the admin site monkeypatch instead.
|
|
631
|
+
TWO_FACTOR_PATCH_ADMIN = False
|
|
632
|
+
# add entries from AUTHENTICATION_BACKENDS that already enforce their own two-factor
|
|
633
|
+
# auth, avoiding having some set up MFA again in the project.
|
|
634
|
+
MAYKIN_2FA_ALLOW_MFA_BYPASS_BACKENDS = [
|
|
635
|
+
"mozilla_django_oidc_db.backends.OIDCAuthenticationBackend",
|
|
636
|
+
]
|
|
637
|
+
|
|
638
|
+
if config("DISABLE_2FA", default=False): # pragma: no cover
|
|
639
|
+
MAYKIN_2FA_ALLOW_MFA_BYPASS_BACKENDS = AUTHENTICATION_BACKENDS
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
#
|
|
643
|
+
# LOG OUTGOING REQUESTS
|
|
644
|
+
#
|
|
645
|
+
LOG_OUTGOING_REQUESTS_EMIT_BODY = True
|
|
646
|
+
LOG_OUTGOING_REQUESTS_DB_SAVE = config("LOG_OUTGOING_REQUESTS_DB_SAVE", default=False)
|
|
647
|
+
LOG_OUTGOING_REQUESTS_RESET_DB_SAVE_AFTER = None
|
|
648
|
+
LOG_OUTGOING_REQUESTS_MAX_AGE = config(
|
|
649
|
+
"LOG_OUTGOING_REQUESTS_MAX_AGE", default=7
|
|
650
|
+
) # number of days
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
from urllib.parse import urlparse
|
|
5
|
+
|
|
6
|
+
from decouple import Csv, config as _config, undefined
|
|
7
|
+
from sentry_sdk.integrations import DidNotEnable, django, redis
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def config(option: str, default: Any = undefined, *args, **kwargs):
|
|
11
|
+
"""
|
|
12
|
+
Pull a config parameter from the environment.
|
|
13
|
+
|
|
14
|
+
Read the config variable ``option``. If it's optional, use the ``default`` value.
|
|
15
|
+
Input is automatically cast to the correct type, where the type is derived from the
|
|
16
|
+
default value if possible.
|
|
17
|
+
|
|
18
|
+
Pass ``split=True`` to split the comma-separated input into a list.
|
|
19
|
+
"""
|
|
20
|
+
if "split" in kwargs:
|
|
21
|
+
kwargs.pop("split")
|
|
22
|
+
kwargs["cast"] = Csv()
|
|
23
|
+
if isinstance(default, list):
|
|
24
|
+
default = ",".join(default)
|
|
25
|
+
|
|
26
|
+
if default is not undefined and default is not None:
|
|
27
|
+
kwargs.setdefault("cast", type(default))
|
|
28
|
+
return _config(option, default=default, *args, **kwargs)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_sentry_integrations() -> list:
|
|
32
|
+
"""
|
|
33
|
+
Determine which Sentry SDK integrations to enable.
|
|
34
|
+
"""
|
|
35
|
+
default = [
|
|
36
|
+
django.DjangoIntegration(),
|
|
37
|
+
redis.RedisIntegration(),
|
|
38
|
+
]
|
|
39
|
+
extra = []
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
from sentry_sdk.integrations import celery
|
|
43
|
+
except DidNotEnable: # happens if the celery import fails by the integration
|
|
44
|
+
pass
|
|
45
|
+
else:
|
|
46
|
+
extra.append(celery.CeleryIntegration())
|
|
47
|
+
|
|
48
|
+
return [*default, *extra]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def strip_protocol_from_origin(origin: str) -> str:
|
|
52
|
+
parsed = urlparse(origin)
|
|
53
|
+
return parsed.netloc
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_project_dirname() -> str:
|
|
57
|
+
return config("DJANGO_SETTINGS_MODULE").split(".")[0]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_django_project_dir() -> str:
|
|
61
|
+
# Get the path of the importing module
|
|
62
|
+
base_dirname = get_project_dirname()
|
|
63
|
+
return Path(sys.modules[base_dirname].__file__).parent
|
|
File without changes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: open_api_framework
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.3.0
|
|
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 2024 Maykin Media
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ Project-URL: Homepage, https://github.com/maykinmedia/open_api_framework
|
|
|
15
15
|
Project-URL: Documentation, http://open_api_framework.readthedocs.io/en/latest/
|
|
16
16
|
Project-URL: Bug Tracker, https://github.com/maykinmedia/open_api_framework/issues
|
|
17
17
|
Project-URL: Source Code, https://github.com/maykinmedia/open_api_framework
|
|
18
|
-
Keywords:
|
|
18
|
+
Keywords: metapackage,dependencies,settings
|
|
19
19
|
Classifier: Development Status :: 3 - Alpha
|
|
20
20
|
Classifier: Framework :: Django
|
|
21
21
|
Classifier: Framework :: Django :: 4.2
|
|
@@ -35,6 +35,7 @@ Requires-Dist: django-axes>=6.3.0
|
|
|
35
35
|
Requires-Dist: django-cors-headers>=4.3.1
|
|
36
36
|
Requires-Dist: django-markup>=1.8.1
|
|
37
37
|
Requires-Dist: django-jsonform>=2.21.4
|
|
38
|
+
Requires-Dist: django-log-outgoing-requests>=0.6.1
|
|
38
39
|
Requires-Dist: django-admin-index>=3.1.0
|
|
39
40
|
Requires-Dist: django-redis>=5.4.0
|
|
40
41
|
Requires-Dist: djangorestframework>=3.12.4
|
|
@@ -77,7 +78,7 @@ Requires-Dist: twine; extra == "release"
|
|
|
77
78
|
Welcome to open-api-framework's documentation!
|
|
78
79
|
=================================================
|
|
79
80
|
|
|
80
|
-
:Version: 0.
|
|
81
|
+
:Version: 0.3.0
|
|
81
82
|
:Source: https://github.com/maykinmedia/open-api-framework
|
|
82
83
|
:Keywords: metapackage, dependencies
|
|
83
84
|
|
|
@@ -116,7 +117,7 @@ Install
|
|
|
116
117
|
Local development
|
|
117
118
|
=================
|
|
118
119
|
|
|
119
|
-
To install and develop the library locally, use
|
|
120
|
+
To install and develop the library locally, use:
|
|
120
121
|
|
|
121
122
|
.. code-block:: bash
|
|
122
123
|
|
{open_api_framework-0.2.0 → open_api_framework-0.3.0}/open_api_framework.egg-info/SOURCES.txt
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
CHANGELOG.rst
|
|
1
2
|
LICENSE
|
|
2
3
|
MANIFEST.in
|
|
3
4
|
README.rst
|
|
@@ -11,4 +12,7 @@ open_api_framework.egg-info/SOURCES.txt
|
|
|
11
12
|
open_api_framework.egg-info/dependency_links.txt
|
|
12
13
|
open_api_framework.egg-info/requires.txt
|
|
13
14
|
open_api_framework.egg-info/top_level.txt
|
|
14
|
-
|
|
15
|
+
open_api_framework/conf/__init__.py
|
|
16
|
+
open_api_framework/conf/base.py
|
|
17
|
+
open_api_framework/conf/utils.py
|
|
18
|
+
tests/test_config_helpers.py
|
|
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "open_api_framework"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.3.0"
|
|
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"}
|
|
11
11
|
]
|
|
12
12
|
readme = "README.rst"
|
|
13
13
|
license = {file = "LICENSE"}
|
|
14
|
-
keywords = ["
|
|
14
|
+
keywords = ["metapackage", "dependencies", "settings"]
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 3 - Alpha",
|
|
17
17
|
"Framework :: Django",
|
|
@@ -32,6 +32,7 @@ dependencies = [
|
|
|
32
32
|
"django-cors-headers>=4.3.1",
|
|
33
33
|
"django-markup>=1.8.1",
|
|
34
34
|
"django-jsonform>=2.21.4",
|
|
35
|
+
"django-log-outgoing-requests>=0.6.1",
|
|
35
36
|
"django-admin-index>=3.1.0",
|
|
36
37
|
"django-redis>=5.4.0",
|
|
37
38
|
"djangorestframework>=3.12.4",
|
|
@@ -98,7 +99,7 @@ testpaths = ["tests"]
|
|
|
98
99
|
DJANGO_SETTINGS_MODULE = "testapp.settings"
|
|
99
100
|
|
|
100
101
|
[tool.bumpversion]
|
|
101
|
-
current_version = "0.
|
|
102
|
+
current_version = "0.3.0"
|
|
102
103
|
files = [
|
|
103
104
|
{filename = "pyproject.toml"},
|
|
104
105
|
{filename = "README.rst"},
|
|
@@ -0,0 +1,13 @@
|
|
|
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=[])
|
|
6
|
+
|
|
7
|
+
assert value == []
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_non_empty_list_as_default():
|
|
11
|
+
value = config("SOME_TEST_ENVVAR", split=True, default=["foo"])
|
|
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.2.0 → open_api_framework-0.3.0}/open_api_framework.egg-info/top_level.txt
RENAMED
|
File without changes
|