1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-17 18:32:35 +00:00

Remove accidentally included filters_disabled field from api endpoint and update api docs so they include field_options.

This commit is contained in:
Nigel Gott 2021-03-16 11:48:20 +00:00
parent 222021eafd
commit 781cde650d
12 changed files with 136 additions and 53 deletions
backend
src/baserow
tests/baserow
changelog.md
web-frontend/modules/database/services

View file

@ -148,7 +148,7 @@ def allowed_includes(*allowed):
"""
A view method decorator that checks which allowed includes are in the GET
parameters of the request. The allowed arguments are going to be added to the
view method kwargs and if they are in the includes GET parameter the value will
view method kwargs and if they are in the `include` GET parameter the value will
be True.
Imagine this request:
@ -174,7 +174,7 @@ def allowed_includes(*allowed):
def validate_decorator(func):
def func_wrapper(*args, **kwargs):
request = get_request(args)
raw_include = request.GET.get('includes', None)
raw_include = request.GET.get('include', None)
includes = raw_include.split(',') if raw_include else []
for include in allowed:

View file

@ -1,7 +1,11 @@
from rest_framework import serializers
from baserow.contrib.database.api.views.grid.serializers import \
GridViewFieldOptionsField
def get_example_pagination_serializer_class(results_serializer_class):
def get_example_pagination_serializer_class(results_serializer_class,
add_field_options=False):
"""
Generates a pagination like response serializer that has the provided serializer
class as results. It is only used for example purposes in combination with the
@ -9,26 +13,35 @@ def get_example_pagination_serializer_class(results_serializer_class):
:param results_serializer_class: The serializer class that needs to be added as
results.
:param add_field_options: When true will include the field_options field on the
returned serializer.
:type results_serializer_class: Serializer
:return: The generated pagination serializer.
:rtype: Serializer
"""
fields = {
'count': serializers.IntegerField(help_text='The total amount of results.'),
'next': serializers.URLField(
allow_blank=True,
allow_null=True,
help_text='URL to the next page.'
),
'previous': serializers.URLField(
allow_blank=True,
allow_null=True,
help_text='URL to the previous page.'
),
'results': results_serializer_class(many=True)
}
serializer_name = 'PaginationSerializer'
if add_field_options:
fields['field_options'] = GridViewFieldOptionsField(required=False)
serializer_name = serializer_name + 'WithFieldOptions'
return type(
'PaginationSerializer' + results_serializer_class.__name__,
serializer_name + results_serializer_class.__name__,
(serializers.Serializer,),
{
'count': serializers.IntegerField(help_text='The total amount of results.'),
'next': serializers.URLField(
allow_blank=True,
allow_null=True,
help_text='URL to the next page.'
),
'previous': serializers.URLField(
allow_blank=True,
allow_null=True,
help_text='URL to the previous page.'
),
'results': results_serializer_class(many=True)
}
fields
)

View file

@ -7,7 +7,6 @@ from baserow.api.serializers import get_example_pagination_serializer_class
from baserow.core.utils import model_default_values, dict_to_object
from baserow.contrib.database.fields.registries import field_type_registry
logger = logging.getLogger(__name__)
@ -133,3 +132,6 @@ def get_example_row_serializer_class(add_id=False):
example_pagination_row_serializer_class = get_example_pagination_serializer_class(
get_example_row_serializer_class(True)
)
example_pagination_row_serializer_class_with_field_options = \
get_example_pagination_serializer_class(
get_example_row_serializer_class(True), add_field_options=True)

View file

@ -12,10 +12,11 @@ from baserow.api.pagination import PageNumberPagination
from baserow.api.schemas import get_error_schema
from baserow.core.exceptions import UserNotInGroupError
from baserow.contrib.database.api.rows.serializers import (
get_row_serializer_class, RowSerializer
get_row_serializer_class, RowSerializer,
example_pagination_row_serializer_class_with_field_options
)
from baserow.contrib.database.api.rows.serializers import (
get_example_row_serializer_class, example_pagination_row_serializer_class
get_example_row_serializer_class
)
from baserow.contrib.database.api.views.grid.serializers import GridViewSerializer
from baserow.contrib.database.views.exceptions import (
@ -92,7 +93,7 @@ class GridViewView(APIView):
'`list_database_table_view_sortings` endpoints.'
),
responses={
200: example_pagination_row_serializer_class,
200: example_pagination_row_serializer_class_with_field_options,
400: get_error_schema(['ERROR_USER_NOT_IN_GROUP']),
404: get_error_schema(['ERROR_GRID_DOES_NOT_EXIST'])
}
@ -109,7 +110,7 @@ class GridViewView(APIView):
else the page number pagination.
Optionally the field options can also be included in the response if the the
`field_options` are provided in the includes GET parameter.
`field_options` are provided in the include GET parameter.
"""
view_handler = ViewHandler()
@ -144,7 +145,8 @@ class GridViewView(APIView):
# but when added to the context the fields don't have to be fetched from
# the database again when checking if they exist.
context = {'fields': [o['field'] for o in model._field_objects.values()]}
response.data.update(**GridViewSerializer(view, context=context).data)
serialized_view = GridViewSerializer(view, context=context).data
response.data['field_options'] = serialized_view['field_options']
return response

