Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/gql/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# core/gql/__init__.py

from .max_length_constraints import MaxLengthConstraintsGQLType, build_max_length_constraints

__all__ = ['MaxLengthConstraintsGQLType', 'build_max_length_constraints']
93 changes: 93 additions & 0 deletions core/gql/max_length_constraints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# core/gql/max_length_constraints.py

from graphene.types.generic import GenericScalar
import graphene
from django.apps import apps

try:
from insuree.models import Insuree
except ImportError:
Insuree = None


def get_model(model_name):
"""Récupère un modèle à partir de son nom."""
try:
return apps.get_model('core', model_name)
except LookupError:
return None


MAX_LENGTH_CONSTRAINTS_FIELDS = {
"admin": {
"user": {
"username": ("User", "username"),
"lastName": ("InteractiveUser", "last_name"),
"otherNames": ("InteractiveUser", "other_names"),
"phone": ("InteractiveUser", "phone"),
"email": ("InteractiveUser", "email"),
},
},
}

if Insuree:
MAX_LENGTH_CONSTRAINTS_FIELDS["insuree"] = {
"insuree": {
"uuid": (Insuree, "uuid"),
"chfId": (Insuree, "chf_id"),
"lastName": (Insuree, "last_name"),
"otherNames": (Insuree, "other_names"),
"marital": (Insuree, "marital"),
"passport": (Insuree, "passport"),
"phone": (Insuree, "phone"),
"email": (Insuree, "email"),
"currentAddress": (Insuree, "current_address"),
"geolocation": (Insuree, "geolocation"),
"status": (Insuree, "status"),
},
}


def build_max_length_constraints():
constraints = {}

for module_name, forms in MAX_LENGTH_CONSTRAINTS_FIELDS.items():
module_constraints = {}

for form_name, fields in forms.items():
form_constraints = {}

for field_name, (model_or_name, model_field_name) in fields.items():
if isinstance(model_or_name, str):
model = get_model(model_or_name)
if model is None:
continue
else:
model = model_or_name

try:
field = model._meta.get_field(model_field_name)
if field.max_length:
form_constraints[field_name] = field.max_length
except Exception:
continue

if form_constraints:
module_constraints[form_name] = form_constraints

if module_constraints:
constraints[module_name] = module_constraints

return constraints


class MaxLengthConstraintsGQLType(graphene.ObjectType):
"""
Returns max_length constraints used by the frontend to enforce field length
limits in supported forms.
"""
constraints = GenericScalar()

@staticmethod
def resolve_constraints(root, info):
return build_max_length_constraints()
3 changes: 2 additions & 1 deletion core/gql_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from core.apps import CoreConfig
from django.utils.translation import gettext as _
from django.core.exceptions import PermissionDenied

from graphene.types.generic import GenericScalar
from .gql import MaxLengthConstraintsGQLType, build_max_length_constraints
from .utils import prefix_filterset

class OfficerGQLType(DjangoObjectType):
Expand Down
6 changes: 6 additions & 0 deletions core/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
PermissionOpenImisGQLType,
ModulePermissionGQLType,
CustomFilterOptionGQLType,
MaxLengthConstraintsGQLType,
)
from core.utils import ( # noqa: 401
ExtendedConnection,
Expand Down Expand Up @@ -758,6 +759,8 @@ class Query(graphene.ObjectType):
ModuleConfigurationGQLType, validity=graphene.String(), layer=graphene.String()
)

max_length_constraints = graphene.Field(MaxLengthConstraintsGQLType)

user_obligatory_fields = GenericScalar()
eo_obligatory_fields = GenericScalar()

Expand Down Expand Up @@ -968,6 +971,9 @@ def resolve_validate_username(self, info, **kwargs):
else:
return True

def resolve_max_length_constraints(self, info):
return MaxLengthConstraintsGQLType()

def resolve_validate_user_email(self, info, **kwargs):
if not info.context.user.has_perms(CoreConfig.gql_query_users_perms):
raise PermissionDenied(_("unauthorized"))
Expand Down
58 changes: 58 additions & 0 deletions core/tests/test_gql_queries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from django.test import TestCase

from core.gql.max_length_constraints import build_max_length_constraints

try:
from insuree.models import Insuree
except ImportError:
Insuree = None


class MaxLengthConstraintsTestCase(TestCase):
def test_build_max_length_constraints_returns_supported_admin_user_fields(self):
constraints = build_max_length_constraints()

self.assertIn("admin", constraints)
self.assertIn("user", constraints["admin"])
self.assertEqual(
constraints["admin"]["user"],
{
"username": 50,
"lastName": 100,
"otherNames": 100,
"phone": 50,
"email": 200,
},
)

def test_build_max_length_constraints_excludes_uncontrolled_models(self):
constraints = build_max_length_constraints()

self.assertNotIn("logentry", constraints)
self.assertNotIn("session", constraints)
self.assertNotIn("historicalinteractiveuser", constraints)

def test_build_max_length_constraints_returns_insuree_fields_when_available(self):
if not Insuree:
self.skipTest("Insuree module is not installed")

constraints = build_max_length_constraints()

self.assertIn("insuree", constraints)
self.assertIn("insuree", constraints["insuree"])
self.assertEqual(
constraints["insuree"]["insuree"],
{
"uuid": 36,
"chfId": 50,
"lastName": 100,
"otherNames": 100,
"marital": 1,
"passport": 25,
"phone": 50,
"email": 100,
"currentAddress": 200,
"geolocation": 250,
"status": 2,
},
)
Loading