def test_rotate_token_key(data_fixture): user = data_fixture.create_user() token_1 = data_fixture.create_token(user=user) token_2 = data_fixture.create_token() handler = TokenHandler() with pytest.raises(TokenDoesNotBelongToUser): handler.rotate_token_key(user=user, token=token_2) old_key = token_1.key new_token = handler.rotate_token_key(user=user, token=token_1) assert len(new_token.key) == 32 assert old_key != new_token.key
def test_update_token(data_fixture): user = data_fixture.create_user() token_1 = data_fixture.create_token(user=user) token_2 = data_fixture.create_token() handler = TokenHandler() with pytest.raises(TokenDoesNotBelongToUser): handler.update_token(user=user, token=token_2, name='New') token_1 = handler.update_token(user=user, token=token_1, name='New') assert token_1.name == 'New' token_1 = Token.objects.get(pk=token_1.id) assert token_1.name == 'New'
def patch(self, request, table_id, row_id): """ Updates the row with the given row_id for the table with the given table_id. Also the post data is validated according to the tables field types. """ table = TableHandler().get_table(request.user, table_id) TokenHandler().check_table_permissions(request, 'update', table, False) # Small side effect of generating the model for only the fields that need to # change is that the response it not going to contain the other fields. It is # however much faster because it doesn't need to get the specific version of # all the field objects. field_ids = RowHandler().extract_field_ids_from_dict(request.data) model = table.get_model(field_ids=field_ids) validation_serializer = get_row_serializer_class(model) data = validate_data(validation_serializer, request.data) row = RowHandler().update_row(request.user, table, row_id, data, model) serializer_class = get_row_serializer_class(model, RowSerializer, is_response=True) serializer = serializer_class(row) return Response(serializer.data)
def post(self, request, table_id): """ Creates a new row for the given table_id. Also the post data is validated according to the tables field types. """ table = TableHandler().get_table(table_id) TokenHandler().check_table_permissions(request, 'create', table, False) model = table.get_model() validation_serializer = get_row_serializer_class(model) data = validate_data(validation_serializer, request.data) before_id = request.GET.get('before') before = (RowHandler().get_row(request.user, table, before_id, model) if before_id else None) row = RowHandler().create_row(request.user, table, data, model, before=before) serializer_class = get_row_serializer_class(model, RowSerializer, is_response=True) serializer = serializer_class(row) return Response(serializer.data)
def get(self, request, table_id): """ Lists all the rows of the given table id paginated. It is also possible to provide a search query. """ table = TableHandler().get_table(request.user, table_id) TokenHandler().check_table_permissions(request, 'read', table, False) model = table.get_model() search = request.GET.get('search') order_by = request.GET.get('order_by') queryset = model.objects.all().enhance_by_fields().order_by('id') if search: queryset = queryset.search_all_fields(search) if order_by: queryset = queryset.order_by_fields_string(order_by) paginator = PageNumberPagination( limit_page_size=settings.ROW_PAGE_SIZE_LIMIT) page = paginator.paginate_queryset(queryset, request, self) serializer_class = get_row_serializer_class(model, RowSerializer, is_response=True) serializer = serializer_class(page, many=True) return paginator.get_paginated_response(serializer.data)
def post(self, request, data): """Creates a new token for the authorized user.""" data['group'] = CoreHandler().get_group(data.pop('group')) token = TokenHandler().create_token(request.user, **data) serializer = TokenSerializer(token) return Response(serializer.data)
def test_get_by_key(data_fixture): user = data_fixture.create_user() data_fixture.create_user() group_1 = data_fixture.create_group(user=user) group_2 = data_fixture.create_group() token = data_fixture.create_token(user=user, group=group_1) data_fixture.create_token(user=user, group=group_2) handler = TokenHandler() with pytest.raises(TokenDoesNotExist): handler.get_by_key(key='abc') token_tmp = handler.get_by_key(key=token.key) assert token_tmp.id == token.id assert token.group_id == group_1.id assert isinstance(token_tmp, Token)
def test_update_token_usage(data_fixture): token_1 = data_fixture.create_token() handler = TokenHandler() assert token_1.handled_calls == 0 assert token_1.last_call is None with freeze_time('2020-01-01 12:00'): token_1 = handler.update_token_usage(token_1) assert token_1.handled_calls == 1 assert token_1.last_call == datetime(2020, 1, 1, 12, 00, tzinfo=timezone('UTC'))
def patch(self, request, data, token_id): """Updates the values of a token.""" token = TokenHandler().get_token(request.user, token_id) permissions = data.pop('permissions', None) rotate_key = data.pop('rotate_key', False) if len(data) > 0: token = TokenHandler().update_token(request.user, token, **data) if permissions: TokenHandler().update_token_permissions(request.user, token, **permissions) if rotate_key: token = TokenHandler().rotate_token_key(request.user, token) serializer = TokenSerializer(token) return Response(serializer.data)
def authenticate(self, request): auth = get_authorization_header(request).split() if not auth or auth[0].lower() != b"token": return None if len(auth) == 1: msg = _("Invalid token header. No token provided.") raise AuthenticationFailed( {"detail": msg, "error": "ERROR_INVALID_TOKEN_HEADER"} ) elif len(auth) > 2: msg = _("Invalid token header. Token string should not contain spaces.") raise AuthenticationFailed( {"detail": msg, "error": "ERROR_INVALID_TOKEN_HEADER"} ) decoded_key = auth[1].decode(HTTP_HEADER_ENCODING) handler = TokenHandler() try: token = handler.get_by_key(decoded_key) except UserNotInGroup: msg = _("The token's user does not belong to the group anymore.") raise AuthenticationFailed( {"detail": msg, "error": "ERROR_TOKEN_GROUP_MISMATCH"} ) except TokenDoesNotExist: msg = _("The provided token does not exist.") raise AuthenticationFailed( {"detail": msg, "error": "ERROR_TOKEN_DOES_NOT_EXIST"} ) if not token.user.is_active: raise AuthenticationFailed( { "detail": "The user related to the token is disabled.", "error": "ERROR_USER_NOT_ACTIVE", } ) token = handler.update_token_usage(token) request.user_token = token return token.user, token
def authenticate(self, request): auth = get_authorization_header(request).split() if not auth or auth[0].lower() != b'token': return None if len(auth) == 1: msg = _('Invalid token header. No token provided.') raise AuthenticationFailed({ 'detail': msg, 'error': 'ERROR_INVALID_TOKEN_HEADER' }) elif len(auth) > 2: msg = _( 'Invalid token header. Token string should not contain spaces.' ) raise AuthenticationFailed({ 'detail': msg, 'error': 'ERROR_INVALID_TOKEN_HEADER' }) decoded_key = auth[1].decode(HTTP_HEADER_ENCODING) handler = TokenHandler() try: token = handler.get_by_key(decoded_key) except UserNotInGroupError: msg = _('The token\'s user does not belong to the group anymore.') raise AuthenticationFailed({ 'detail': msg, 'error': 'ERROR_TOKEN_GROUP_MISMATCH' }) except TokenDoesNotExist: msg = _('The provided token does not exist.') raise AuthenticationFailed({ 'detail': msg, 'error': 'ERROR_TOKEN_DOES_NOT_EXIST' }) token = handler.update_token_usage(token) request.user_token = token return token.user, token
def delete(self, request, table_id, row_id): """ Deletes an existing row with the given row_id for table with the given table_id. """ table = TableHandler().get_table(table_id) TokenHandler().check_table_permissions(request, 'delete', table, False) RowHandler().delete_row(request.user, table, row_id) return Response(status=204)
def test_create_token(data_fixture): user = data_fixture.create_user() data_fixture.create_user() group_1 = data_fixture.create_group(user=user) group_2 = data_fixture.create_group() handler = TokenHandler() with pytest.raises(UserNotInGroupError): handler.create_token(user=user, group=group_2, name='Test') token = handler.create_token(user=user, group=group_1, name='Test') assert token.user_id == user.id assert token.group_id == group_1.id assert token.name == 'Test' assert len(token.key) == 32 assert Token.objects.all().count() == 1 permissions = TokenPermission.objects.all() assert permissions.count() == 4 assert permissions[0].token_id == token.id assert permissions[0].type == 'create' assert permissions[0].database_id is None assert permissions[0].table_id is None assert permissions[1].token_id == token.id assert permissions[1].type == 'read' assert permissions[1].database_id is None assert permissions[1].table_id is None assert permissions[2].token_id == token.id assert permissions[2].type == 'update' assert permissions[2].database_id is None assert permissions[2].table_id is None assert permissions[3].token_id == token.id assert permissions[3].type == 'delete' assert permissions[3].database_id is None assert permissions[3].table_id is None
def patch(self, request, data, token_id): """Updates the values of a token.""" token = TokenHandler().get_token( request.user, token_id, base_queryset=Token.objects.select_for_update()) permissions = data.pop("permissions", None) rotate_key = data.pop("rotate_key", False) if len(data) > 0: token = TokenHandler().update_token(request.user, token, **data) if permissions: TokenHandler().update_token_permissions(request.user, token, **permissions) if rotate_key: token = TokenHandler().rotate_token_key(request.user, token) serializer = TokenSerializer(token) return Response(serializer.data)
def test_delete_token(data_fixture): user = data_fixture.create_user() token_1 = data_fixture.create_token(user=user) token_2 = data_fixture.create_token() handler = TokenHandler() with pytest.raises(TokenDoesNotBelongToUser): handler.delete_token(user=user, token=token_2) handler.update_token_permissions(user, token_1, create=True, read=True, update=True, delete=True) handler.delete_token(user=user, token=token_1) assert Token.objects.all().count() == 1 assert Token.objects.all().first().id == token_2.id
def test_generate_token(data_fixture): user = data_fixture.create_user() group = data_fixture.create_group(user=user) handler = TokenHandler() assert len(handler.generate_unique_key(32)) == 32 assert len(handler.generate_unique_key(10)) == 10 assert handler.generate_unique_key(32) != handler.generate_unique_key(32) key = handler.generate_unique_key(32) assert not Token.objects.filter(key=key).exists() for char in string.ascii_letters + string.digits: data_fixture.create_token(key=char, user=user, group=group) with pytest.raises(MaximumUniqueTokenTriesError): handler.generate_unique_key(1, 3)
def get(self, request, table_id, row_id): """ Responds with a serializer version of the row related to the provided row_id and table_id. """ table = TableHandler().get_table(table_id) TokenHandler().check_table_permissions(request, 'read', table, False) model = table.get_model() row = RowHandler().get_row(request.user, table, row_id, model) serializer_class = get_row_serializer_class(model, RowSerializer, is_response=True) serializer = serializer_class(row) return Response(serializer.data)
def get(self, request, table_id): """ Lists all the rows of the given table id paginated. It is also possible to provide a search query. """ table = TableHandler().get_table(table_id) table.database.group.has_user(request.user, raise_error=True) TokenHandler().check_table_permissions(request, 'read', table, False) search = request.GET.get('search') order_by = request.GET.get('order_by') include = request.GET.get('include') exclude = request.GET.get('exclude') fields = RowHandler().get_include_exclude_fields( table, include, exclude) model = table.get_model(fields=fields, field_ids=[] if fields else None) queryset = model.objects.all().enhance_by_fields() if search: queryset = queryset.search_all_fields(search) if order_by: queryset = queryset.order_by_fields_string(order_by) filter_type = (FILTER_TYPE_OR if str(request.GET.get('filter_type')).upper() == 'OR' else FILTER_TYPE_AND) filter_object = { key: request.GET.getlist(key) for key in request.GET.keys() } queryset = queryset.filter_by_fields_object(filter_object, filter_type) paginator = PageNumberPagination( limit_page_size=settings.ROW_PAGE_SIZE_LIMIT) page = paginator.paginate_queryset(queryset, request, self) serializer_class = get_row_serializer_class(model, RowSerializer, is_response=True) serializer = serializer_class(page, many=True) return paginator.get_paginated_response(serializer.data)
def patch(self, request, table_id, row_id): """Moves the row to another position.""" table = TableHandler().get_table(table_id) TokenHandler().check_table_permissions(request, "update", table, False) model = table.get_model() before_id = request.GET.get("before_id") before = ( RowHandler().get_row(request.user, table, before_id, model) if before_id else None ) row = RowHandler().move_row( request.user, table, row_id, before=before, model=model ) serializer_class = get_row_serializer_class( model, RowSerializer, is_response=True ) serializer = serializer_class(row) return Response(serializer.data)
def patch(self, request, table_id, row_id): """ Updates the row with the given row_id for the table with the given table_id. Also the post data is validated according to the tables field types. """ table = TableHandler().get_table(table_id) TokenHandler().check_table_permissions(request, "update", table, False) field_ids = RowHandler().extract_field_ids_from_dict(request.data) model = table.get_model() validation_serializer = get_row_serializer_class(model, field_ids=field_ids) data = validate_data(validation_serializer, request.data) row = RowHandler().update_row(request.user, table, row_id, data, model) serializer_class = get_row_serializer_class( model, RowSerializer, is_response=True ) serializer = serializer_class(row) return Response(serializer.data)
def delete(self, request, token_id): """Deletes an existing token.""" token = TokenHandler().get_token(request.user, token_id) TokenHandler().delete_token(request.user, token) return Response(status=204)
def get(self, request, token_id): """Responds with a serialized token instance.""" token = TokenHandler().get_token(request.user, token_id) serializer = TokenSerializer(token) return Response(serializer.data)
def test_get_token(api_client, data_fixture): user, token = data_fixture.create_user_and_token() group_1 = data_fixture.create_group(user=user) group_2 = data_fixture.create_group() token_1 = data_fixture.create_token(user=user, group=group_1) token_2 = data_fixture.create_token(user=user, group=group_2) token_3 = data_fixture.create_token() database_1 = data_fixture.create_database_application(group=group_1) database_2 = data_fixture.create_database_application(group=group_1) data_fixture.create_database_table(database=database_1, create_table=False) data_fixture.create_database_table(database=database_1, create_table=False) table_3 = data_fixture.create_database_table(database=database_2, create_table=False) url = reverse('api:database:tokens:item', kwargs={'token_id': token_1.id}) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT random') assert response.status_code == HTTP_401_UNAUTHORIZED url = reverse('api:database:tokens:item', kwargs={'token_id': 99999}) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' url = reverse('api:database:tokens:item', kwargs={'token_id': token_3.id}) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' url = reverse('api:database:tokens:item', kwargs={'token_id': token_2.id}) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_400_BAD_REQUEST assert response.json()['error'] == 'ERROR_USER_NOT_IN_GROUP' url = reverse('api:database:tokens:item', kwargs={'token_id': token_1.id}) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['id'] == token_1.id assert response_json['name'] == token_1.name assert response_json['group'] == token_1.group_id assert response_json['key'] == token_1.key assert len(response_json['key']) == 32 assert response_json['permissions'] == { 'create': False, 'read': False, 'update': False, 'delete': False } TokenHandler().update_token_permissions(user, token_1, create=True, read=[database_2], update=[database_1, table_3], delete=False) url = reverse('api:database:tokens:item', kwargs={'token_id': token_1.id}) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['permissions']['create'] is True assert len(response_json['permissions']['read']) == 1 assert response_json['permissions']['read'][0] == [ 'database', database_2.id ] assert len(response_json['permissions']['update']) == 2 assert response_json['permissions']['update'][0] == [ 'database', database_1.id ] assert response_json['permissions']['update'][1] == ['table', table_3.id] assert response_json['permissions']['delete'] is False TokenHandler().update_token_permissions(user, token_1, create=[database_1, database_2], read=False, update=True, delete=[table_3]) url = reverse('api:database:tokens:item', kwargs={'token_id': token_1.id}) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert len(response_json['permissions']['create']) == 2 assert response_json['permissions']['create'][0] == [ 'database', database_1.id ] assert response_json['permissions']['create'][1] == [ 'database', database_2.id ] assert response_json['permissions']['read'] is False assert response_json['permissions']['update'] is True assert len(response_json['permissions']['delete']) == 1 assert response_json['permissions']['delete'][0] == ['table', table_3.id]
def test_delete_token(api_client, data_fixture): user, token = data_fixture.create_user_and_token() group_1 = data_fixture.create_group(user=user) group_2 = data_fixture.create_group() token_1 = data_fixture.create_token(user=user, group=group_1) token_2 = data_fixture.create_token(user=user, group=group_2) token_3 = data_fixture.create_token() TokenHandler().update_token_permissions(user, token_1, create=True, read=True, update=True, delete=True) url = reverse('api:database:tokens:item', kwargs={'token_id': token_1.id}) response = api_client.delete(url, format='json', HTTP_AUTHORIZATION=f'JWT random') assert response.status_code == HTTP_401_UNAUTHORIZED url = reverse('api:database:tokens:item', kwargs={'token_id': 99999}) response = api_client.delete(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' url = reverse('api:database:tokens:item', kwargs={'token_id': token_3.id}) response = api_client.delete(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' url = reverse('api:database:tokens:item', kwargs={'token_id': token_2.id}) response = api_client.delete(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_400_BAD_REQUEST assert response.json()['error'] == 'ERROR_USER_NOT_IN_GROUP' url = reverse('api:database:tokens:item', kwargs={'token_id': token_3.id}) response = api_client.delete(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_404_NOT_FOUND assert response_json['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' assert Token.objects.all().count() == 3 assert TokenPermission.objects.all().count() == 4 url = reverse('api:database:tokens:item', kwargs={'token_id': token_1.id}) response = api_client.delete(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_204_NO_CONTENT assert Token.objects.all().count() == 2 assert TokenPermission.objects.all().count() == 0
def test_delete_row(api_client, data_fixture): user, jwt_token = data_fixture.create_user_and_token() table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table() data_fixture.create_text_field(table=table, order=0, name='Color', text_default='white') data_fixture.create_number_field(table=table, order=1, name='Horsepower') data_fixture.create_boolean_field(table=table, order=2, name='For sale') token = TokenHandler().create_token(user, table.database.group, 'Good') wrong_token = TokenHandler().create_token(user, table.database.group, 'Wrong') TokenHandler().update_token_permissions(user, wrong_token, True, True, True, False) model = table.get_model() row_1 = model.objects.create() row_2 = model.objects.create() row_3 = model.objects.create() url = reverse('api:database:rows:item', kwargs={ 'table_id': 9999, 'row_id': 9999 }) response = api_client.delete(url, HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_TABLE_DOES_NOT_EXIST' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': 9999 }) response = api_client.delete(url, HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_ROW_DOES_NOT_EXIST' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.delete(url, HTTP_AUTHORIZATION=f'Token abc123') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.delete(url, HTTP_AUTHORIZATION=f'Token {wrong_token.key}') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_NO_PERMISSION_TO_TABLE' url = reverse('api:database:rows:item', kwargs={ 'table_id': table_2.id, 'row_id': row_1.id }) response = api_client.delete(url, HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_400_BAD_REQUEST assert response.json()['error'] == 'ERROR_USER_NOT_IN_GROUP' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.delete(url, HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == 204 assert model.objects.count() == 2 assert model.objects.all()[0].id == row_2.id url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_2.id }) response = api_client.delete(url, HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == 204 assert model.objects.count() == 1 url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_3.id }) response = api_client.delete(url, HTTP_AUTHORIZATION=f'Token {token.key}') assert response.status_code == 204 assert model.objects.count() == 0
def test_update_row(api_client, data_fixture): user, jwt_token = data_fixture.create_user_and_token() table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table() text_field = data_fixture.create_text_field(table=table, order=0, name='Color', text_default='white') number_field = data_fixture.create_number_field(table=table, order=1, name='Horsepower') boolean_field = data_fixture.create_boolean_field(table=table, order=2, name='For sale') token = TokenHandler().create_token(user, table.database.group, 'Good') wrong_token = TokenHandler().create_token(user, table.database.group, 'Wrong') TokenHandler().update_token_permissions(user, wrong_token, True, True, False, True) model = table.get_model() row_1 = model.objects.create() row_2 = model.objects.create() url = reverse('api:database:rows:item', kwargs={ 'table_id': 9999, 'row_id': 9999 }) response = api_client.patch(url, {f'field_{text_field.id}': 'Orange'}, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_TABLE_DOES_NOT_EXIST' url = reverse('api:database:rows:item', kwargs={ 'table_id': table_2.id, 'row_id': row_1.id }) response = api_client.patch(url, {f'field_{text_field.id}': 'Orange'}, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_400_BAD_REQUEST assert response.json()['error'] == 'ERROR_USER_NOT_IN_GROUP' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.patch(url, {f'field_{text_field.id}': 'Orange'}, format='json', HTTP_AUTHORIZATION='Token abc123') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.patch(url, {f'field_{text_field.id}': 'Orange'}, format='json', HTTP_AUTHORIZATION=f'Token {wrong_token.key}') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_NO_PERMISSION_TO_TABLE' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': 99999 }) response = api_client.patch(url, {f'field_{text_field.id}': 'Orange'}, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_ROW_DOES_NOT_EXIST' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.patch(url, { f'field_{text_field.id}': 'Green', f'field_{number_field.id}': -10, f'field_{boolean_field.id}': None }, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_400_BAD_REQUEST assert response_json['error'] == 'ERROR_REQUEST_BODY_VALIDATION' assert len(response_json['detail']) == 2 assert response_json['detail'][f'field_{number_field.id}'][0][ 'code'] == 'min_value' assert response_json['detail'][f'field_{boolean_field.id}'][0][ 'code'] == 'null' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.patch(url, { f'field_{text_field.id}': 'Green', f'field_{number_field.id}': 120, f'field_{boolean_field.id}': True }, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json_row_1 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_1['id'] == row_1.id assert response_json_row_1[f'field_{text_field.id}'] == 'Green' assert response_json_row_1[f'field_{number_field.id}'] == 120 assert response_json_row_1[f'field_{boolean_field.id}'] is True row_1.refresh_from_db() assert getattr(row_1, f'field_{text_field.id}') == 'Green' assert getattr(row_1, f'field_{number_field.id}') == 120 assert getattr(row_1, f'field_{boolean_field.id}') is True response = api_client.patch(url, {f'field_{text_field.id}': 'Purple'}, format='json', HTTP_AUTHORIZATION=f'Token {token.key}') response_json_row_1 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_1[f'field_{text_field.id}'] == 'Purple' row_1.refresh_from_db() assert getattr(row_1, f'field_{text_field.id}') == 'Purple' response = api_client.patch(url, {f'field_{text_field.id}': 'Orange'}, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json_row_1 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_1[f'field_{text_field.id}'] == 'Orange' # Because the model is generated only for the field we want to change the other # fields are not included in the serializer. assert f'field_{number_field.id}' not in response_json_row_1 assert f'field_{boolean_field.id}' not in response_json_row_1 row_1.refresh_from_db() assert getattr(row_1, f'field_{text_field.id}') == 'Orange' assert getattr(row_1, f'field_{number_field.id}') == 120 assert getattr(row_1, f'field_{boolean_field.id}') is True url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_2.id }) response = api_client.patch(url, { f'field_{text_field.id}': 'Blue', f'field_{number_field.id}': 50, f'field_{boolean_field.id}': False }, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json_row_2 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_2['id'] == row_2.id assert response_json_row_2[f'field_{text_field.id}'] == 'Blue' assert response_json_row_2[f'field_{number_field.id}'] == 50 assert response_json_row_2[f'field_{boolean_field.id}'] is False row_2.refresh_from_db() assert getattr(row_2, f'field_{text_field.id}') == 'Blue' assert getattr(row_2, f'field_{number_field.id}') == 50 assert getattr(row_2, f'field_{boolean_field.id}') is False url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_2.id }) response = api_client.patch(url, { f'field_{text_field.id}': None, f'field_{number_field.id}': None, f'field_{boolean_field.id}': False }, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json_row_2 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_2['id'] == row_2.id assert response_json_row_2[f'field_{text_field.id}'] is None assert response_json_row_2[f'field_{number_field.id}'] is None assert response_json_row_2[f'field_{boolean_field.id}'] is False row_2.refresh_from_db() assert getattr(row_2, f'field_{text_field.id}') is None assert getattr(row_2, f'field_{number_field.id}') is None assert getattr(row_2, f'field_{boolean_field.id}') is False table_3 = data_fixture.create_database_table(user=user) decimal_field = data_fixture.create_number_field(table=table_3, order=0, name='Price', number_type='DECIMAL', number_decimal_places=2) model_3 = table_3.get_model() row_3 = model_3.objects.create() url = reverse('api:database:rows:item', kwargs={ 'table_id': table_3.id, 'row_id': row_3.id }) response = api_client.patch(url, {f'field_{decimal_field.id}': 10.22}, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json[f'field_{decimal_field.id}'] == '10.22' row_3.refresh_from_db() assert getattr(row_3, f'field_{decimal_field.id}') == Decimal('10.22')
def test_get_row(api_client, data_fixture): user, jwt_token = data_fixture.create_user_and_token() table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table() text_field = data_fixture.create_text_field(table=table, order=0, name='Color', text_default='white') number_field = data_fixture.create_number_field(table=table, order=1, name='Horsepower') boolean_field = data_fixture.create_boolean_field(table=table, order=2, name='For sale') token = TokenHandler().create_token(user, table.database.group, 'Good') wrong_token = TokenHandler().create_token(user, table.database.group, 'Wrong') TokenHandler().update_token_permissions(user, wrong_token, True, False, True, True) model = table.get_model() row_1 = model.objects.create( **{ f'field_{text_field.id}': 'Green', f'field_{number_field.id}': 120, f'field_{boolean_field.id}': False }) row_2 = model.objects.create( **{ f'field_{text_field.id}': 'Purple', f'field_{number_field.id}': 240, f'field_{boolean_field.id}': True }) url = reverse('api:database:rows:item', kwargs={ 'table_id': 9999, 'row_id': 9999 }) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_TABLE_DOES_NOT_EXIST' url = reverse('api:database:rows:item', kwargs={ 'table_id': table_2.id, 'row_id': row_1.id }) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_400_BAD_REQUEST assert response.json()['error'] == 'ERROR_USER_NOT_IN_GROUP' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.get(url, format='json', HTTP_AUTHORIZATION='Token abc123') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'Token {wrong_token.key}') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_NO_PERMISSION_TO_TABLE' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': 99999 }) response = api_client.get(url, {f'field_{text_field.id}': 'Orange'}, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_ROW_DOES_NOT_EXIST' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_1.id }) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['id'] == row_1.id assert response_json[f'field_{text_field.id}'] == 'Green' assert response_json[f'field_{number_field.id}'] == 120 assert response_json[f'field_{boolean_field.id}'] is False url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': row_2.id }) response = api_client.get(url, format='json', HTTP_AUTHORIZATION=f'Token {token.key}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['id'] == row_2.id assert response_json[f'field_{text_field.id}'] == 'Purple' assert response_json[f'field_{number_field.id}'] == 240 assert response_json[f'field_{boolean_field.id}'] is True
def test_create_row(api_client, data_fixture): user, jwt_token = data_fixture.create_user_and_token() table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table() text_field = data_fixture.create_text_field(table=table, order=0, name='Color', text_default='white') number_field = data_fixture.create_number_field(table=table, order=1, name='Horsepower') boolean_field = data_fixture.create_boolean_field(table=table, order=2, name='For sale') text_field_2 = data_fixture.create_text_field(table=table, order=3, name='Description') token = TokenHandler().create_token(user, table.database.group, 'Good') wrong_token = TokenHandler().create_token(user, table.database.group, 'Wrong') TokenHandler().update_token_permissions(user, wrong_token, False, True, True, True) response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': 99999}), {f'field_{text_field.id}': 'Test 1'}, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_TABLE_DOES_NOT_EXIST' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), {f'field_{text_field.id}': 'Test 1'}, format='json', HTTP_AUTHORIZATION='Token abc123') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), {f'field_{text_field.id}': 'Test 1'}, format='json', HTTP_AUTHORIZATION=f'Token {wrong_token.key}') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_NO_PERMISSION_TO_TABLE' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table_2.id}), {f'field_{text_field.id}': 'Green'}, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_400_BAD_REQUEST assert response.json()['error'] == 'ERROR_USER_NOT_IN_GROUP' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), { f'field_{text_field.id}': 'Green', f'field_{number_field.id}': -10, f'field_{boolean_field.id}': None, f'field_{text_field_2.id}': None }, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_400_BAD_REQUEST assert response_json['error'] == 'ERROR_REQUEST_BODY_VALIDATION' assert len(response_json['detail']) == 2 assert response_json['detail'][f'field_{number_field.id}'][0][ 'code'] == 'min_value' assert response_json['detail'][f'field_{boolean_field.id}'][0][ 'code'] == 'null' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), {}, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json_row_1 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_1[f'field_{text_field.id}'] == 'white' assert not response_json_row_1[f'field_{number_field.id}'] assert response_json_row_1[f'field_{boolean_field.id}'] is False assert response_json_row_1[f'field_{text_field_2.id}'] is None response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), { f'field_{number_field.id}': None, f'field_{boolean_field.id}': False, f'field_{text_field_2.id}': '', }, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json_row_2 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_2[f'field_{text_field.id}'] == 'white' assert not response_json_row_2[f'field_{number_field.id}'] assert response_json_row_2[f'field_{boolean_field.id}'] is False assert response_json_row_2[f'field_{text_field_2.id}'] == '' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), { f'field_{text_field.id}': 'Green', f'field_{number_field.id}': 120, f'field_{boolean_field.id}': True, f'field_{text_field_2.id}': 'Not important', }, format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json_row_3 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_3[f'field_{text_field.id}'] == 'Green' assert response_json_row_3[f'field_{number_field.id}'] == 120 assert response_json_row_3[f'field_{boolean_field.id}'] assert response_json_row_3[f'field_{text_field_2.id}'] == 'Not important' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), { f'field_{text_field.id}': 'Purple', f'field_{number_field.id}': 240, f'field_{boolean_field.id}': True, f'field_{text_field_2.id}': '' }, format='json', HTTP_AUTHORIZATION=f'Token {token.key}') response_json_row_4 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_4[f'field_{text_field.id}'] == 'Purple' assert response_json_row_4[f'field_{number_field.id}'] == 240 assert response_json_row_4[f'field_{boolean_field.id}'] assert response_json_row_4[f'field_{text_field_2.id}'] == '' model = table.get_model() assert model.objects.all().count() == 4 rows = model.objects.all().order_by('id') row_1 = rows[0] assert row_1.id == response_json_row_1['id'] assert getattr(row_1, f'field_{text_field.id}') == 'white' assert getattr(row_1, f'field_{number_field.id}') is None assert getattr(row_1, f'field_{boolean_field.id}') is False assert getattr(row_1, f'field_{text_field_2.id}') is None row_2 = rows[1] assert row_2.id == response_json_row_2['id'] assert getattr(row_2, f'field_{text_field.id}') == 'white' assert getattr(row_2, f'field_{number_field.id}') is None assert getattr(row_2, f'field_{boolean_field.id}') is False assert getattr(row_1, f'field_{text_field_2.id}') is None row_3 = rows[2] assert row_3.id == response_json_row_3['id'] assert getattr(row_3, f'field_{text_field.id}') == 'Green' assert getattr(row_3, f'field_{number_field.id}') == 120 assert getattr(row_3, f'field_{boolean_field.id}') is True assert getattr(row_3, f'field_{text_field_2.id}') == 'Not important' row_4 = rows[3] assert row_4.id == response_json_row_4['id'] assert getattr(row_4, f'field_{text_field.id}') == 'Purple' assert getattr(row_4, f'field_{number_field.id}') == 240 assert getattr(row_4, f'field_{boolean_field.id}') is True assert getattr(row_4, f'field_{text_field_2.id}') == ''
def test_list_rows(api_client, data_fixture): user, jwt_token = data_fixture.create_user_and_token(email='*****@*****.**', password='******', first_name='Test1') table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table() field_1 = data_fixture.create_text_field(name='Name', table=table, primary=True) field_2 = data_fixture.create_number_field(name='Price', table=table) token = TokenHandler().create_token(user, table.database.group, 'Good') wrong_token = TokenHandler().create_token(user, table.database.group, 'Wrong') TokenHandler().update_token_permissions(user, wrong_token, True, False, True, True) model = table.get_model(attribute_names=True) row_1 = model.objects.create(name='Product 1', price=50) row_2 = model.objects.create(name='Product 2/3', price=100) row_3 = model.objects.create(name='Product 3', price=150) row_4 = model.objects.create(name='Last product', price=200) response = api_client.get(reverse('api:database:rows:list', kwargs={'table_id': 999999}), format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_404_NOT_FOUND assert response.json()['error'] == 'ERROR_TABLE_DOES_NOT_EXIST' response = api_client.get(reverse('api:database:rows:list', kwargs={'table_id': table_2.id}), format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_400_BAD_REQUEST assert response.json()['error'] == 'ERROR_USER_NOT_IN_GROUP' response = api_client.get(reverse('api:database:rows:list', kwargs={'table_id': table.id}), format='json', HTTP_AUTHORIZATION='Token abc123') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_TOKEN_DOES_NOT_EXIST' response = api_client.get(reverse('api:database:rows:list', kwargs={'table_id': table.id}), format='json', HTTP_AUTHORIZATION=f'Token {wrong_token.key}') assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()['error'] == 'ERROR_NO_PERMISSION_TO_TABLE' response = api_client.get(reverse('api:database:rows:list', kwargs={'table_id': table.id}), format='json', HTTP_AUTHORIZATION=f'Token {token.key}') assert response.status_code == HTTP_200_OK response = api_client.get(reverse('api:database:rows:list', kwargs={'table_id': table.id}), format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['count'] == 4 assert len(response_json['results']) == 4 assert response_json['results'][0]['id'] == row_1.id assert response_json['results'][0][f'field_{field_1.id}'] == 'Product 1' assert response_json['results'][0][f'field_{field_2.id}'] == 50 url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?size=2&page=1', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['count'] == 4 assert len(response_json['results']) == 2 assert response_json['results'][0]['id'] == row_1.id assert response_json['results'][1]['id'] == row_2.id url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?size=2&page=2', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['count'] == 4 assert len(response_json['results']) == 2 assert response_json['results'][0]['id'] == row_3.id assert response_json['results'][1]['id'] == row_4.id url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?size=2&page=3', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_400_BAD_REQUEST assert response_json['error'] == 'ERROR_INVALID_PAGE' url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?size=201', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_400_BAD_REQUEST assert response_json['error'] == 'ERROR_PAGE_SIZE_LIMIT' url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?search=Product 1', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['count'] == 1 assert len(response_json['results']) == 1 assert response_json['results'][0]['id'] == row_1.id url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?search=1', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['count'] == 1 assert len(response_json['results']) == 1 assert response_json['results'][0]['id'] == row_1.id url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?search=3', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['count'] == 2 assert len(response_json['results']) == 2 assert response_json['results'][0]['id'] == row_2.id assert response_json['results'][1]['id'] == row_3.id url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?search=200', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['count'] == 1 assert len(response_json['results']) == 1 assert response_json['results'][0]['id'] == row_4.id url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?order_by=field_999999', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_400_BAD_REQUEST response_json = response.json() assert response_json['error'] == 'ERROR_ORDER_BY_FIELD_NOT_FOUND' assert response_json['detail'] == ( 'The field field_999999 was not found in the table.') number_field_type = field_type_registry.get('number') old_can_order_by = number_field_type.can_order_by number_field_type.can_order_by = False url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?order_by=-field_{field_2.id}', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') assert response.status_code == HTTP_400_BAD_REQUEST response_json = response.json() assert response_json['error'] == 'ERROR_ORDER_BY_FIELD_NOT_POSSIBLE' assert response_json['detail'] == ( f'It is not possible to order by field_{field_2.id} because the field type ' f'number does not support filtering.') number_field_type.can_order_by = old_can_order_by url = reverse('api:database:rows:list', kwargs={'table_id': table.id}) response = api_client.get(f'{url}?order_by=-field_{field_2.id}', format='json', HTTP_AUTHORIZATION=f'JWT {jwt_token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['count'] == 4 assert len(response_json['results']) == 4 assert response_json['results'][0]['id'] == row_4.id assert response_json['results'][1]['id'] == row_3.id assert response_json['results'][2]['id'] == row_2.id assert response_json['results'][3]['id'] == row_1.id
def test_move_row(api_client, data_fixture): user, jwt_token = data_fixture.create_user_and_token() table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table() token = TokenHandler().create_token(user, table.database.group, "Good") wrong_token = TokenHandler().create_token(user, table.database.group, "Wrong") TokenHandler().update_token_permissions(user, wrong_token, True, True, False, True) handler = RowHandler() row_1 = handler.create_row(user=user, table=table) row_2 = handler.create_row(user=user, table=table) row_3 = handler.create_row(user=user, table=table) url = reverse("api:database:rows:move", kwargs={ "table_id": 9999, "row_id": 9999 }) response = api_client.patch( url, format="json", HTTP_AUTHORIZATION=f"JWT {jwt_token}", ) assert response.status_code == HTTP_404_NOT_FOUND assert response.json()["error"] == "ERROR_TABLE_DOES_NOT_EXIST" url = reverse("api:database:rows:move", kwargs={ "table_id": table_2.id, "row_id": row_1.id }) response = api_client.patch( url, format="json", HTTP_AUTHORIZATION=f"JWT {jwt_token}", ) assert response.status_code == HTTP_400_BAD_REQUEST assert response.json()["error"] == "ERROR_USER_NOT_IN_GROUP" url = reverse("api:database:rows:move", kwargs={ "table_id": table.id, "row_id": row_1.id }) response = api_client.patch( url, format="json", HTTP_AUTHORIZATION="Token abc123", ) assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()["error"] == "ERROR_TOKEN_DOES_NOT_EXIST" url = reverse("api:database:rows:move", kwargs={ "table_id": table.id, "row_id": row_1.id }) response = api_client.patch( url, format="json", HTTP_AUTHORIZATION=f"Token {wrong_token.key}", ) assert response.status_code == HTTP_401_UNAUTHORIZED assert response.json()["error"] == "ERROR_NO_PERMISSION_TO_TABLE" url = reverse("api:database:rows:move", kwargs={ "table_id": table.id, "row_id": 99999 }) response = api_client.patch( url, format="json", HTTP_AUTHORIZATION=f"JWT {jwt_token}", ) assert response.status_code == HTTP_404_NOT_FOUND assert response.json()["error"] == "ERROR_ROW_DOES_NOT_EXIST" url = reverse("api:database:rows:move", kwargs={ "table_id": table.id, "row_id": row_1.id }) response = api_client.patch( f"{url}?before_id=-1", format="json", HTTP_AUTHORIZATION=f"JWT {jwt_token}", ) assert response.status_code == HTTP_404_NOT_FOUND assert response.json()["error"] == "ERROR_ROW_DOES_NOT_EXIST" url = reverse("api:database:rows:move", kwargs={ "table_id": table.id, "row_id": row_1.id }) response = api_client.patch( url, format="json", HTTP_AUTHORIZATION=f"JWT {jwt_token}", ) response_json_row_1 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_1["id"] == row_1.id assert response_json_row_1["order"] == "4.00000000000000000000" row_1.refresh_from_db() row_2.refresh_from_db() row_3.refresh_from_db() assert row_1.order == Decimal("4.00000000000000000000") assert row_2.order == Decimal("2.00000000000000000000") assert row_3.order == Decimal("3.00000000000000000000") url = reverse("api:database:rows:move", kwargs={ "table_id": table.id, "row_id": row_1.id }) response = api_client.patch( f"{url}?before_id={row_3.id}", format="json", HTTP_AUTHORIZATION=f"Token {token.key}", ) response_json_row_1 = response.json() assert response.status_code == HTTP_200_OK assert response_json_row_1["id"] == row_1.id assert response_json_row_1["order"] == "2.99999999999999999999" row_1.refresh_from_db() row_2.refresh_from_db() row_3.refresh_from_db() assert row_1.order == Decimal("2.99999999999999999999") assert row_2.order == Decimal("2.00000000000000000000") assert row_3.order == Decimal("3.00000000000000000000")