diff --git a/docker-compose.yml b/docker-compose.yml index 781bb10f80c8..ebfca6e3224d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -68,6 +68,8 @@ services: test: ["CMD-SHELL", "DJANGO_SETTINGS_MODULE=olympia celery -A olympia.amo.celery status"] interval: 1s retries: 100 + depends_on: + - mysqld web: <<: *worker diff --git a/requirements/prod.txt b/requirements/prod.txt index 84519d3be54d..7279b5a68196 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -1231,3 +1231,118 @@ watchdog[watchmedo]==3.0.0 \ django-node-assets==0.9.14 \ --hash=sha256:80cbe3d10521808309712b2aa5ef6d69799bbcafef844cf7f223d3c93f405768 \ --hash=sha256:d5b5c472136084d533268f52ab77897327863a102e25c81f484aae85eb806987 +drf-spectacular==0.27.2 \ + --hash=sha256:a199492f2163c4101055075ebdbb037d59c6e0030692fc83a1a8c0fc65929981 \ + --hash=sha256:b1c04bf8b2fbbeaf6f59414b4ea448c8787aba4d32f76055c3b13335cf7ec37b +jsonschema==4.23.0 \ + --hash=sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4 \ + --hash=sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566 +referencing==0.35.1 \ + --hash=sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c \ + --hash=sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de +rpds-py==0.19.0 \ + --hash=sha256:0121803b0f424ee2109d6e1f27db45b166ebaa4b32ff47d6aa225642636cd834 \ + --hash=sha256:06925c50f86da0596b9c3c64c3837b2481337b83ef3519e5db2701df695453a4 \ + --hash=sha256:071d4adc734de562bd11d43bd134330fb6249769b2f66b9310dab7460f4bf714 \ + --hash=sha256:1540d807364c84516417115c38f0119dfec5ea5c0dd9a25332dea60b1d26fc4d \ + --hash=sha256:15e65395a59d2e0e96caf8ee5389ffb4604e980479c32742936ddd7ade914b22 \ + --hash=sha256:19d02c45f2507b489fd4df7b827940f1420480b3e2e471e952af4d44a1ea8e34 \ + --hash=sha256:1c26da90b8d06227d7769f34915913911222d24ce08c0ab2d60b354e2d9c7aff \ + --hash=sha256:1d16089dfa58719c98a1c06f2daceba6d8e3fb9b5d7931af4a990a3c486241cb \ + --hash=sha256:1dd46f309e953927dd018567d6a9e2fb84783963650171f6c5fe7e5c41fd5666 \ + --hash=sha256:2575efaa5d949c9f4e2cdbe7d805d02122c16065bfb8d95c129372d65a291a0b \ + --hash=sha256:3208f9aea18991ac7f2b39721e947bbd752a1abbe79ad90d9b6a84a74d44409b \ + --hash=sha256:329c719d31362355a96b435f4653e3b4b061fcc9eba9f91dd40804ca637d914e \ + --hash=sha256:3384d278df99ec2c6acf701d067147320b864ef6727405d6470838476e44d9e8 \ + --hash=sha256:34a01a4490e170376cd79258b7f755fa13b1a6c3667e872c8e35051ae857a92b \ + --hash=sha256:354f3a91718489912f2e0fc331c24eaaf6a4565c080e00fbedb6015857c00582 \ + --hash=sha256:37f46bb11858717e0efa7893c0f7055c43b44c103e40e69442db5061cb26ed34 \ + --hash=sha256:3b4cf5a9497874822341c2ebe0d5850fed392034caadc0bad134ab6822c0925b \ + --hash=sha256:3f148c3f47f7f29a79c38cc5d020edcb5ca780020fab94dbc21f9af95c463581 \ + --hash=sha256:443cec402ddd650bb2b885113e1dcedb22b1175c6be223b14246a714b61cd521 \ + --hash=sha256:462b0c18fbb48fdbf980914a02ee38c423a25fcc4cf40f66bacc95a2d2d73bc8 \ + --hash=sha256:474bc83233abdcf2124ed3f66230a1c8435896046caa4b0b5ab6013c640803cc \ + --hash=sha256:4d438e4c020d8c39961deaf58f6913b1bf8832d9b6f62ec35bd93e97807e9cbc \ + --hash=sha256:4fdc9afadbeb393b4bbbad75481e0ea78e4469f2e1d713a90811700830b553a9 \ + --hash=sha256:5039e3cef7b3e7a060de468a4a60a60a1f31786da94c6cb054e7a3c75906111c \ + --hash=sha256:5095a7c838a8647c32aa37c3a460d2c48debff7fc26e1136aee60100a8cd8f68 \ + --hash=sha256:52e466bea6f8f3a44b1234570244b1cff45150f59a4acae3fcc5fd700c2993ca \ + --hash=sha256:535d4b52524a961d220875688159277f0e9eeeda0ac45e766092bfb54437543f \ + --hash=sha256:57dbc9167d48e355e2569346b5aa4077f29bf86389c924df25c0a8b9124461fb \ + --hash=sha256:5a4b07cdf3f84310c08c1de2c12ddadbb7a77568bcb16e95489f9c81074322ed \ + --hash=sha256:5c872814b77a4e84afa293a1bee08c14daed1068b2bb1cc312edbf020bbbca2b \ + --hash=sha256:5f83689a38e76969327e9b682be5521d87a0c9e5a2e187d2bc6be4765f0d4600 \ + --hash=sha256:688aa6b8aa724db1596514751ffb767766e02e5c4a87486ab36b8e1ebc1aedac \ + --hash=sha256:6b130bd4163c93798a6b9bb96be64a7c43e1cec81126ffa7ffaa106e1fc5cef5 \ + --hash=sha256:6b31f059878eb1f5da8b2fd82480cc18bed8dcd7fb8fe68370e2e6285fa86da6 \ + --hash=sha256:6d45080095e585f8c5097897313def60caa2046da202cdb17a01f147fb263b81 \ + --hash=sha256:6f2f78ef14077e08856e788fa482107aa602636c16c25bdf59c22ea525a785e9 \ + --hash=sha256:6fe87efd7f47266dfc42fe76dae89060038f1d9cb911f89ae7e5084148d1cc08 \ + --hash=sha256:75969cf900d7be665ccb1622a9aba225cf386bbc9c3bcfeeab9f62b5048f4a07 \ + --hash=sha256:75a6076289b2df6c8ecb9d13ff79ae0cad1d5fb40af377a5021016d58cd691ec \ + --hash=sha256:78d57546bad81e0da13263e4c9ce30e96dcbe720dbff5ada08d2600a3502e526 \ + --hash=sha256:79e205c70afddd41f6ee79a8656aec738492a550247a7af697d5bd1aee14f766 \ + --hash=sha256:7c98298a15d6b90c8f6e3caa6457f4f022423caa5fa1a1ca7a5e9e512bdb77a4 \ + --hash=sha256:7ec72df7354e6b7f6eb2a17fa6901350018c3a9ad78e48d7b2b54d0412539a67 \ + --hash=sha256:81ea573aa46d3b6b3d890cd3c0ad82105985e6058a4baed03cf92518081eec8c \ + --hash=sha256:8344127403dea42f5970adccf6c5957a71a47f522171fafaf4c6ddb41b61703a \ + --hash=sha256:8445f23f13339da640d1be8e44e5baf4af97e396882ebbf1692aecd67f67c479 \ + --hash=sha256:850720e1b383df199b8433a20e02b25b72f0fded28bc03c5bd79e2ce7ef050be \ + --hash=sha256:88cb4bac7185a9f0168d38c01d7a00addece9822a52870eee26b8d5b61409213 \ + --hash=sha256:8a790d235b9d39c70a466200d506bb33a98e2ee374a9b4eec7a8ac64c2c261fa \ + --hash=sha256:8b1a94b8afc154fbe36978a511a1f155f9bd97664e4f1f7a374d72e180ceb0ae \ + --hash=sha256:8d6ad132b1bc13d05ffe5b85e7a01a3998bf3a6302ba594b28d61b8c2cf13aaf \ + --hash=sha256:8eb488ef928cdbc05a27245e52de73c0d7c72a34240ef4d9893fdf65a8c1a955 \ + --hash=sha256:90bf55d9d139e5d127193170f38c584ed3c79e16638890d2e36f23aa1630b952 \ + --hash=sha256:9133d75dc119a61d1a0ded38fb9ba40a00ef41697cc07adb6ae098c875195a3f \ + --hash=sha256:93a91c2640645303e874eada51f4f33351b84b351a689d470f8108d0e0694210 \ + --hash=sha256:959179efb3e4a27610e8d54d667c02a9feaa86bbabaf63efa7faa4dfa780d4f1 \ + --hash=sha256:9625367c8955e4319049113ea4f8fee0c6c1145192d57946c6ffcd8fe8bf48dd \ + --hash=sha256:9da6f400eeb8c36f72ef6646ea530d6d175a4f77ff2ed8dfd6352842274c1d8b \ + --hash=sha256:9e65489222b410f79711dc3d2d5003d2757e30874096b2008d50329ea4d0f88c \ + --hash=sha256:a3e2fd14c5d49ee1da322672375963f19f32b3d5953f0615b175ff7b9d38daed \ + --hash=sha256:a5a7c1062ef8aea3eda149f08120f10795835fc1c8bc6ad948fb9652a113ca55 \ + --hash=sha256:a5da93debdfe27b2bfc69eefb592e1831d957b9535e0943a0ee8b97996de21b5 \ + --hash=sha256:a6e605bb9edcf010f54f8b6a590dd23a4b40a8cb141255eec2a03db249bc915b \ + --hash=sha256:a707b158b4410aefb6b054715545bbb21aaa5d5d0080217290131c49c2124a6e \ + --hash=sha256:a8b6683a37338818646af718c9ca2a07f89787551057fae57c4ec0446dc6224b \ + --hash=sha256:aa5476c3e3a402c37779e95f7b4048db2cb5b0ed0b9d006983965e93f40fe05a \ + --hash=sha256:ab1932ca6cb8c7499a4d87cb21ccc0d3326f172cfb6a64021a889b591bb3045c \ + --hash=sha256:ae8b6068ee374fdfab63689be0963333aa83b0815ead5d8648389a8ded593378 \ + --hash=sha256:b0906357f90784a66e89ae3eadc2654f36c580a7d65cf63e6a616e4aec3a81be \ + --hash=sha256:b0da31853ab6e58a11db3205729133ce0df26e6804e93079dee095be3d681dc1 \ + --hash=sha256:b1c30841f5040de47a0046c243fc1b44ddc87d1b12435a43b8edff7e7cb1e0d0 \ + --hash=sha256:b228e693a2559888790936e20f5f88b6e9f8162c681830eda303bad7517b4d5a \ + --hash=sha256:b7cc6cb44f8636fbf4a934ca72f3e786ba3c9f9ba4f4d74611e7da80684e48d2 \ + --hash=sha256:ba0ed0dc6763d8bd6e5de5cf0d746d28e706a10b615ea382ac0ab17bb7388633 \ + --hash=sha256:bc9128e74fe94650367fe23f37074f121b9f796cabbd2f928f13e9661837296d \ + --hash=sha256:bcf426a8c38eb57f7bf28932e68425ba86def6e756a5b8cb4731d8e62e4e0223 \ + --hash=sha256:bec35eb20792ea64c3c57891bc3ca0bedb2884fbac2c8249d9b731447ecde4fa \ + --hash=sha256:c3444fe52b82f122d8a99bf66777aed6b858d392b12f4c317da19f8234db4533 \ + --hash=sha256:c5c9581019c96f865483d031691a5ff1cc455feb4d84fc6920a5ffc48a794d8a \ + --hash=sha256:c6feacd1d178c30e5bc37184526e56740342fd2aa6371a28367bad7908d454fc \ + --hash=sha256:c8f77e661ffd96ff104bebf7d0f3255b02aa5d5b28326f5408d6284c4a8b3248 \ + --hash=sha256:cb0f6eb3a320f24b94d177e62f4074ff438f2ad9d27e75a46221904ef21a7b05 \ + --hash=sha256:ce84a7efa5af9f54c0aa7692c45861c1667080814286cacb9958c07fc50294fb \ + --hash=sha256:cf902878b4af334a09de7a45badbff0389e7cf8dc2e4dcf5f07125d0b7c2656d \ + --hash=sha256:dab8d921b55a28287733263c0e4c7db11b3ee22aee158a4de09f13c93283c62d \ + --hash=sha256:dc9ac4659456bde7c567107556ab065801622396b435a3ff213daef27b495388 \ + --hash=sha256:dd36b712d35e757e28bf2f40a71e8f8a2d43c8b026d881aa0c617b450d6865c9 \ + --hash=sha256:e19509145275d46bc4d1e16af0b57a12d227c8253655a46bbd5ec317e941279d \ + --hash=sha256:e21cc693045fda7f745c790cb687958161ce172ffe3c5719ca1764e752237d16 \ + --hash=sha256:e54548e0be3ac117595408fd4ca0ac9278fde89829b0b518be92863b17ff67a2 \ + --hash=sha256:e5b9fc03bf76a94065299d4a2ecd8dfbae4ae8e2e8098bbfa6ab6413ca267709 \ + --hash=sha256:e8481b946792415adc07410420d6fc65a352b45d347b78fec45d8f8f0d7496f0 \ + --hash=sha256:ebcbf356bf5c51afc3290e491d3722b26aaf5b6af3c1c7f6a1b757828a46e336 \ + --hash=sha256:ef9101f3f7b59043a34f1dccbb385ca760467590951952d6701df0da9893ca0c \ + --hash=sha256:f2afd2164a1e85226fcb6a1da77a5c8896c18bfe08e82e8ceced5181c42d2179 \ + --hash=sha256:f629ecc2db6a4736b5ba95a8347b0089240d69ad14ac364f557d52ad68cf94b0 \ + --hash=sha256:f68eea5df6347d3f1378ce992d86b2af16ad7ff4dcb4a19ccdc23dea901b87fb \ + --hash=sha256:f757f359f30ec7dcebca662a6bd46d1098f8b9fb1fcd661a9e13f2e8ce343ba1 \ + --hash=sha256:fb37bd599f031f1a6fb9e58ec62864ccf3ad549cf14bac527dbfa97123edcca4 +jsonschema-specifications==2023.12.1 \ + --hash=sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc \ + --hash=sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c +drf-spectacular-sidecar==2024.7.1 \ + --hash=sha256:5dc8b38ad153e90b328152674c7959bf114bf86360a617a5a4516e135cb832bc \ + --hash=sha256:beb992d6ece806a2d422ad626983e2472c0a5550de9647a7ed6764716a5abdfe diff --git a/settings.py b/settings.py index dbb5be584c93..d1cf014114e4 100644 --- a/settings.py +++ b/settings.py @@ -147,31 +147,3 @@ def insert_debug_toolbar_middleware(middlewares): ADDONS_SERVER_DOCS_URL = 'https://addons-server.readthedocs.io/en/latest' - -SWAGGER_SETTINGS = { - 'USE_SESSION_AUTH': False, - 'DEEP_LINKING': True, - 'SECURITY_DEFINITIONS': { - 'Session ID': { - 'type': 'apiKey', - 'name': 'Authorization', - 'in': 'header', - 'description': ( - 'Use your session ID found in the sessionid cookie. See the ' - f'[docs]({ADDONS_SERVER_DOCS_URL}/topics/api/auth_internal.html). \n' - 'Format as `Session `' - ), - }, - 'JWT': { - 'type': 'apiKey', - 'name': 'Authorization', - 'in': 'header', - 'description': ( - 'Use JWT token. see the ' - f'[docs]({ADDONS_SERVER_DOCS_URL}/topics/api/auth.html). \n' - 'Format as `JWT `' - ), - }, - }, - 'PERSIST_AUTH': True, -} diff --git a/src/olympia/addons/views.py b/src/olympia/addons/views.py index 7f4128bae79c..41db37b8f6c8 100644 --- a/src/olympia/addons/views.py +++ b/src/olympia/addons/views.py @@ -7,7 +7,7 @@ from django.utils.cache import patch_cache_control from django.utils.translation import gettext -from drf_yasg.utils import swagger_auto_schema +from drf_spectacular.utils import extend_schema_view, extend_schema from elasticsearch_dsl import Q, Search, query from rest_framework import exceptions, serializers, status from rest_framework.decorators import action @@ -210,7 +210,21 @@ def find_replacement_addon(request): ) return redirect(replace_url, permanent=False) +@extend_schema_view( + create=extend_schema( + description=""" + This endpoint allows a submission of an upload to create a new add-on + and setting other AMO metadata. + To create an add-on with a listed version from an upload + (an upload that has channel == listed) certain metadata must be defined + - version license + - add-on name + - add-on summary + - add-on categories for each app the version is compatible with. + """ + ) +) class AddonViewSet( CreateModelMixin, RetrieveModelMixin, @@ -399,23 +413,6 @@ def update(self, request, *args, **kwargs): self.action = 'create' return self.create(request, *args, **kwargs) - @swagger_auto_schema( - operation_description=""" - This endpoint allows a submission of an upload to create a new add-on - and setting other AMO metadata. - - To create an add-on with a listed version from an upload - (an upload that has channel == listed) certain metadata must be defined - - version license - - add-on name - - add-on summary - - add-on categories for each app the version is compatible with. - """ - ) - def create(self, request, *args, **kwargs): - response = super().create(request, *args, **kwargs) - return response - class AddonChildMixin: """Mixin containing method to retrieve the parent add-on object.""" diff --git a/src/olympia/api/urls.py b/src/olympia/api/urls.py index 9bde639a0557..8710eaca926b 100644 --- a/src/olympia/api/urls.py +++ b/src/olympia/api/urls.py @@ -1,29 +1,20 @@ from django.conf import settings from django.urls import include, re_path -from drf_yasg import openapi -from drf_yasg.views import get_schema_view -from rest_framework import permissions +from drf_spectacular.views import ( + SpectacularAPIView, + SpectacularRedocView, + SpectacularSwaggerSplitView, +) from olympia.accounts.urls import accounts_v3, accounts_v4, auth_urls from olympia.addons.api_urls import addons_v3, addons_v4, addons_v5 from olympia.amo.urls import api_patterns as amo_api_patterns from olympia.ratings.api_urls import ratings_v3, ratings_v4 - def get_versioned_api_routes(version, url_patterns): route_pattern = r'^{}/'.format(version) - schema_view = get_schema_view( - openapi.Info( - title='AMO API', - default_version=version, - description='The official API for addons.mozilla.org.', - ), - public=True, - permission_classes=(permissions.AllowAny,), - ) - routes = url_patterns # For now, this feature is only enabled in dev mode @@ -31,18 +22,18 @@ def get_versioned_api_routes(version, url_patterns): routes.extend( [ re_path( - r'^swagger(?P\.json|\.yaml)$', - schema_view.without_ui(cache_timeout=0), - name='schema-json', + r'^schema/$', + SpectacularAPIView.as_view(), + name='schema', ), re_path( r'^swagger/$', - schema_view.with_ui('swagger', cache_timeout=0), + SpectacularSwaggerSplitView.as_view(url_name='schema'), name='schema-swagger-ui', ), re_path( r'^redoc/$', - schema_view.with_ui('redoc', cache_timeout=0), + SpectacularRedocView.as_view(url_name='schema'), name='schema-redoc', ), ] diff --git a/src/olympia/core/apps.py b/src/olympia/core/apps.py index 6bc556df83ee..bf7fb906fe27 100644 --- a/src/olympia/core/apps.py +++ b/src/olympia/core/apps.py @@ -21,6 +21,26 @@ class CustomTags(Tags): custom_setup = 'setup' +@register(CustomTags.custom_setup) +def swagger_check(app_configs, **kwargs): + """Check that the swagger schema can be generated.""" + schema_path = settings.SWAGGER_SCHEMA_FILE + errors = [] + try: + call_command('spectacular', file=schema_path, validate=True) + # remove schema file after validation + os.remove(schema_path) + except Exception as exc: + errors.append( + Error( + f'Error generating schema: {exc}', + id='setup.E001', + ) + ) + + return errors + + @register(CustomTags.custom_setup) def uwsgi_check(app_configs, **kwargs): """Custom check triggered when ./manage.py check is ran (should be done diff --git a/src/olympia/lib/settings_base.py b/src/olympia/lib/settings_base.py index caf3fe03debf..f1369745ed2d 100644 --- a/src/olympia/lib/settings_base.py +++ b/src/olympia/lib/settings_base.py @@ -332,8 +332,9 @@ def get_db_config(environ_var, atomic_requests=True): # Django's sitemap_index.xml template uses some syntax that jinja doesn't support r'sitemap_index.xml', # Swagger URLs are for the API docs, use some syntax that jinja doesn't support - r'drf-yasg/swagger-ui.html', - r'drf-yasg/redoc.html', + r'drf_spectacular/swagger_ui.html', + r'drf_spectacular/redoc.html', + r'drf_spectacular/swagger_ui.js', ) TEMPLATES = [ @@ -533,8 +534,9 @@ def get_db_config(environ_var, atomic_requests=True): 'django_jinja', 'rangefilter', 'django_recaptcha', - 'drf_yasg', 'django_node_assets', + 'drf_spectacular', + 'drf_spectacular_sidecar', # Django contrib apps 'django.contrib.admin', 'django.contrib.auth', @@ -1075,6 +1077,7 @@ def get_db_config(environ_var, atomic_requests=True): CSP_OBJECT_SRC = ("'none'",) CSP_SCRIPT_SRC = ( + "'self'", GOOGLE_ANALYTICS_HOST, GOOGLE_TAGMANAGER_HOST, 'https://www.recaptcha.net/recaptcha/', @@ -1437,6 +1440,8 @@ def read_only_mode(env): # Use our pagination class by default, which allows clients to request a # different page size. 'TEST_REQUEST_DEFAULT_FORMAT': 'json', + # register spectacular AutoSchema with DRF. + 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', } # We need to load this before sentry_sdk.init or our reverse replacement is too late. @@ -1573,3 +1578,17 @@ def read_only_mode(env): SOCKET_LABS_HOST = env('SOCKET_LABS_HOST', default='https://api.socketlabs.com/v2/') SOCKET_LABS_TOKEN = env('SOCKET_LABS_TOKEN', default=None) SOCKET_LABS_SERVER_ID = env('SOCKET_LABS_SERVER_ID', default=None) + +SPECTACULAR_SETTINGS = { + 'TITLE': 'AMO API', + 'DESCRIPTION': 'Addons Server API Documentation', + 'SERVE_INCLUDE_SCHEMA': True, + 'SERVE_PERMISSIONS': ['rest_framework.permissions.AllowAny'], + # load swagger/redoc assets via collectstatic assets + # rather than via the CDN + 'SWAGGER_UI_DIST': 'SIDECAR', + 'SWAGGER_UI_FAVICON_HREF': 'SIDECAR', + 'REDOC_DIST': 'SIDECAR', +} + +SWAGGER_SCHEMA_FILE = path('schema.yml')