Repository: mathesar-foundation/mathesar

Issue:
Implement filtering options for Boolean types
## Problem
We need to ensure that records that include columns of Boolean type support the following filters via API: 
- is {true/false}
- is not {true/false}
- is empty
- is not empty

This may already be done, please verify that it works and add some test cases specifically for boolean data types.

This involves:
- Implementing the filters in the backend
- Updating the `/api/v0/databases/<id>/types/` endpoint to store available filters on this type
  - Filter information should include the number of parameters needing to be passed in (e.g. `is` needs 1 parameter, `is empty` needs 0)

## Additional context
- We're using our fork of `sqlalchemy-filters` to provide filtering. See: https://github.com/centerofci/sqlalchemy-filters.
- See also: #387
- #557 provides some context on storing filters.


Discussion:
This seems to be functional already. Adding tests and the available filters on the type endpoint.
@powellc You might've seen this already, but there are already some tests for filtering at `db/tests/records/test_filtering.py`. It lists all the operators that are implemented by `sqlalchemy-filters` which might be useful. 

And if you need to update our fork of `sqlalchemy-filters`, I've been working off [this branch](https://github.com/centerofci/sqlalchemy-filters/tree/models_to_tables). If it needs to change, it might be worth properly organizing that repo and getting things merged into master and updating the readme - let me know and I can help out.
That's great @eito-fis, thanks! I hadn't see the filter tests yet. Do you think it makes sense to still test the filtering via the API, or would these test suffice to cover that logic?  I think I'm leaning towards also adding tests to the `mathesar/tests/views//api/test_records_api.py` file, but I could be conviced not to 😄 
I was previously testing all the filtering on the backend and then just ensuring the API called the function with the right arguments. At the time it wasn't very principled, but I liked that the db tests ran faster than the API tests (django db gets reset between api tests) so I went with that. 
@powellc @eito-fis I think it makes sense to add tests to the API for each data type to ensure that we support the specific filters for that data type. 
Sounds good! I'll push up some API tests for filtering around booleans in a bit. Thanks!

Resolving patch:
diff --git a/mathesar/filters.py b/mathesar/filters.py
--- a/mathesar/filters.py
+++ b/mathesar/filters.py
@@ -1,8 +1,25 @@
 from django_filters import BooleanFilter, DateTimeFromToRangeFilter
 from django_property_filter import PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter
 
+from mathesar.database.types import MathesarTypeIdentifier
 from mathesar.models import Schema, Table, Database
 
+FILTER_OPTIONS_BY_TYPE_IDENTIFIER = {
+    MathesarTypeIdentifier.BOOLEAN.value:
+    {
+        "db_type": "BOOLEAN",
+        "options": [{
+            "op": "eq",
+            "value": {
+                "allowed_types": ["BOOLEAN"],
+            }
+        }, {
+            "op": "is_null",
+            "value": "null",
+        }]
+    }
+}
+
 
 class CharInFilter(PropertyBaseInFilter, PropertyCharFilter):
     pass
diff --git a/mathesar/serializers.py b/mathesar/serializers.py
--- a/mathesar/serializers.py
+++ b/mathesar/serializers.py
@@ -6,6 +6,7 @@
 from rest_framework.fields import empty
 from rest_framework.settings import api_settings
 
+from mathesar.filters import FILTER_OPTIONS_BY_TYPE_IDENTIFIER
 from mathesar.models import Table, Schema, DataFile, Database, Constraint
 
 
@@ -225,6 +226,10 @@ class TypeSerializer(serializers.Serializer):
     identifier = serializers.CharField()
     name = serializers.CharField()
     db_types = serializers.ListField(child=serializers.CharField())
+    filters = serializers.SerializerMethodField()
+
+    def get_filters(self, obj):
+        return FILTER_OPTIONS_BY_TYPE_IDENTIFIER.get(obj.get('identifier'))
 
 
 class DatabaseSerializer(serializers.ModelSerializer):


Test patch:
diff --git a/mathesar/tests/views/api/test_database_api.py b/mathesar/tests/views/api/test_database_api.py
--- a/mathesar/tests/views/api/test_database_api.py
+++ b/mathesar/tests/views/api/test_database_api.py
@@ -2,6 +2,7 @@
 from django.conf import settings
 from django.core.cache import cache
 
+from mathesar.filters import FILTER_OPTIONS_BY_TYPE_IDENTIFIER
 from mathesar.reflection import reflect_db_objects
 from mathesar.models import Table, Schema, Database
 from db.tests.types import fixtures
@@ -161,7 +162,10 @@ def test_type_list(client, test_db_name):
     assert response.status_code == 200
     assert len(response_data) == len(database.supported_types)
     for supported_type in response_data:
-        assert all([key in supported_type for key in ['identifier', 'name', 'db_types']])
+        assert all([key in supported_type for key in ['identifier', 'name', 'db_types', 'filters']])
+        found_filters = supported_type.get('filters')
+        expected_filters = FILTER_OPTIONS_BY_TYPE_IDENTIFIER.get(supported_type.get('identifier'))
+        assert found_filters == expected_filters
 
 
 def test_database_types_installed(client, test_db_name, engine_email_type):