View file

@ -82,8 +82,8 @@ class UpdateViewSortSerializer(serializers.ModelSerializer):
class ViewSerializer(serializers.ModelSerializer):
type = serializers.SerializerMethodField()
table = TableSerializer()
filters = ViewFilterSerializer(many=True, source='viewfilter_set')
sortings = ViewSortSerializer(many=True, source='viewsort_set')
filters = ViewFilterSerializer(many=True, source='viewfilter_set', required=False)
sortings = ViewSortSerializer(many=True, source='viewsort_set', required=False)
class Meta:
model = View
@ -95,15 +95,23 @@ class ViewSerializer(serializers.ModelSerializer):
}
def __init__(self, *args, **kwargs):
include_filters = kwargs.pop('filters') if 'filters' in kwargs else False
include_sortings = kwargs.pop('sortings') if 'sortings' in kwargs else False
context = kwargs.setdefault("context", {})
context['include_filters'] = kwargs.pop('filters', False)
context['include_sortings'] = kwargs.pop('sortings', False)
super().__init__(*args, **kwargs)
if not include_filters:
self.fields.pop('filters')
def to_representation(self, instance):
# We remove the fields in to_representation rather than __init__ as otherwise
# drf-spectacular will not know that filters and sortings exist as optional
# return fields. This way the fields are still dynamic and also show up in the
# OpenAPI specification.
if not self.context['include_filters']:
self.fields.pop('filters', None)
if not include_sortings:
self.fields.pop('sortings')
if not self.context['include_sortings']:
self.fields.pop('sortings', None)
return super().to_representation(instance)
@extend_schema_field(OpenApiTypes.STR)
def get_type(self, instance):

View file

@ -54,7 +54,20 @@ class ViewsView(APIView):
type=OpenApiTypes.INT,
description='Returns only views of the table related to the provided '
'value.'
)
),
OpenApiParameter(
name='include',
location=OpenApiParameter.QUERY,
type=OpenApiTypes.STR,
description=(
'A comma separated list of extra attributes to include on each '
'view in the response. The supported attributes are `filters` and '
'`sortings`. '
'For example `include=filters,sortings` will add the attributes '
'`filters` and `sortings` to every returned view, containing '
'a list of the views filters and sortings respectively.'
)
),
],
tags=['Database table views'],
operation_id='list_database_table_views',
@ -117,7 +130,20 @@ class ViewsView(APIView):
type=OpenApiTypes.INT,
description='Creates a view for the table related to the provided '
'value.'
)
),
OpenApiParameter(
name='include',
location=OpenApiParameter.QUERY,
type=OpenApiTypes.STR,
description=(
'A comma separated list of extra attributes to include on each '
'view in the response. The supported attributes are `filters` and '
'`sortings`. '
'For example `include=filters,sortings` will add the attributes '
'`filters` and `sortings` to every returned view, containing '
'a list of the views filters and sortings respectively.'
)
),
],
tags=['Database table views'],
operation_id='create_database_table_view',
@ -176,7 +202,20 @@ class ViewView(APIView):
location=OpenApiParameter.PATH,
type=OpenApiTypes.INT,
description='Returns the view related to the provided value.'
)
),
OpenApiParameter(
name='include',
location=OpenApiParameter.QUERY,
type=OpenApiTypes.STR,
description=(
'A comma separated list of extra attributes to include on the '
'returned view. The supported attributes are are `filters` and '
'`sortings`. '
'For example `include=filters,sortings` will add the attributes '
'`filters` and `sortings` to every returned view, containing '
'a list of the views filters and sortings respectively.'
)
),
],
tags=['Database table views'],
operation_id='get_database_table_view',
@ -219,7 +258,20 @@ class ViewView(APIView):
location=OpenApiParameter.PATH,
type=OpenApiTypes.INT,
description='Updates the view related to the provided value.'
)
),
OpenApiParameter(
name='include',
location=OpenApiParameter.QUERY,
type=OpenApiTypes.STR,
description=(
'A comma separated list of extra attributes to include on the '
'returned view. The supported attributes are are `filters` and '
'`sortings`. '
'For example `include=filters,sortings` will add the attributes '
'`filters` and `sortings` to every returned view, containing '
'a list of the views filters and sortings respectively.'
)
),
],
tags=['Database table views'],
operation_id='update_database_table_view',

