Repository: ckan/ckan

Issue:
not_empty validator does not support `False` or 0
**CKAN version**
All

**Describe the bug**
The `not_empty` validator raises an error when the value is any "[falsy](https://docs.python.org/3/library/stdtypes.html#truth-value-testing)" one , including `False` or `0`, which might be legitimately correct in a boolean or int field.

This is generally not an issue because the validators get data sent from an HTTP request so the incoming data_dict that is validated has all values defined as strings.

```python
{
    "name": "my-dataset",
    "visible_to_external_users": "False",
}
```

But if calling the actions directly from Python (eg in the tests with `call_action`) the value can be passed as a boolean and then validation fails:

```python
{
    "name": "my-dataset",
    "visible_to_external_users": False,
}

# raises ckan.logic.ValidationError: None - {'visible_to_external_users': ['Missing value']}
```

`not_empty` should allow `False` and `0` for numeric types.





Discussion:

Resolving patch:
diff --git a/ckan/lib/navl/validators.py b/ckan/lib/navl/validators.py
--- a/ckan/lib/navl/validators.py
+++ b/ckan/lib/navl/validators.py
@@ -60,14 +60,19 @@ def not_empty(key: FlattenKey, data: FlattenDataDict,
     .. code-block::
 
         data, errors = tk.navl_validate(
-            {"hello": 0},
+            {"hello": None},
             {"hello": [not_empty]}
         )
         assert errors == {"hello": [error_message]}
 
     """
     value = data.get(key)
-    if not value or value is missing:
+    valid_values = [False, 0, 0.0]
+
+    if value in valid_values:
+        return
+
+    if value is missing or not value:
         errors[key].append(_('Missing value'))
         raise StopOnError
 


Test patch:
diff --git a/ckan/tests/lib/navl/test_validators.py b/ckan/tests/lib/navl/test_validators.py
--- a/ckan/tests/lib/navl/test_validators.py
+++ b/ckan/tests/lib/navl/test_validators.py
@@ -7,6 +7,7 @@
 
 import pytest
 import ckan.lib.navl.validators as validators
+from ckan.lib.navl.dictization_functions import StopOnError
 
 
 def validator_data_dict():
@@ -343,3 +344,41 @@ def test_already_has_a_value(self):
         dict_ = {"key": "original"}
         validators.default("default_value")("key", dict_, {}, {})
         assert dict_ == {"key": "original"}
+
+
+class TestNotEmpty(object):
+
+    def test_not_empty_passes(self):
+        dict_ = {"key": "some value"}
+        errors = {"key": []}
+        validators.not_empty("key", dict_, errors, {})
+        assert errors == {"key": []}
+
+    def test_not_empty_fails(self):
+        dict_ = {"key": ""}
+        errors = {"key": []}
+        with pytest.raises(StopOnError):
+            validators.not_empty("key", dict_, errors, {})
+        assert errors == {"key": ["Missing value"]}
+
+    @pytest.mark.parametrize('value', [True, 1, "False", "0"])
+    def test_not_empty_passes_truthy(self, value):
+        dict_ = {"key": value}
+        errors = {"key": []}
+        validators.not_empty("key", dict_, errors, {})
+        assert errors == {"key": []}
+
+    @pytest.mark.parametrize('value', [False, 0, 0.0])
+    def test_not_empty_passes_false_and_zero(self, value):
+        dict_ = {"key": value}
+        errors = {"key": []}
+        validators.not_empty("key", dict_, errors, {})
+        assert errors == {"key": []}
+
+    @pytest.mark.parametrize('value', [[], {}, set(), ()])
+    def test_not_empty_fails_empty_objects(self, value):
+        dict_ = {"key": value}
+        errors = {"key": []}
+        with pytest.raises(StopOnError):
+            validators.not_empty("key", dict_, errors, {})
+        assert errors == {"key": ["Missing value"]}