Commit b1f1f3e4 authored by Florent Chehab's avatar Florent Chehab
Browse files

Further improvements to backend dynamic imports

parent 304fa96f
from django.contrib import admin from django.contrib import admin
from reversion_compare.admin import CompareVersionAdmin from reversion_compare.admin import CompareVersionAdmin
from shared import get_api_config, get_api_objs from shared import get_api_objs
api_config = get_api_config()
VERSIONNED_MODELS = map( VERSIONNED_MODELS = map(
lambda obj: obj.Model, lambda obj: obj.Model,
......
from django.contrib.auth.models import User
from django.db import models from django.db import models
from rest_framework import serializers
from backend_app.models.university import University
from backend_app.fields import JSONField from backend_app.fields import JSONField
from backend_app.models.abstract.my_model import ( from backend_app.models.abstract.my_model import (
MyModel, MyModel,
MyModelSerializer, MyModelSerializer,
MyModelViewSet, MyModelViewSet,
) )
from django.contrib.auth.models import User from backend_app.models.university import University
from backend_app.utils import get_viewset_permissions, get_model_config, get_user_level
from backend_app.permissions.__list_user_post_permission import ( from backend_app.permissions.__list_user_post_permission import (
list_user_post_permission, list_user_post_permission,
) )
from backend_app.utils import get_model_config, get_user_level, get_viewset_permissions
from rest_framework import serializers
class UserData(MyModel): class UserData(MyModel):
......
from django.conf import settings from typing import List
from dotmap import DotMap
from shared import get_api_config from django.contrib.auth.models import User
import importlib
api_config = get_api_config() from shared import get_api_objs
ALL_VIEWSETS = {} ALL_VIEWSETS = {}
for model in api_config: for api_obj in get_api_objs(
model = DotMap(model) has_model=None,
if "is_api_view" in model and model.is_api_view: requires_testing=False,
continue is_api_view=False,
if not model.requires_testing: ignore_models=["UserData"],
if model.viewset != "UserDataViewSet": ):
module = importlib.import_module( ALL_VIEWSETS[api_obj.viewset] = api_obj.Viewset
"backend_app.models.{}".format(model.import_location)
) for api_obj in get_api_objs(
ALL_VIEWSETS[model.viewset] = getattr(module, model.viewset) has_model=None, requires_testing=True, is_api_view=False, ignore_models=["UserData"]
):
if settings.TESTING: ALL_VIEWSETS[api_obj.viewset] = api_obj.Viewset
for model in api_config:
model = DotMap(model)
if "is_api_view" in model and model.is_api_view:
continue
if model.requires_testing:
if model.viewset != "UserDataViewSet":
module = importlib.import_module(
"backend_app.models.{}".format(model.import_location)
)
ALL_VIEWSETS[model.viewset] = getattr(module, model.viewset)
class Request(object): class Request(object):
...@@ -38,7 +26,11 @@ class Request(object): ...@@ -38,7 +26,11 @@ class Request(object):
self.method = method self.method = method
def list_user_post_permission(user): def list_user_post_permission(user: User) -> List[str]:
"""
Function the list the viewset to which a user can submit a post request to.
"""
viewsets_user_can_post = [] viewsets_user_can_post = []
request = Request(user, "POST") request = Request(user, "POST")
for viewset_name in ALL_VIEWSETS: for viewset_name in ALL_VIEWSETS:
......
from django.conf import settings
from django.conf.urls import include, url from django.conf.urls import include, url
from rest_framework import routers from rest_framework import routers
from rest_framework.documentation import include_docs_urls from rest_framework.documentation import include_docs_urls
from backend_app.permissions import DEFAULT_VIEWSET_PERMISSIONS from backend_app.permissions import DEFAULT_VIEWSET_PERMISSIONS
from shared import get_api_config from shared import get_api_objs
from dotmap import DotMap
from .other_viewsets import AppModerationStatusViewSet
import importlib
urlpatterns = [url(r"^api-docs/", include_docs_urls(title="Outgoing API"))] urlpatterns = [url(r"^api-docs/", include_docs_urls(title="Outgoing API"))]
router = routers.DefaultRouter() router = routers.DefaultRouter()
ALL_MODELS = [] ALL_MODELS = map(
ALL_VIEWSETS = [] lambda obj: obj.Model,
get_api_objs(has_model=True, ignore_in_admin=False, requires_testing="smart"),
)
# Automatically load models and viewset based on API config file ALL_VIEWSETS = []
api_config = get_api_config()
for entry in api_config:
model_obj = DotMap(entry)
if "is_api_view" in model_obj and model_obj.is_api_view:
continue
if (not model_obj.requires_testing) or (
settings.TESTING and model_obj.requires_testing
):
module = importlib.import_module(
"backend_app.models.{}".format(model_obj.import_location)
)
Viewset = getattr(module, model_obj.viewset)
ALL_VIEWSETS.append(Viewset)
if model_obj.model is not None and not model_obj.ignore_in_admin: for api_obj in get_api_objs(
ALL_MODELS.append(getattr(module, model_obj.model)) has_model=None, requires_testing="smart", is_api_view=False
):
ALL_VIEWSETS.append(api_obj.Viewset)
# Creating the correct router entry # Creating the correct router entry
str_url = model_obj.api_end_point str_url = api_obj.api_end_point
if "api_attr" in model_obj: if "api_attr" in api_obj:
str_url += "/{}".format(model_obj.api_attr) str_url += "/{}".format(api_obj.api_attr)
if "api_name" in model_obj: if "api_name" in api_obj:
router.register(str_url, Viewset, model_obj.api_name) router.register(str_url, api_obj.Viewset, api_obj.api_name)
else: else:
router.register(str_url, Viewset) router.register(str_url, api_obj.Viewset)
# Add all the endpoints for the base api # Add all the endpoints for the base api
urlpatterns += [ urlpatterns.append(url(r"^api/", include(router.urls)))
url(r"^api/", include(router.urls)),
url(r"^api/serverModerationStatus/", AppModerationStatusViewSet.as_view()), for api_obj in get_api_objs(has_model=None, requires_testing="smart", is_api_view=True):
] urlpatterns.append(
url(r"^api/{}/".format(api_obj.api_end_point), api_obj.Viewset.as_view())
)
####### #######
# Models and Viewset checks # Models and Viewset checks
......
from shared import get_api_config from shared import load_api_config
def find_api_end_point_for_viewset(viewset_name): def find_api_end_point_for_viewset(viewset_name):
api_config = get_api_config() api_config = load_api_config()
for obj in api_config: for obj in api_config:
if obj["viewset"] == viewset_name: if obj["viewset"] == viewset_name:
return obj["api_end_point"] return obj["api_end_point"]
......
from shared import get_api_config from shared import load_api_config
def get_model_config(model): def get_model_config(model):
api_config = get_api_config() api_config = load_api_config()
for obj in api_config: for obj in api_config:
if "is_api_view" in obj and obj["is_api_view"]: if "is_api_view" in obj and obj["is_api_view"]:
......
...@@ -8,11 +8,11 @@ from backend_app.permissions import ( ...@@ -8,11 +8,11 @@ from backend_app.permissions import (
) )
from rest_framework.permissions import IsAdminUser from rest_framework.permissions import IsAdminUser
from backend_app.permissions import DEFAULT_VIEWSET_PERMISSIONS from backend_app.permissions import DEFAULT_VIEWSET_PERMISSIONS
from shared import get_api_config from shared import load_api_config
def get_viewset_permissions(viewset): def get_viewset_permissions(viewset):
api_config = get_api_config() api_config = load_api_config()
for obj in api_config: for obj in api_config:
if obj["viewset"] == viewset: if obj["viewset"] == viewset:
custom_permission = obj["viewset_permission"] custom_permission = obj["viewset_permission"]
......
from .get_api_config import get_api_config, get_api_objs from .get_api_config import load_api_config, get_api_objs
from .obj_moderation_permission import ( from .obj_moderation_permission import (
DEFAULT_OBJ_MODERATION_LV, DEFAULT_OBJ_MODERATION_LV,
OBJ_MODERATION_PERMISSIONS, OBJ_MODERATION_PERMISSIONS,
) )
__all__ = [ __all__ = [
"get_api_config", "load_api_config",
"DEFAULT_OBJ_MODERATION_LV", "DEFAULT_OBJ_MODERATION_LV",
"OBJ_MODERATION_PERMISSIONS", "OBJ_MODERATION_PERMISSIONS",
"get_api_objs", "get_api_objs",
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
- viewset: AppModerationStatusViewSet - viewset: AppModerationStatusViewSet
api_end_point: serverModerationStatus api_end_point: serverModerationStatus
import_location: other_viewsets
read_only: true read_only: true
is_api_view: true is_api_view: true
......
import importlib
from os.path import dirname, join, realpath
from typing import List, Optional, Union, Dict
from django.conf import settings
import yaml import yaml
from os.path import join, realpath, dirname
from .obj_moderation_permission import OBJ_MODERATION_PERMISSIONS
from dotmap import DotMap from dotmap import DotMap
from django.conf import settings
import importlib from .obj_moderation_permission import OBJ_MODERATION_PERMISSIONS
def get_api_config(): def load_api_config() -> List[Dict]:
"""
Returns the list of api objects without filtering add default attributes
added to all objects if they are missing.
"""
current_dir = dirname(realpath(__file__)) current_dir = dirname(realpath(__file__))
with open(join(current_dir, "api_config.yml"), "r") as f: with open(join(current_dir, "api_config.yml"), "r") as f:
api_config = yaml.load(f) api_config = yaml.load(f)
...@@ -35,16 +44,26 @@ def get_api_config(): ...@@ -35,16 +44,26 @@ def get_api_config():
def get_api_objs( def get_api_objs(
has_model, has_model: Optional[bool],
ignore_in_admin=None, ignore_in_admin: Optional[bool] = None,
versionned=None, versionned: Optional[bool] = None,
requires_testing=None, requires_testing: Union[None, bool, "smart"] = None,
is_api_view=False, is_api_view: Optional[bool] = False,
) -> dict: ignore_models: List[str] = list(),
) -> List[DotMap]:
"""
Returns a list of DotMap objects corresponding the api config file
with filtering applied to some attributes.
If the parameter is `None` then no filtering is applied to this attribute.
If the parameter is `True` or `False` the object is returned only if it matched.
There is one exception for the parameter `requires_testing` if it is set to `smart` then
the object is returned only if doesn't require testing or if testing is activated.
"""
out = list() out = list()
for entry in get_api_config(): for entry in load_api_config():
obj = DotMap(entry) obj = DotMap(entry)
if has_model is not None: if has_model is not None:
...@@ -66,12 +85,16 @@ def get_api_objs( ...@@ -66,12 +85,16 @@ def get_api_objs(
continue continue
if requires_testing is not None: if requires_testing is not None:
if requires_testing and not settings.TESTING: if requires_testing == "smart":
continue if obj.requires_testing and not settings.TESTING:
if requires_testing and not obj.requires_testing: continue
continue else:
if not requires_testing and obj.requires_testing: if requires_testing and not settings.TESTING:
continue continue
if requires_testing and not obj.requires_testing:
continue
if not requires_testing and obj.requires_testing:
continue
if is_api_view is not None: if is_api_view is not None:
if is_api_view and not obj.is_api_view: if is_api_view and not obj.is_api_view:
...@@ -84,6 +107,8 @@ def get_api_objs( ...@@ -84,6 +107,8 @@ def get_api_objs(
) )
if obj.model is not None: if obj.model is not None:
if obj.model in ignore_models:
continue
Model = getattr(module, obj.model) Model = getattr(module, obj.model)
obj.Model = Model obj.Model = Model
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment