Skip to content

Commit

Permalink
[feature] Implemented config app API filters #747
Browse files Browse the repository at this point in the history
Closes #747

Co-authored-by: Federico Capoano <f.capoano@openwisp.io>
  • Loading branch information
Aryamanz29 and nemesifier committed Apr 27, 2023
1 parent 8a8218b commit b889e63
Show file tree
Hide file tree
Showing 8 changed files with 706 additions and 32 deletions.
207 changes: 198 additions & 9 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,70 @@ List devices
GET /api/v1/controller/device/
**Available filters**

You can filter a list of devices based on their configuration
status using the ``status`` (e.g modified, applied, or error).

.. code-block:: text
GET /api/v1/controller/device/?config__status={status}
You can filter a list of devices based on their configuration backend
using the ``backend`` (e.g netjsonconfig.OpenWrt or netjsonconfig.OpenWisp).

.. code-block:: text
GET /api/v1/controller/device/?config__backend={backend}
You can filter a list of devices based on their
organization using the ``organization_id`` or ``organization_slug``.

.. code-block:: text
GET /api/v1/controller/device/?organization={organization_id}
.. code-block:: text
GET /api/v1/controller/device/?organization_slug={organization_slug}
You can filter a list of devices based on their
configuration templates using the ``template_id``.

.. code-block:: text
GET /api/v1/controller/device/?config__templates={template_id}
You can filter a list of devices based on
their device group using the ``group_id``.

.. code-block:: text
GET /api/v1/controller/device/?group={group_id}
You can filter a list of devices that have a device
location object using the ``with_geo`` (eg. true or false).

.. code-block:: text
GET /api/v1/controller/device/?with_geo={with_geo}
You can filter a list of devices based on
their creation time using the ``creation_time``.

.. code-block:: text
# Created exact
GET /api/v1/controller/device/?created={creation_time}
# Created greater than or equal to
GET /api/v1/controller/device/?created__gte={creation_time}
# Created is less than
GET /api/v1/controller/device/?created__lt={creation_time}
Create device
#############

Expand Down Expand Up @@ -1637,6 +1701,27 @@ List device groups
GET /api/v1/controller/group/
**Available filters**

You can filter a list of device groups based on their
organization using the ``organization_id`` or ``organization_slug``.

.. code-block:: text
GET /api/v1/controller/group/?organization={organization_id}
.. code-block:: text
GET /api/v1/controller/group/?organization_slug={organization_slug}
You can filter a list of device groups that have a
device object using the ``empty`` (eg. true or false).

.. code-block:: text
GET /api/v1/controller/group/?empty={empty}
Create device group
###################

Expand Down Expand Up @@ -1913,12 +1998,18 @@ List locations
GET /api/v1/controller/location/
You can filter using ``organization_slug`` to get list locations that
belongs to an organization.
**Available filters**

You can filter using ``organization_id`` or ``organization_slug``
to get list locations that belongs to an organization.

.. code-block:: text
GET /api/v1/controller/location/?organization={organization_id}
.. code-block:: text
GET /api/v1/controller/location/?organization_slug=<organization_slug>
GET /api/v1/controller/location/?organization_slug={organization_slug}
Create location
###############
Expand Down Expand Up @@ -2023,12 +2114,18 @@ List locations with devices deployed (in GeoJSON format)
GET /api/v1/controller/location/geojson/
You can filter using ``organization_slug`` to get list location of
devices from that organization.
**Available filters**

You can filter using ``organization_id`` or ``organization_slug``
to get list location of devices from that organization.

.. code-block:: text
GET /api/v1/controller/location/geojson/?organization_id={organization_id}
.. code-block:: text
GET /api/v1/controller/location/geojson/?organization_slug=<organization_slug>
GET /api/v1/controller/location/geojson/?organization_slug={organization_slug}
List floorplans
###############
Expand All @@ -2037,12 +2134,18 @@ List floorplans
GET /api/v1/controller/floorplan/
You can filter using ``organization_slug`` to get list floorplans that
belongs to an organization.
**Available filters**

You can filter using ``organization_id`` or ``organization_slug``
to get list floorplans that belongs to an organization.

.. code-block:: text
GET /api/v1/controller/floorplan/?organization_slug=<organization_slug>
GET /api/v1/controller/floorplan/?organization={organization_id}
.. code-block:: text
GET /api/v1/controller/floorplan/?organization_slug={organization_slug}
Create floorplan
################
Expand Down Expand Up @@ -2079,6 +2182,64 @@ List templates
GET /api/v1/controller/template/
**Available filters**

You can filter a list of templates based on their organization
using the ``organization_id`` or ``organization_slug``.

.. code-block:: text
GET /api/v1/controller/template/?organization={organization_id}
.. code-block:: text
GET /api/v1/controller/template/?organization_slug={organization_slug}
You can filter a list of templates based on their backend using
the ``backend`` (e.g netjsonconfig.OpenWrt or netjsonconfig.OpenWisp).

.. code-block:: text
GET /api/v1/controller/template/?backend={backend}
You can filter a list of templates based on their
type using the ``type`` (eg. vpn or generic).

.. code-block:: text
GET /api/v1/controller/template/?type={type}
You can filter a list of templates that are enabled
by default or not using the ``default`` (eg. true or false).

.. code-block:: text
GET /api/v1/controller/template/?default={default}
You can filter a list of templates that are required
or not using the ``required`` (eg. true or false).

.. code-block:: text
GET /api/v1/controller/template/?required={required}
You can filter a list of templates based on
their creation time using the ``creation_time``.

.. code-block:: text
# Created exact
GET /api/v1/controller/template/?created={creation_time}
# Created greater than or equal to
GET /api/v1/controller/template/?created__gte={creation_time}
# Created is less than
GET /api/v1/controller/template/?created__lt={creation_time}
Create template
###############

Expand Down Expand Up @@ -2131,6 +2292,34 @@ List VPNs
GET /api/v1/controller/vpn/
**Available filters**

You can filter a list of vpns based
on their backend using the ``backend``
(e.g openwisp_controller.vpn_backends.OpenVpn
or openwisp_controller.vpn_backends.Wireguard).

.. code-block:: text
GET /api/v1/controller/vpn/?backend={backend}
You can filter a list of vpns based on their subnet using the ``subnet_id``.

.. code-block:: text
GET /api/v1/controller/vpn/?subnet={subnet_id}
You can filter a list of vpns based on their organization
using the ``organization_id`` or ``organization_slug``.

.. code-block:: text
GET /api/v1/controller/vpn/?organization={organization_id}
.. code-block:: text
GET /api/v1/controller/vpn/?organization_slug={organization_slug}
Create VPN
##########

Expand Down
134 changes: 134 additions & 0 deletions openwisp_controller/config/api/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from uuid import UUID

from django.utils.translation import gettext_lazy as _
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.exceptions import ValidationError
from swapper import load_model

from openwisp_users.api.filters import OrganizationManagedFilter

Template = load_model('config', 'Template')
Vpn = load_model('config', 'Vpn')
Device = load_model('config', 'Device')
DeviceGroup = load_model('config', 'DeviceGroup')


class BaseConfigAPIFilter(OrganizationManagedFilter):
def _set_valid_filterform_lables(self):
# When not filtering on a model field, an error message
# with the label "[invalid_name]" will be displayed in filter form.
# To avoid this error, we need to provide the label explicitly.
raise NotImplementedError


class TemplateListFilter(BaseConfigAPIFilter):
created__gte = filters.DateTimeFilter(
field_name='created',
lookup_expr='gte',
)
created__lt = filters.DateTimeFilter(
field_name='created',
lookup_expr='lt',
)

def _set_valid_filterform_lables(self):
self.filters['backend'].label = _('Template backend')
self.filters['type'].label = _('Template type')

def __init__(self, *args, **kwargs):
super(TemplateListFilter, self).__init__(*args, **kwargs)
self._set_valid_filterform_lables()

class Meta(BaseConfigAPIFilter.Meta):
model = Template
fields = BaseConfigAPIFilter.Meta.fields + [
'backend',
'type',
'default',
'required',
'created',
]


class VPNListFilter(BaseConfigAPIFilter):
def _set_valid_filterform_lables(self):
self.filters['backend'].label = _('VPN Backend')
self.filters['subnet'].label = _('VPN Subnet')

def __init__(self, *args, **kwargs):
super(VPNListFilter, self).__init__(*args, **kwargs)
self._set_valid_filterform_lables()

class Meta(BaseConfigAPIFilter.Meta):
model = Vpn
fields = BaseConfigAPIFilter.Meta.fields + ['backend', 'subnet']


class DeviceListFilterBackend(DjangoFilterBackend):
def filter_queryset(self, request, queryset, view):
"""
Validate that the request parameters contain
a valid configuration template uuid format
"""
config_template_uuid = request.query_params.get('config__templates')
if config_template_uuid:
try:
# Attempt to convert the uuid string to a UUID object
config_template_uuid_obj = UUID(config_template_uuid)
except ValueError:
raise ValidationError({'config__templates': _('Invalid UUID format')})
# Add the config__templates filter to the queryset
return queryset.filter(config__templates=config_template_uuid_obj)
return super().filter_queryset(request, queryset, view)


class DeviceListFilter(BaseConfigAPIFilter):
created__gte = filters.DateTimeFilter(
field_name='created',
lookup_expr='gte',
)
created__lt = filters.DateTimeFilter(
field_name='created',
lookup_expr='lt',
)

def _set_valid_filterform_lables(self):
self.filters['group'].label = _('Device group')
self.filters['config__templates'].label = _('Config template')
self.filters['config__status'].label = _('Config status')
self.filters['config__backend'].label = _('Config backend')

def __init__(self, *args, **kwargs):
super(DeviceListFilter, self).__init__(*args, **kwargs)
self._set_valid_filterform_lables()

class Meta(BaseConfigAPIFilter.Meta):
model = Device
fields = BaseConfigAPIFilter.Meta.fields + [
'config__status',
'config__backend',
'config__templates',
'group',
'created',
]


class DeviceGroupListFilter(BaseConfigAPIFilter):
# Using filter query param name `empty`
# which is similar to admin filter
empty = filters.BooleanFilter(field_name='device', method='filter_device')

def filter_device(self, queryset, name, value):
# Returns list of device groups that have devicelocation objects
return queryset.exclude(device__isnull=value).distinct()

def _set_valid_filterform_lables(self):
self.filters['empty'].label = _('Has devices?')

def __init__(self, *args, **kwargs):
super(DeviceGroupListFilter, self).__init__(*args, **kwargs)
self._set_valid_filterform_lables()

class Meta(BaseConfigAPIFilter.Meta):
model = DeviceGroup
Loading

0 comments on commit b889e63

Please sign in to comment.