mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-07-03 03:38:56 +00:00
add ability to generate legacy API with legacy naming scheme
regenerate legacy API
This commit is contained in:
parent
4fa7155fc3
commit
4771f890cb
11 changed files with 21299 additions and 13370 deletions
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
# WARNING: COMPONENT_SPLIT_REQUEST must be enabled, if not schemas might be wrong
|
||||
|
||||
|
||||
def custom_postprocessing_hook(result, generator, request, public):
|
||||
for c in result['components']['schemas'].keys():
|
||||
# handle schemas used by the client to do requests on the server
|
||||
|
|
@ -36,4 +37,90 @@ def custom_postprocessing_hook(result, generator, request, public):
|
|||
else:
|
||||
result['components']['schemas'][c]['required'].append('id')
|
||||
|
||||
return result
|
||||
return result
|
||||
|
||||
|
||||
# TODO remove below once legacy API has been fully deprecated
|
||||
from drf_spectacular.openapi import AutoSchema # noqa: E402 isort: skip
|
||||
import functools # noqa: E402 isort: skip
|
||||
import re # noqa: E402 isort: skip
|
||||
|
||||
|
||||
class LegacySchema(AutoSchema):
|
||||
operation_id_base = None
|
||||
|
||||
@functools.cached_property
|
||||
def path(self):
|
||||
path = re.sub(pattern=self.path_prefix, repl='', string=self.path, flags=re.IGNORECASE)
|
||||
# remove path variables
|
||||
return re.sub(pattern=r'\{[\w\-]+\}', repl='', string=path)
|
||||
|
||||
def get_operation_id(self):
|
||||
"""
|
||||
Compute an operation ID from the view type and get_operation_id_base method.
|
||||
"""
|
||||
method_name = getattr(self.view, 'action', self.method.lower())
|
||||
if self._is_list_view():
|
||||
action = 'list'
|
||||
elif method_name not in self.method_mapping:
|
||||
action = self._to_camel_case(method_name)
|
||||
else:
|
||||
action = self.method_mapping[self.method.lower()]
|
||||
|
||||
name = self.get_operation_id_base(action)
|
||||
|
||||
return action + name
|
||||
|
||||
def get_operation_id_base(self, action):
|
||||
"""
|
||||
Compute the base part for operation ID from the model, serializer or view name.
|
||||
"""
|
||||
model = getattr(getattr(self.view, 'queryset', None), 'model', None)
|
||||
|
||||
if self.operation_id_base is not None:
|
||||
name = self.operation_id_base
|
||||
|
||||
# Try to deduce the ID from the view's model
|
||||
elif model is not None:
|
||||
name = model.__name__
|
||||
|
||||
# Try with the serializer class name
|
||||
elif self.get_serializer() is not None:
|
||||
name = self.get_serializer().__class__.__name__
|
||||
if name.endswith('Serializer'):
|
||||
name = name[:-10]
|
||||
|
||||
# Fallback to the view name
|
||||
else:
|
||||
name = self.view.__class__.__name__
|
||||
if name.endswith('APIView'):
|
||||
name = name[:-7]
|
||||
elif name.endswith('View'):
|
||||
name = name[:-4]
|
||||
|
||||
# Due to camel-casing of classes and `action` being lowercase, apply title in order to find if action truly
|
||||
# comes at the end of the name
|
||||
if name.endswith(action.title()): # ListView, UpdateAPIView, ThingDelete ...
|
||||
name = name[:-len(action)]
|
||||
|
||||
if action == 'list' and not name.endswith('s'): # listThings instead of listThing
|
||||
name += 's'
|
||||
|
||||
return name
|
||||
|
||||
def get_serializer(self):
|
||||
view = self.view
|
||||
|
||||
if not hasattr(view, 'get_serializer'):
|
||||
return None
|
||||
|
||||
try:
|
||||
return view.get_serializer()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _to_camel_case(self, snake_str):
|
||||
components = snake_str.split('_')
|
||||
# We capitalize the first letter of each component except the first one
|
||||
# with the 'title' method and join them together.
|
||||
return components[0] + ''.join(x.title() for x in components[1:])
|
||||
|
|
|
|||
10
cookbook/static/vue3/assets/main-CNHK5kQT.css
Normal file
10
cookbook/static/vue3/assets/main-CNHK5kQT.css
Normal file
File diff suppressed because one or more lines are too long
7821
cookbook/static/vue3/assets/main-MtGxR7il.js
Normal file
7821
cookbook/static/vue3/assets/main-MtGxR7il.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -52,11 +52,12 @@
|
|||
"src": "node_modules/mavon-editor/dist/font/fontello.woff2"
|
||||
},
|
||||
"src/apps/tandoor/main.ts": {
|
||||
"file": "assets/main-CnbWi3Kc.js",
|
||||
"file": "assets/main-MtGxR7il.js",
|
||||
"name": "main",
|
||||
"src": "src/apps/tandoor/main.ts",
|
||||
"isEntry": true,
|
||||
"css": [
|
||||
"assets/main-BiQ-D_PZ.css"
|
||||
"assets/main-CNHK5kQT.css"
|
||||
],
|
||||
"assets": [
|
||||
"assets/brand_logo-B3nCJMk0.svg",
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ from recipe_scrapers._exceptions import NoSchemaFoundInWildMode
|
|||
from requests.exceptions import MissingSchema
|
||||
from rest_framework import decorators, status, viewsets
|
||||
from rest_framework.authtoken.views import ObtainAuthToken
|
||||
from rest_framework.decorators import action, api_view, permission_classes
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.exceptions import APIException, PermissionDenied
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.parsers import MultiPartParser
|
||||
|
|
@ -784,7 +784,7 @@ class MealPlanViewSet(viewsets.ModelViewSet):
|
|||
|
||||
return queryset
|
||||
|
||||
@action(detail=False)
|
||||
@decorators.action(detail=False)
|
||||
def ical(self, request):
|
||||
from_date = self.request.query_params.get('from_date', None)
|
||||
to_date = self.request.query_params.get('to_date', None)
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ PyCharm can be configured to format and lint on save. Doing so requires some man
|
|||
2. Click the '+' to add a new watcher.
|
||||
3. Configure the watcher as below.
|
||||
|
||||

|
||||

|
||||
|
||||
4. Navigate to File -> Settings -> Editor -> Inspections -> File watcher problems
|
||||
5. Under Severity select 'Edit Severities'
|
||||
6. Click the '+' to add a severity calling it 'Linting Error'
|
||||
7. Configure a background and effect as below.
|
||||
|
||||

|
||||

|
||||
|
||||
## Setup isort
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ PyCharm can be configured to format and lint on save. Doing so requires some man
|
|||
2. Click the '+' to add a new watcher.
|
||||
3. Configure the watcher as below.
|
||||
|
||||

|
||||

|
||||
|
||||
## Setup yapf
|
||||
|
||||
|
|
@ -36,12 +36,16 @@ PyCharm can be configured to format and lint on save. Doing so requires some man
|
|||
2. Click the '+' to add a new watcher.
|
||||
3. Configure the watcher as below.
|
||||
|
||||

|
||||

|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! hint
|
||||
Adding a comma at the end of a list will trigger yapf to put each element of the list on a new line
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! note
|
||||
In order to debug vue yarn and vite servers must be started before starting the django server.
|
||||
|
||||
## Setup prettier
|
||||
|
||||
1. Navigate to File -> Settings -> Tools -> File Watchers
|
||||
|
|
@ -50,13 +54,11 @@ PyCharm can be configured to format and lint on save. Doing so requires some man
|
|||
4. Click the three dots next to 'Scope' to create a custom scope.
|
||||
5. Click '+' to add a new scope
|
||||
|
||||
- Name: prettier
|
||||
- Pattern: `file:vue/src//*||file:vue3/src//*||file:docs//*`
|
||||
- Name: prettier
|
||||
- Pattern: `file:vue/src//*||file:vue3/src//*||file:docs//*`
|
||||
|
||||
6. Configure the watcher as below.
|
||||
|
||||

|
||||

|
||||
|
||||
- Arguments: `--cwd $ProjectFileDir$\vue prettier -w --config $ProjectFileDir$\.prettierrc $FilePath$`
|
||||
|
||||
## Setup Volar??
|
||||
- Arguments: `--cwd $ProjectFileDir$\vue prettier -w --config $ProjectFileDir$\.prettierrc $FilePath$`
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=esbenp.
|
|||
|
||||
## VSCode Tasks
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! note
|
||||
In order to debug vue yarn and vite servers must be started before starting the django server.
|
||||
|
||||
There are a number of built in tasks that are available. Here are a few of the key ones:
|
||||
|
||||
- `Setup Dev Server` - Runs all the prerequisite steps so that the dev server can be run inside VSCode.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ DISABLE_ENDING_COMMA_HEURISTIC = false
|
|||
COALESCE_BRACKETS = true
|
||||
DEDENT_CLOSING_BRACKETS = true
|
||||
FORCE_MULTILINE_DICT = false
|
||||
INDENT_DICTIONARY_VALUE = true
|
||||
SPLIT_BEFORE_DOT = true
|
||||
ALLOW_SPLIT_BEFORE_DICT_VALUE = false
|
||||
|
||||
[tool.isort]
|
||||
multi_line_output = 5
|
||||
|
|
|
|||
|
|
@ -224,14 +224,14 @@ MIDDLEWARE = [
|
|||
]
|
||||
|
||||
if DEBUG_TOOLBAR:
|
||||
MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
|
||||
INSTALLED_APPS += ('debug_toolbar',)
|
||||
MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware', )
|
||||
INSTALLED_APPS += ('debug_toolbar', )
|
||||
|
||||
SORT_TREE_BY_NAME = bool(int(os.getenv('SORT_TREE_BY_NAME', False)))
|
||||
DISABLE_TREE_FIX_STARTUP = bool(int(os.getenv('DISABLE_TREE_FIX_STARTUP', False)))
|
||||
|
||||
if bool(int(os.getenv('SQL_DEBUG', False))):
|
||||
MIDDLEWARE += ('recipes.middleware.SqlPrintingMiddleware',)
|
||||
MIDDLEWARE += ('recipes.middleware.SqlPrintingMiddleware', )
|
||||
|
||||
if ENABLE_METRICS:
|
||||
MIDDLEWARE += 'django_prometheus.middleware.PrometheusAfterMiddleware',
|
||||
|
|
@ -271,13 +271,13 @@ if LDAP_AUTH:
|
|||
"disable_existing_loggers": False,
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler"
|
||||
"class": "logging.StreamHandler",
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"django_auth_ldap": {
|
||||
"level": "DEBUG",
|
||||
"handlers": ["console"]
|
||||
"handlers": ["console"],
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -301,16 +301,16 @@ if REMOTE_USER_AUTH:
|
|||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'
|
||||
},
|
||||
]
|
||||
|
||||
|
|
@ -320,18 +320,23 @@ OAUTH2_PROVIDER = {'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'boo
|
|||
READ_SCOPE = 'read'
|
||||
WRITE_SCOPE = 'write'
|
||||
|
||||
##################################################################
|
||||
####### change DEFAULT_SCHEMA_CLASS below to regenerate legacy API
|
||||
##################################################################
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES':
|
||||
('rest_framework.authentication.SessionAuthentication', 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'rest_framework.authentication.BasicAuthentication',
|
||||
),
|
||||
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated', ],
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||
'rest_framework.authentication.SessionAuthentication', 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'rest_framework.authentication.BasicAuthentication'
|
||||
),
|
||||
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'],
|
||||
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
||||
# 'DEFAULT_SCHEMA_CLASS': 'cookbook.helper.drf_spectacular_hooks.LegacySchema',
|
||||
'COERCE_DECIMAL_TO_STRING': False,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
##################################################################
|
||||
####### change DEFAULT_SCHEMA_CLASS above to regenerate legacy API
|
||||
##################################################################
|
||||
|
||||
SPECTACULAR_SETTINGS = {
|
||||
'TITLE': 'Tandoor',
|
||||
|
|
@ -349,7 +354,9 @@ SPECTACULAR_SETTINGS = {
|
|||
}
|
||||
}
|
||||
},
|
||||
"SECURITY": [{"ApiKeyAuth": []}],
|
||||
"SECURITY": [{
|
||||
"ApiKeyAuth": []
|
||||
}],
|
||||
'SWAGGER_UI_DIST': 'SIDECAR',
|
||||
'SWAGGER_UI_FAVICON_HREF': 'SIDECAR',
|
||||
'REDOC_DIST': 'SIDECAR',
|
||||
|
|
@ -369,10 +376,7 @@ SPECTACULAR_SETTINGS = {
|
|||
'schemaExpansionLevel': 'all',
|
||||
'showExtensions': True
|
||||
},
|
||||
'POSTPROCESSING_HOOKS': [
|
||||
'drf_spectacular.hooks.postprocess_schema_enums',
|
||||
'cookbook.helper.drf_spectacular_hooks.custom_postprocessing_hook'
|
||||
]
|
||||
'POSTPROCESSING_HOOKS': ['drf_spectacular.hooks.postprocess_schema_enums', 'cookbook.helper.drf_spectacular_hooks.custom_postprocessing_hook']
|
||||
}
|
||||
|
||||
ROOT_URLCONF = 'recipes.urls'
|
||||
|
|
@ -620,10 +624,7 @@ DISABLE_EXTERNAL_CONNECTORS = bool(int(os.getenv('DISABLE_EXTERNAL_CONNECTORS',
|
|||
EXTERNAL_CONNECTORS_QUEUE_SIZE = int(os.getenv('EXTERNAL_CONNECTORS_QUEUE_SIZE', 100))
|
||||
|
||||
# ACCOUNT_SIGNUP_FORM_CLASS = 'cookbook.forms.AllAuthSignupForm'
|
||||
ACCOUNT_FORMS = {
|
||||
'signup': 'cookbook.forms.AllAuthSignupForm',
|
||||
'reset_password': 'cookbook.forms.CustomPasswordResetForm'
|
||||
}
|
||||
ACCOUNT_FORMS = {'signup': 'cookbook.forms.AllAuthSignupForm', 'reset_password': 'cookbook.forms.CustomPasswordResetForm'}
|
||||
|
||||
ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False
|
||||
ACCOUNT_RATE_LIMITS = {
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ export default {
|
|||
"shared": autoPlan.shared,
|
||||
"addshopping": autoPlan.addshopping
|
||||
}
|
||||
return apiClient.createAutoPlanViewSet(data)
|
||||
return apiClient.createAutoPlan(data)
|
||||
|
||||
},
|
||||
refreshEntries() { //TODO move properly to MealPLanStore (save period for default refresh)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue