Skip to content

Commit

Permalink
add js validation
Browse files Browse the repository at this point in the history
  • Loading branch information
saxix committed Jul 2, 2024
1 parent 7a092b8 commit 28ef5f8
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# HOPR FlexFields

[![Test](https://github.com/unicef/hope-flex-fields/actions/workflows/test.yml/badge.svg)](https://github.com/unicef/hope-flex-fields/actions/workflows/test.yml)

[![codecov](https://codecov.io/gh/unicef/hope-flex-fields/graph/badge.svg?token=GSYAH4IEUK)](https://codecov.io/gh/unicef/hope-flex-fields)

## Install
CSP_SCRIPT_SRC = [
Expand Down
41 changes: 39 additions & 2 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ dependencies = [
"django-admin-extra-buttons>=1.5.8",
"django-jsoneditor>=0.2.4",
"djangorestframework>=3.15.1",
"mini-racer>=0.12.4",
"jsonpickle>=3.2.2",
]
requires-python = ">=3.11"
readme = "README.md"
Expand Down
6 changes: 5 additions & 1 deletion src/hope_flex_fields/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django import forms
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.text import slugify

from django_regex.fields import RegexField
from django_regex.validators import RegexValidator
Expand Down Expand Up @@ -53,11 +54,14 @@ def clean(self):
try:
self.set_default_arguments()
self.get_field()
self.name = slugify(str(self.name))
except TypeError as e:
raise ValidationError(e)

def set_default_arguments(self):
stored = self.attrs or {}
if not isinstance(self.attrs, dict):
self.attrs = {}
stored = self.attrs
sig: inspect.Signature = inspect.signature(self.field_type)
defaults = {
k.name: k.default
Expand Down
41 changes: 41 additions & 0 deletions src/hope_flex_fields/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import json

from django.core.exceptions import ValidationError
from django.core.validators import BaseValidator
from django.utils.translation import gettext as _

from py_mini_racer import JSArray, JSObject, MiniRacer


class JsValidator(BaseValidator):

@property
def code(self):
return self.limit_value

def __call__(self, value):
ctx = MiniRacer()

pickled = json.dumps(value or "")
base = f"var value = {pickled};"
ctx.eval(base)
ret = ctx.eval(self.code)

# try:
# ret = jsonpickle.decode(result)
# except (JSONDecodeError, TypeError):
# ret = result

if isinstance(ret, JSArray):
raise ValidationError(list(ret))

if isinstance(ret, JSObject):
errors = {s: k for s, k in ret.items()}
raise ValidationError(errors)

if isinstance(ret, str):
raise ValidationError(_(ret))
elif isinstance(ret, bool) and not ret:
raise ValidationError(_("Please insert a valid value"))

return True
66 changes: 66 additions & 0 deletions tests/test_jsvalidator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from django.core.exceptions import ValidationError

import pytest

from hope_flex_fields.validators import JsValidator


def test_jsvalidator():
code = "result=value*2"
v = JsValidator(code)
assert v(22)


#
# def test_jsvalidator_error():
# code = 'value=2'
# v = JsValidator(code)
# with pytest.raises(ValueError) as e:
# v(22)
# assert e.value.messages == [' Validator code must returns something']


def test_jsvalidator_fail():
code = "result=value==2"
v = JsValidator(code)
with pytest.raises(ValidationError) as e:
v(22)
assert e.value.messages == ["Please insert a valid value"]


def test_jsvalidator_return_dict():
code = 'result={"value": "error"}'
v = JsValidator(code)
with pytest.raises(ValidationError) as e:
v(22)
assert e.value.message_dict == {"value": ["error"]}


def test_jsvalidator_return_tuple():
code = 'result=["1","2","3"]'
v = JsValidator(code)
with pytest.raises(ValidationError) as e:
v(22)
assert e.value.messages == ["1", "2", "3"]


def test_jsvalidator_fail_custom_message():
code = """
if (value <2){
result = "Provided number must be less than or equal to 2!";
}else if (value > 5){
result = "Provided number must be greater than or equal to 5!";
}
"""
v = JsValidator(code)
with pytest.raises(ValidationError) as e:
v(22)
assert e.value.messages == ["Provided number must be greater than or equal to 5!"]


def test_jsvalidator_return_error():
code = "result={}"
v = JsValidator(code)
with pytest.raises(ValidationError) as e:
v(22)
assert e.value.message_dict == {}
6 changes: 4 additions & 2 deletions tests/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ def config(db):
from hope_flex_fields.models import FieldDefinition, Fieldset, FieldsetField

fd1 = FieldDefinition.objects.create(
name="IntField", field_type=forms.IntegerField, attrs={"min_value": 1}
name="IntField",
field_type=forms.IntegerField,
attrs={"min_value": 1},
validation="",
)
fd2 = FieldDefinition.objects.create(
name="FloatField", field_type=forms.FloatField, attrs={"min_value": 1}
Expand All @@ -24,7 +27,6 @@ def config(db):


def test_validate_row(config):
# try to validate json formatted data against a FieldSet
data = {"int": 1, "float": 1.1, "str": "string"}
fs: Fieldset = config["fs"]

Expand Down

0 comments on commit 28ef5f8

Please sign in to comment.