View file

@ -250,7 +250,7 @@ def test_allowed_includes():
request = Request(factory.get(
'/some-page/',
data={'includes': 'test_1,test_2'},
data={'include': 'test_1,test_2'},
))
@allowed_includes('test_1', 'test_3')
@ -262,7 +262,7 @@ def test_allowed_includes():
request = Request(factory.get(
'/some-page/',
data={'includes': 'test_3'},
data={'include': 'test_3'},
))
@allowed_includes('test_1', 'test_3')

View file

@ -218,7 +218,7 @@ def test_list_rows_include_field_options(api_client, data_fixture):
url = reverse('api:database:views:grid:list', kwargs={'view_id': grid.id})
response = api_client.get(
url,
{'includes': 'field_options'},
{'include': 'field_options'},
**{'HTTP_AUTHORIZATION': f'JWT {token}'}
)
response_json = response.json()
@ -230,6 +230,7 @@ def test_list_rows_include_field_options(api_client, data_fixture):
assert response_json['field_options'][str(number_field.id)]['width'] == 200
assert response_json['field_options'][str(number_field.id)]['hidden'] is False
assert response_json['field_options'][str(number_field.id)]['order'] == 32767
assert 'filters_disabled' not in response_json
@pytest.mark.django_db

View file

@ -98,7 +98,7 @@ def test_list_views_including_filters(api_client, data_fixture):
assert 'filters' not in response_json[1]
response = api_client.get(
'{}?includes=filters'.format(reverse(
'{}?include=filters'.format(reverse(
'api:database:views:list',
kwargs={'table_id': table_1.id}
)),
@ -148,7 +148,7 @@ def test_list_views_including_sortings(api_client, data_fixture):
assert 'sortings' not in response_json[1]
response = api_client.get(
'{}?includes=sortings'.format(reverse(
'{}?include=sortings'.format(reverse(
'api:database:views:list',
kwargs={'table_id': table_1.id}
)),
@ -232,7 +232,7 @@ def test_create_view(api_client, data_fixture):
assert 'sortings' not in response_json
response = api_client.post(
'{}?includes=filters,sortings'.format(
'{}?include=filters,sortings'.format(
reverse('api:database:views:list', kwargs={'table_id': table.id})
),
{
@ -318,7 +318,7 @@ def test_get_view(api_client, data_fixture):
url = reverse('api:database:views:item', kwargs={'view_id': view.id})
response = api_client.get(
'{}?includes=filters,sortings'.format(url),
'{}?include=filters,sortings'.format(url),
format='json',
HTTP_AUTHORIZATION=f'JWT {token}'
)
@ -417,7 +417,7 @@ def test_update_view(api_client, data_fixture):
filter_1 = data_fixture.create_view_filter(view=view)
url = reverse('api:database:views:item', kwargs={'view_id': view.id})
response = api_client.patch(
'{}?includes=filters,sortings'.format(url),
'{}?include=filters,sortings'.format(url),
{'filter_type': 'AND'},
format='json',
HTTP_AUTHORIZATION=f'JWT {token}'

View file

@ -9,6 +9,11 @@
* Made it possible to re-order fields in a grid view.
* Show the number of filters and sorts active in the header of a grid view.
* The first user to sign-up after installation now gets given staff status.
* Rename the "includes" get parameter across all API endpoints to "include" to be
consistent.
* Add missing include query parameter and corresponding response attributes to API docs.
* Remove incorrectly included "filters_disabled" field from
list_database_table_grid_view_rows api endpoint.
* Show an error to the user when the web socket connection could not be made and the
reconnect loop stops.
* Fixed 100X backend web socket errors when refreshing the page.

View file

@ -4,18 +4,18 @@ export default (client) => {
const config = {
params: {},
}
const includes = []
const include = []
if (includeFilters) {
includes.push('filters')
include.push('filters')
}
if (includeSortings) {
includes.push('sortings')
include.push('sortings')
}
if (includes.length > 0) {
config.params.includes = includes.join(',')
if (include.length > 0) {
config.params.include = include.join(',')
}
return client.get(`/database/views/table/${tableId}/`, config)

View file

@ -12,7 +12,7 @@ export default (client) => {
limit,
},
}
const includes = []
const include = []
if (offset !== null) {
config.params.offset = offset
@ -23,11 +23,11 @@ export default (client) => {
}
if (includeFieldOptions) {
includes.push('field_options')
include.push('field_options')
}
if (includes.length > 0) {
config.params.includes = includes.join(',')
if (include.length > 0) {
config.params.include = include.join(',')
}
return client.get(`/database/views/grid/${gridId}/`, config)