mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-07 22:35:36 +00:00
Prevent triggering webhook if query param is provided on row create, update, or delete endpoints
This commit is contained in:
parent
27e24b7a8b
commit
851826141e
13 changed files with 507 additions and 10 deletions
backend
src/baserow/contrib/database
tests/baserow/contrib/database/api
changelog/entries/unreleased/feature
web-frontend
locales
modules/database/components/docs/sections
|
@ -54,6 +54,7 @@ from baserow.contrib.database.api.tokens.authentications import TokenAuthenticat
|
|||
from baserow.contrib.database.api.tokens.errors import ERROR_NO_PERMISSION_TO_TABLE
|
||||
from baserow.contrib.database.api.utils import (
|
||||
extract_link_row_joins_from_request,
|
||||
extract_send_webhook_events_from_params,
|
||||
extract_user_field_names_from_params,
|
||||
get_include_exclude_fields,
|
||||
)
|
||||
|
@ -445,6 +446,16 @@ class RowsView(APIView):
|
|||
"field names (e.g., field_123)."
|
||||
),
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="send_webhook_events",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
description=(
|
||||
"A flag query parameter that triggers webhooks after the operation,"
|
||||
" if set to `y`, `yes`, `true`, `t`, `on`, `1`, `or` left empty. "
|
||||
"Defaults to `true`"
|
||||
),
|
||||
),
|
||||
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
|
||||
CLIENT_UNDO_REDO_ACTION_GROUP_ID_SCHEMA_PARAMETER,
|
||||
],
|
||||
|
@ -514,6 +525,7 @@ class RowsView(APIView):
|
|||
)
|
||||
|
||||
user_field_names = extract_user_field_names_from_params(request.GET)
|
||||
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
|
||||
|
||||
model = table.get_model()
|
||||
|
||||
|
@ -537,6 +549,7 @@ class RowsView(APIView):
|
|||
model=model,
|
||||
before_row=before_row,
|
||||
user_field_names=user_field_names,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
except ValidationError as e:
|
||||
raise RequestBodyValidationException(detail=e.message)
|
||||
|
@ -771,6 +784,16 @@ class RowView(APIView):
|
|||
"field names (e.g., field_123)."
|
||||
),
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="send_webhook_events",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
description=(
|
||||
"A flag query parameter that triggers webhooks after the operation,"
|
||||
" if set to `y`, `yes`, `true`, `t`, `on`, `1`, `or` left empty. "
|
||||
"Defaults to `true`"
|
||||
),
|
||||
),
|
||||
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
|
||||
CLIENT_UNDO_REDO_ACTION_GROUP_ID_SCHEMA_PARAMETER,
|
||||
],
|
||||
|
@ -833,6 +856,7 @@ class RowView(APIView):
|
|||
TokenHandler().check_table_permissions(request, "update", table, False)
|
||||
|
||||
user_field_names = extract_user_field_names_from_params(request.GET)
|
||||
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
|
||||
field_ids, field_names = None, None
|
||||
|
||||
if user_field_names:
|
||||
|
@ -852,7 +876,11 @@ class RowView(APIView):
|
|||
try:
|
||||
data["id"] = int(row_id)
|
||||
row = action_type_registry.get_by_type(UpdateRowsActionType).do(
|
||||
request.user, table, [data], model
|
||||
request.user,
|
||||
table,
|
||||
[data],
|
||||
model=model,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)[0]
|
||||
except ValidationError as exc:
|
||||
raise RequestBodyValidationException(detail=exc.message) from exc
|
||||
|
@ -877,6 +905,16 @@ class RowView(APIView):
|
|||
type=OpenApiTypes.INT,
|
||||
description="Deletes the row related to the value.",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="send_webhook_events",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
description=(
|
||||
"A flag query parameter that triggers webhooks after the operation,"
|
||||
" if set to `y`, `yes`, `true`, `t`, `on`, `1`, `or` left empty. "
|
||||
"Defaults to `true`"
|
||||
),
|
||||
),
|
||||
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
|
||||
CLIENT_UNDO_REDO_ACTION_GROUP_ID_SCHEMA_PARAMETER,
|
||||
],
|
||||
|
@ -913,11 +951,13 @@ class RowView(APIView):
|
|||
table_id.
|
||||
"""
|
||||
|
||||
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
|
||||
|
||||
table = TableHandler().get_table(table_id)
|
||||
TokenHandler().check_table_permissions(request, "delete", table, False)
|
||||
|
||||
action_type_registry.get_by_type(DeleteRowActionType).do(
|
||||
request.user, table, row_id
|
||||
request.user, table, row_id, send_webhook_events=send_webhook_events
|
||||
)
|
||||
|
||||
return Response(status=204)
|
||||
|
@ -961,6 +1001,16 @@ class RowMoveView(APIView):
|
|||
"field names (e.g., field_123)."
|
||||
),
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="send_webhook_events",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
description=(
|
||||
"A flag query parameter that triggers webhooks after the operation,"
|
||||
" if set to `y`, `yes`, `true`, `t`, `on`, `1`, `or` left empty. "
|
||||
"Defaults to `true`"
|
||||
),
|
||||
),
|
||||
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
|
||||
CLIENT_UNDO_REDO_ACTION_GROUP_ID_SCHEMA_PARAMETER,
|
||||
],
|
||||
|
@ -1001,6 +1051,7 @@ class RowMoveView(APIView):
|
|||
TokenHandler().check_table_permissions(request, "update", table, False)
|
||||
|
||||
user_field_names = extract_user_field_names_from_params(request.GET)
|
||||
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
|
||||
|
||||
model = table.get_model()
|
||||
|
||||
|
@ -1014,7 +1065,12 @@ class RowMoveView(APIView):
|
|||
)
|
||||
|
||||
row = action_type_registry.get_by_type(MoveRowActionType).do(
|
||||
request.user, table, row_id, before_row=before_row, model=model
|
||||
request.user,
|
||||
table,
|
||||
row_id,
|
||||
before_row=before_row,
|
||||
model=model,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
|
||||
serializer_class = get_row_serializer_class(
|
||||
|
@ -1055,6 +1111,16 @@ class BatchRowsView(APIView):
|
|||
"field names (e.g., field_123)."
|
||||
),
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="send_webhook_events",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
description=(
|
||||
"A flag query parameter that triggers webhooks after the operation,"
|
||||
" if set to `y`, `yes`, `true`, `t`, `on`, `1`, `or` left empty. "
|
||||
"Defaults to `true`"
|
||||
),
|
||||
),
|
||||
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
|
||||
CLIENT_UNDO_REDO_ACTION_GROUP_ID_SCHEMA_PARAMETER,
|
||||
],
|
||||
|
@ -1120,6 +1186,7 @@ class BatchRowsView(APIView):
|
|||
model = table.get_model()
|
||||
|
||||
user_field_names = extract_user_field_names_from_params(request.GET)
|
||||
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
|
||||
before_id = query_params.get("before")
|
||||
before_row = (
|
||||
RowHandler().get_row(request.user, table, before_id, model)
|
||||
|
@ -1139,7 +1206,12 @@ class BatchRowsView(APIView):
|
|||
|
||||
try:
|
||||
rows = action_type_registry.get_by_type(CreateRowsActionType).do(
|
||||
request.user, table, data["items"], before_row, model
|
||||
request.user,
|
||||
table,
|
||||
data["items"],
|
||||
before_row,
|
||||
model=model,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
except ValidationError as exc:
|
||||
raise RequestBodyValidationException(detail=exc.message)
|
||||
|
@ -1173,6 +1245,16 @@ class BatchRowsView(APIView):
|
|||
"field names (e.g., field_123)."
|
||||
),
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="send_webhook_events",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
description=(
|
||||
"A flag query parameter that triggers webhooks after the operation,"
|
||||
" if set to `y`, `yes`, `true`, `t`, `on`, `1`, `or` left empty. "
|
||||
"Defaults to `true`"
|
||||
),
|
||||
),
|
||||
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
|
||||
CLIENT_UNDO_REDO_ACTION_GROUP_ID_SCHEMA_PARAMETER,
|
||||
],
|
||||
|
@ -1237,6 +1319,7 @@ class BatchRowsView(APIView):
|
|||
model = table.get_model()
|
||||
|
||||
user_field_names = extract_user_field_names_from_params(request.GET)
|
||||
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
|
||||
|
||||
row_validation_serializer = get_row_serializer_class(
|
||||
model,
|
||||
|
@ -1253,7 +1336,11 @@ class BatchRowsView(APIView):
|
|||
|
||||
try:
|
||||
rows = action_type_registry.get_by_type(UpdateRowsActionType).do(
|
||||
request.user, table, data["items"], model
|
||||
request.user,
|
||||
table,
|
||||
data["items"],
|
||||
model=model,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
except ValidationError as e:
|
||||
raise RequestBodyValidationException(detail=e.message)
|
||||
|
@ -1280,6 +1367,16 @@ class BatchDeleteRowsView(APIView):
|
|||
type=OpenApiTypes.INT,
|
||||
description="Deletes the rows in the table related to the value.",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="send_webhook_events",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
description=(
|
||||
"A flag query parameter that triggers webhooks after the operation,"
|
||||
" if set to `y`, `yes`, `true`, `t`, `on`, `1`, `or` left empty. "
|
||||
"Defaults to `true`"
|
||||
),
|
||||
),
|
||||
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
|
||||
CLIENT_UNDO_REDO_ACTION_GROUP_ID_SCHEMA_PARAMETER,
|
||||
],
|
||||
|
@ -1327,10 +1424,13 @@ class BatchDeleteRowsView(APIView):
|
|||
table = TableHandler().get_table(table_id)
|
||||
TokenHandler().check_table_permissions(request, "delete", table, False)
|
||||
|
||||
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
|
||||
|
||||
action_type_registry.get_by_type(DeleteRowsActionType).do(
|
||||
request.user,
|
||||
table,
|
||||
row_ids=data["items"],
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
|
||||
return Response(status=204)
|
||||
|
|
|
@ -174,6 +174,20 @@ def extract_user_field_names_from_params(query_params):
|
|||
return str_to_bool(value)
|
||||
|
||||
|
||||
def extract_send_webhook_events_from_params(query_params) -> bool:
|
||||
"""
|
||||
Extracts the send_webhook_events parameter from the query_params and returns
|
||||
boolean value. Defaults to true if not provided or empty.
|
||||
"""
|
||||
|
||||
value = query_params.get("send_webhook_events")
|
||||
|
||||
if value is None or value == "":
|
||||
return True
|
||||
|
||||
return str_to_bool(value)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LinkedTargetField:
|
||||
field_id: int
|
||||
|
|
|
@ -58,6 +58,7 @@ class CreateRowActionType(UndoableActionType):
|
|||
model: Optional[Type[GeneratedTableModel]] = None,
|
||||
before_row: Optional[GeneratedTableModel] = None,
|
||||
user_field_names: bool = False,
|
||||
send_webhook_events: bool = True,
|
||||
) -> GeneratedTableModel:
|
||||
"""
|
||||
Creates a new row for a given table with the provided values if the user
|
||||
|
@ -76,6 +77,8 @@ class CreateRowActionType(UndoableActionType):
|
|||
instance.
|
||||
:param user_field_names: Whether or not the values are keyed by the internal
|
||||
Baserow field name (field_1,field_2 etc) or by the user field names.
|
||||
:param send_webhook_events: If set the false then the webhooks will not be
|
||||
triggered. Defaults to true.
|
||||
:return: The created row instance.
|
||||
"""
|
||||
|
||||
|
@ -91,6 +94,7 @@ class CreateRowActionType(UndoableActionType):
|
|||
model=model,
|
||||
before_row=before_row,
|
||||
user_field_names=user_field_names,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
|
||||
workspace = table.database.workspace
|
||||
|
@ -148,6 +152,7 @@ class CreateRowsActionType(UndoableActionType):
|
|||
rows_values: List[Dict[str, Any]],
|
||||
before_row: Optional[GeneratedTableModel] = None,
|
||||
model: Optional[Type[GeneratedTableModel]] = None,
|
||||
send_webhook_events: bool = True,
|
||||
) -> List[GeneratedTableModel]:
|
||||
"""
|
||||
Creates rows for a given table with the provided values if the user
|
||||
|
@ -163,6 +168,8 @@ class CreateRowsActionType(UndoableActionType):
|
|||
the row with this id.
|
||||
:param model: If the correct model has already been generated it can be
|
||||
provided so that it does not have to be generated for a second time.
|
||||
:param send_webhook_events: If set the false then the webhooks will not be
|
||||
triggered. Defaults to true.
|
||||
:return: The created list of rows instances.
|
||||
"""
|
||||
|
||||
|
@ -177,6 +184,7 @@ class CreateRowsActionType(UndoableActionType):
|
|||
rows_values,
|
||||
before_row=before_row,
|
||||
model=model,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
|
||||
workspace = table.database.workspace
|
||||
|
@ -327,6 +335,7 @@ class DeleteRowActionType(UndoableActionType):
|
|||
table: Table,
|
||||
row_id: int,
|
||||
model: Optional[Type[GeneratedTableModel]] = None,
|
||||
send_webhook_events: bool = True,
|
||||
):
|
||||
"""
|
||||
Deletes an existing row of the given table and with row_id.
|
||||
|
@ -339,6 +348,8 @@ class DeleteRowActionType(UndoableActionType):
|
|||
:param row_id: The id of the row that must be deleted.
|
||||
:param model: If the correct model has already been generated, it can be
|
||||
provided so that it does not have to be generated for a second time.
|
||||
:param send_webhook_events: If set the false then the webhooks will not be
|
||||
triggered. Defaults to true.
|
||||
:raises RowDoesNotExist: When the row with the provided id does not exist.
|
||||
"""
|
||||
|
||||
|
@ -347,7 +358,9 @@ class DeleteRowActionType(UndoableActionType):
|
|||
"Can't delete rows because it has a data sync."
|
||||
)
|
||||
|
||||
RowHandler().delete_row_by_id(user, table, row_id, model=model)
|
||||
RowHandler().delete_row_by_id(
|
||||
user, table, row_id, model=model, send_webhook_events=send_webhook_events
|
||||
)
|
||||
|
||||
database = table.database
|
||||
params = cls.Params(table.id, table.name, database.id, database.name, row_id)
|
||||
|
@ -399,6 +412,7 @@ class DeleteRowsActionType(UndoableActionType):
|
|||
table: Table,
|
||||
row_ids: List[int],
|
||||
model: Optional[Type[GeneratedTableModel]] = None,
|
||||
send_webhook_events: bool = True,
|
||||
):
|
||||
"""
|
||||
Deletes rows of the given table with the given row_ids.
|
||||
|
@ -411,6 +425,8 @@ class DeleteRowsActionType(UndoableActionType):
|
|||
:param row_ids: The id of the row that must be deleted.
|
||||
:param model: If the correct model has already been generated, it can be
|
||||
provided so that it does not have to be generated for a second time.
|
||||
:param send_webhook_events: If set the false then the webhooks will not be
|
||||
triggered. Defaults to true.
|
||||
:raises RowDoesNotExist: When the row with the provided id does not exist.
|
||||
"""
|
||||
|
||||
|
@ -419,7 +435,9 @@ class DeleteRowsActionType(UndoableActionType):
|
|||
"Can't delete rows because it has a data sync."
|
||||
)
|
||||
|
||||
trashed_rows_entry = RowHandler().delete_rows(user, table, row_ids, model=model)
|
||||
trashed_rows_entry = RowHandler().delete_rows(
|
||||
user, table, row_ids, model=model, send_webhook_events=send_webhook_events
|
||||
)
|
||||
|
||||
workspace = table.database.workspace
|
||||
params = cls.Params(
|
||||
|
@ -548,6 +566,7 @@ class MoveRowActionType(UndoableActionType):
|
|||
row_id: int,
|
||||
before_row: Optional[GeneratedTableModel] = None,
|
||||
model: Optional[Type[GeneratedTableModel]] = None,
|
||||
send_webhook_events: bool = True,
|
||||
) -> GeneratedTableModelForUpdate:
|
||||
"""
|
||||
Moves the row before another row or to the end if no before row is provided.
|
||||
|
@ -566,6 +585,8 @@ class MoveRowActionType(UndoableActionType):
|
|||
instance. Otherwise the row will be moved to the end.
|
||||
:param model: If the correct model has already been generated, it can be
|
||||
provided so that it does not have to be generated for a second time.
|
||||
:param send_webhook_events: If set the false then the webhooks will not be
|
||||
triggered. Defaults to true.
|
||||
"""
|
||||
|
||||
if model is None:
|
||||
|
@ -577,7 +598,12 @@ class MoveRowActionType(UndoableActionType):
|
|||
original_row_order = row.order
|
||||
|
||||
updated_row = row_handler.move_row(
|
||||
user, table, row, before_row=before_row, model=model
|
||||
user,
|
||||
table,
|
||||
row,
|
||||
before_row=before_row,
|
||||
model=model,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
|
||||
rows_displacement = get_rows_displacement(
|
||||
|
@ -762,6 +788,7 @@ class UpdateRowsActionType(UndoableActionType):
|
|||
table: Table,
|
||||
rows_values: List[Dict[str, Any]],
|
||||
model: Optional[Type[GeneratedTableModel]] = None,
|
||||
send_webhook_events: bool = True,
|
||||
) -> List[GeneratedTableModelForUpdate]:
|
||||
"""
|
||||
Updates field values in batch based on provided rows with the new values.
|
||||
|
@ -776,6 +803,8 @@ class UpdateRowsActionType(UndoableActionType):
|
|||
field ids plus the id of the row.
|
||||
:param model: If the correct model has already been generated it can be
|
||||
provided so that it does not have to be generated for a second time.
|
||||
:param send_webhook_events: If set the false then the webhooks will not be
|
||||
triggered. Defaults to true.
|
||||
:return: The updated rows.
|
||||
"""
|
||||
|
||||
|
@ -786,6 +815,7 @@ class UpdateRowsActionType(UndoableActionType):
|
|||
table,
|
||||
rows_values,
|
||||
model=model,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
updated_rows = result.updated_rows
|
||||
|
||||
|
|
|
@ -667,6 +667,7 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
before_row: Optional[GeneratedTableModel] = None,
|
||||
user_field_names: bool = False,
|
||||
values_already_prepared: bool = False,
|
||||
send_webhook_events: bool = True,
|
||||
) -> GeneratedTableModel:
|
||||
"""
|
||||
Creates a new row for a given table with the provided values if the user
|
||||
|
@ -685,6 +686,8 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
:param values_already_prepared: Whether or not the values are already sanitized
|
||||
and validated for every field and can be used directly by the handler
|
||||
without any further check.
|
||||
:param send_webhook_events: If set the false then the webhooks will not be
|
||||
triggered. Defaults to true.
|
||||
:return: The created row instance.
|
||||
"""
|
||||
|
||||
|
@ -706,6 +709,7 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
before_row,
|
||||
user_field_names,
|
||||
values_already_prepared=values_already_prepared,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
|
||||
def force_create_row(
|
||||
|
@ -717,6 +721,7 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
before: Optional[GeneratedTableModel] = None,
|
||||
user_field_names: bool = False,
|
||||
values_already_prepared: bool = False,
|
||||
send_webhook_events: bool = True,
|
||||
):
|
||||
"""
|
||||
Creates a new row for a given table with the provided values.
|
||||
|
@ -735,6 +740,8 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
:param values_already_prepared: Whether or not the values are already sanitized
|
||||
and validated for every field and can be used directly by the handler
|
||||
without any further check.
|
||||
:param send_webhook_events: If set the false then the webhooks will not be
|
||||
triggered. Defaults to true.
|
||||
:return: The created row instance.
|
||||
:rtype: Model
|
||||
"""
|
||||
|
@ -806,7 +813,7 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
table=table,
|
||||
model=model,
|
||||
send_realtime_update=True,
|
||||
send_webhook_events=True,
|
||||
send_webhook_events=send_webhook_events,
|
||||
rows_values_refreshed_from_db=False,
|
||||
m2m_change_tracker=m2m_change_tracker,
|
||||
)
|
||||
|
@ -2028,6 +2035,7 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
row: GeneratedTableModelForUpdate,
|
||||
before_row: Optional[GeneratedTableModel] = None,
|
||||
model: Optional[Type[GeneratedTableModel]] = None,
|
||||
send_webhook_events: bool = True,
|
||||
) -> GeneratedTableModelForUpdate:
|
||||
"""
|
||||
Updates the row order value.
|
||||
|
@ -2039,6 +2047,8 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
instance. Otherwise the row will be moved to the end.
|
||||
:param model: If the correct model has already been generated, it can be
|
||||
provided so that it does not have to be generated for a second time.
|
||||
:param send_webhook_events: If set the false then the webhooks will not be
|
||||
triggered. Defaults to true.
|
||||
"""
|
||||
|
||||
workspace = table.database.workspace
|
||||
|
@ -2090,6 +2100,7 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
before_return=before_return,
|
||||
updated_field_ids=[],
|
||||
prepared_rows_values=None,
|
||||
send_webhook_events=send_webhook_events,
|
||||
)
|
||||
|
||||
return row
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from decimal import Decimal
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import connection
|
||||
|
@ -289,6 +290,57 @@ def test_batch_create_rows(api_client, data_fixture):
|
|||
assert row_2.needs_background_update
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
@pytest.mark.api_rows
|
||||
def test_batch_create_rows_with_disabled_webhook_events(api_client, data_fixture):
|
||||
user, jwt_token = data_fixture.create_user_and_token()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
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"
|
||||
)
|
||||
|
||||
data_fixture.create_table_webhook(
|
||||
table=table,
|
||||
user=user,
|
||||
request_method="POST",
|
||||
url="http://localhost",
|
||||
events=[],
|
||||
)
|
||||
|
||||
url = reverse("api:database:rows:batch", kwargs={"table_id": table.id})
|
||||
request_body = {
|
||||
"items": [
|
||||
{
|
||||
f"field_{text_field.id}": "green",
|
||||
f"field_{number_field.id}": 120,
|
||||
f"field_{boolean_field.id}": True,
|
||||
},
|
||||
{
|
||||
f"field_{text_field.id}": "yellow",
|
||||
f"field_{number_field.id}": 240,
|
||||
f"field_{boolean_field.id}": False,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
with patch("baserow.contrib.database.webhooks.registries.call_webhook.delay") as m:
|
||||
response = api_client.post(
|
||||
f"{url}?send_webhook_events=false",
|
||||
request_body,
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {jwt_token}",
|
||||
)
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
m.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.api_rows
|
||||
def test_batch_create_rows_id_field_ignored(api_client, data_fixture):
|
||||
|
@ -1184,6 +1236,62 @@ def test_batch_update_rows(api_client, data_fixture):
|
|||
assert row_2.needs_background_update
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
@pytest.mark.api_rows
|
||||
def test_batch_update_rows_with_disabled_webhook_events(api_client, data_fixture):
|
||||
user, jwt_token = data_fixture.create_user_and_token()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
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"
|
||||
)
|
||||
model = table.get_model()
|
||||
row_1 = model.objects.create()
|
||||
row_2 = model.objects.create()
|
||||
model.objects.update(needs_background_update=False)
|
||||
|
||||
data_fixture.create_table_webhook(
|
||||
table=table,
|
||||
user=user,
|
||||
request_method="POST",
|
||||
url="http://localhost",
|
||||
events=[],
|
||||
)
|
||||
|
||||
url = reverse("api:database:rows:batch", kwargs={"table_id": table.id})
|
||||
request_body = {
|
||||
"items": [
|
||||
{
|
||||
f"id": row_1.id,
|
||||
f"field_{text_field.id}": "green",
|
||||
f"field_{number_field.id}": 120,
|
||||
f"field_{boolean_field.id}": True,
|
||||
},
|
||||
{
|
||||
f"id": row_2.id,
|
||||
f"field_{text_field.id}": "yellow",
|
||||
f"field_{number_field.id}": 240,
|
||||
f"field_{boolean_field.id}": False,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
with patch("baserow.contrib.database.webhooks.registries.call_webhook.delay") as m:
|
||||
response = api_client.patch(
|
||||
f"{url}?send_webhook_events=false",
|
||||
request_body,
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {jwt_token}",
|
||||
)
|
||||
assert response.status_code == HTTP_200_OK
|
||||
m.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.api_rows
|
||||
def test_batch_update_rows_last_modified_field(api_client, data_fixture):
|
||||
|
@ -2275,3 +2383,34 @@ def test_batch_delete_rows_num_of_queries(api_client, data_fixture):
|
|||
assert len(delete_one_row_ctx.captured_queries) == len(
|
||||
delete_multiple_rows_ctx.captured_queries
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
@pytest.mark.api_rows
|
||||
def test_batch_delete_rows_disabled_webhook_events(api_client, data_fixture):
|
||||
user, jwt_token = data_fixture.create_user_and_token()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
model = table.get_model()
|
||||
row_1 = model.objects.create()
|
||||
row_2 = model.objects.create()
|
||||
model.objects.create()
|
||||
url = reverse("api:database:rows:batch-delete", kwargs={"table_id": table.id})
|
||||
request_body = {"items": [row_1.id, row_2.id]}
|
||||
|
||||
data_fixture.create_table_webhook(
|
||||
table=table,
|
||||
user=user,
|
||||
request_method="POST",
|
||||
url="http://localhost",
|
||||
events=[],
|
||||
)
|
||||
|
||||
with patch("baserow.contrib.database.webhooks.registries.call_webhook.delay") as m:
|
||||
response = api_client.post(
|
||||
f"{url}?send_webhook_events=false",
|
||||
request_body,
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {jwt_token}",
|
||||
)
|
||||
assert response.status_code == HTTP_204_NO_CONTENT
|
||||
m.assert_not_called()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import json
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from decimal import Decimal
|
||||
from unittest.mock import patch
|
||||
from urllib.parse import quote
|
||||
|
||||
from django.db import connection
|
||||
|
@ -1840,6 +1841,35 @@ def test_create_row(api_client, data_fixture):
|
|||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_create_row_with_disabled_webhook_events(api_client, data_fixture):
|
||||
user, token = data_fixture.create_user_and_token()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
text_field = data_fixture.create_text_field(
|
||||
table=table, order=0, name="Color", text_default="white"
|
||||
)
|
||||
|
||||
data_fixture.create_table_webhook(
|
||||
table=table,
|
||||
user=user,
|
||||
request_method="POST",
|
||||
url="http://localhost",
|
||||
events=[],
|
||||
)
|
||||
|
||||
url = reverse("api:database:rows:list", kwargs={"table_id": table.id})
|
||||
|
||||
with patch("baserow.contrib.database.webhooks.registries.call_webhook.delay") as m:
|
||||
response = api_client.post(
|
||||
f"{url}?send_webhook_events=false",
|
||||
{f"field_{text_field.id}": "Test 1"},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
assert response.status_code == HTTP_200_OK
|
||||
m.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_row_with_read_only_field(api_client, data_fixture):
|
||||
user, jwt_token = data_fixture.create_user_and_token()
|
||||
|
@ -2290,6 +2320,40 @@ def test_update_row(api_client, data_fixture):
|
|||
assert getattr(row_2, f"field_{boolean_field.id}") is False
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_update_row_with_disabled_webhook_events(api_client, data_fixture):
|
||||
user, token = data_fixture.create_user_and_token()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
text_field = data_fixture.create_text_field(
|
||||
table=table, order=0, name="Color", text_default="white"
|
||||
)
|
||||
|
||||
model = table.get_model()
|
||||
row_1 = model.objects.create()
|
||||
|
||||
data_fixture.create_table_webhook(
|
||||
table=table,
|
||||
user=user,
|
||||
request_method="POST",
|
||||
url="http://localhost",
|
||||
events=[],
|
||||
)
|
||||
|
||||
url = reverse(
|
||||
"api:database:rows:item", kwargs={"table_id": table.id, "row_id": row_1.id}
|
||||
)
|
||||
|
||||
with patch("baserow.contrib.database.webhooks.registries.call_webhook.delay") as m:
|
||||
response = api_client.patch(
|
||||
f"{url}?send_webhook_events=false",
|
||||
{f"field_{text_field.id}": "Test 1"},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
assert response.status_code == HTTP_200_OK
|
||||
m.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_row_with_read_only_field(api_client, data_fixture):
|
||||
user, jwt_token = data_fixture.create_user_and_token()
|
||||
|
@ -2472,6 +2536,40 @@ def test_move_row(api_client, data_fixture):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_move_row_with_disabled_webhook_events(api_client, data_fixture):
|
||||
user, token = data_fixture.create_user_and_token()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
data_fixture.create_text_field(
|
||||
table=table, order=0, name="Color", text_default="white"
|
||||
)
|
||||
|
||||
model = table.get_model()
|
||||
row_1 = model.objects.create()
|
||||
row_2 = model.objects.create()
|
||||
|
||||
data_fixture.create_table_webhook(
|
||||
table=table,
|
||||
user=user,
|
||||
request_method="POST",
|
||||
url="http://localhost",
|
||||
events=[],
|
||||
)
|
||||
|
||||
url = reverse(
|
||||
"api:database:rows:move", kwargs={"table_id": table.id, "row_id": row_2.id}
|
||||
)
|
||||
|
||||
with patch("baserow.contrib.database.webhooks.registries.call_webhook.delay") as m:
|
||||
response = api_client.patch(
|
||||
f"{url}?before_id={row_1.id}&send_webhook_events=false",
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
assert response.status_code == HTTP_200_OK
|
||||
m.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_cannot_delete_row_by_id_with_data_sync(api_client, data_fixture):
|
||||
user, jwt_token = data_fixture.create_user_and_token()
|
||||
|
@ -2566,6 +2664,39 @@ def test_delete_row_by_id(api_client, data_fixture):
|
|||
assert model.objects.count() == 0
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_delete_row_by_id_with_disabled_webhook_events(api_client, data_fixture):
|
||||
user, token = data_fixture.create_user_and_token()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
data_fixture.create_text_field(
|
||||
table=table, order=0, name="Color", text_default="white"
|
||||
)
|
||||
|
||||
model = table.get_model()
|
||||
row_1 = model.objects.create()
|
||||
|
||||
data_fixture.create_table_webhook(
|
||||
table=table,
|
||||
user=user,
|
||||
request_method="POST",
|
||||
url="http://localhost",
|
||||
events=[],
|
||||
)
|
||||
|
||||
url = reverse(
|
||||
"api:database:rows:item", kwargs={"table_id": table.id, "row_id": row_1.id}
|
||||
)
|
||||
|
||||
with patch("baserow.contrib.database.webhooks.registries.call_webhook.delay") as m:
|
||||
response = api_client.delete(
|
||||
f"{url}?send_webhook_events=false",
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
assert response.status_code == HTTP_204_NO_CONTENT
|
||||
m.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_list_rows_with_attribute_names(api_client, data_fixture):
|
||||
user, jwt_token = data_fixture.create_user_and_token(
|
||||
|
|
|
@ -229,6 +229,7 @@ def test_create_form_view_with_webhooks(api_client, data_fixture):
|
|||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
assert m.called
|
||||
print("CALL ARGS", m.call_args)
|
||||
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_200_OK
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"message": "Prevent triggering webhook if query param is provided on row create update or delete endpoints",
|
||||
"issue_number": 3085,
|
||||
"bullet_points": [],
|
||||
"created_at": "2024-11-19"
|
||||
}
|
|
@ -432,6 +432,7 @@
|
|||
"pathParameters": "Path parameters",
|
||||
"requestBodySchema": "Request body schema",
|
||||
"userFieldNamesDescription": "When the `user_field_names` GET parameter is provided and its value is one of the following: `y`, `yes`, `true`, `t`, `on`, `1`, or empty string, the field names returned by this endpoint will be the actual names of the fields.\n\nIf the `user_field_names` GET parameter is not provided, or if it does not match any of the above values, then all returned field names will be `field_` followed by the id of the field. For example `field_1` refers to the field with an id of `1`.",
|
||||
"sendWebhookEventsDescription": "A flag query parameter that triggers webhooks after the operation, if set to `y`, `yes`, `true`, `t`, `on`, `1`, `or` left empty. Defaults to `true`",
|
||||
"singleRow": "Single",
|
||||
"batchRows": "Batch",
|
||||
"fileUploads": "File uploads"
|
||||
|
|
|
@ -39,6 +39,16 @@
|
|||
<APIDocsParameter :optional="true" name="before" type="integer">
|
||||
{{ $t('apiDocsTableCreateRow.before') }}
|
||||
</APIDocsParameter>
|
||||
<APIDocsParameter
|
||||
name="send_webhook_events"
|
||||
:optional="true"
|
||||
type="any"
|
||||
>
|
||||
<MarkdownIt
|
||||
class="api-docs__content"
|
||||
:content="$t('apiDocs.sendWebhookEventsDescription')"
|
||||
/>
|
||||
</APIDocsParameter>
|
||||
</ul>
|
||||
<h4 class="api-docs__heading-4">
|
||||
{{ $t('apiDocs.requestBodySchema') }}
|
||||
|
@ -72,6 +82,16 @@
|
|||
<APIDocsParameter :optional="true" name="before" type="integer">
|
||||
{{ $t('apiDocsTableCreateRows.before') }}
|
||||
</APIDocsParameter>
|
||||
<APIDocsParameter
|
||||
name="send_webhook_events"
|
||||
:optional="true"
|
||||
type="any"
|
||||
>
|
||||
<MarkdownIt
|
||||
class="api-docs__content"
|
||||
:content="$t('apiDocs.sendWebhookEventsDescription')"
|
||||
/>
|
||||
</APIDocsParameter>
|
||||
</ul>
|
||||
<h4 class="api-docs__heading-4">
|
||||
{{ $t('apiDocs.requestBodySchema') }}
|
||||
|
|
|
@ -33,7 +33,20 @@
|
|||
</APIDocsParameter>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else>
|
||||
<h4 class="api-docs__heading-4">{{ $t('apiDocs.queryParameters') }}</h4>
|
||||
<ul class="api-docs__parameters">
|
||||
<APIDocsParameter
|
||||
name="send_webhook_events"
|
||||
:optional="true"
|
||||
type="any"
|
||||
>
|
||||
<MarkdownIt
|
||||
class="api-docs__content"
|
||||
:content="$t('apiDocs.sendWebhookEventsDescription')"
|
||||
/>
|
||||
</APIDocsParameter>
|
||||
</ul>
|
||||
<div v-if="batchMode === true">
|
||||
<h4 class="api-docs__heading-4">
|
||||
{{ $t('apiDocs.requestBodySchema') }}
|
||||
</h4>
|
||||
|
|
|
@ -28,6 +28,16 @@
|
|||
<APIDocsParameter name="before_id" type="integer" :optional="true">
|
||||
{{ $t('apiDocsTableMoveRow.before') }}
|
||||
</APIDocsParameter>
|
||||
<APIDocsParameter
|
||||
name="send_webhook_events"
|
||||
:optional="true"
|
||||
type="any"
|
||||
>
|
||||
<MarkdownIt
|
||||
class="api-docs__content"
|
||||
:content="$t('apiDocs.sendWebhookEventsDescription')"
|
||||
/>
|
||||
</APIDocsParameter>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="api-docs__right">
|
||||
|
|
|
@ -40,6 +40,16 @@
|
|||
:content="$t('apiDocs.userFieldNamesDescription')"
|
||||
/>
|
||||
</APIDocsParameter>
|
||||
<APIDocsParameter
|
||||
name="send_webhook_events"
|
||||
:optional="true"
|
||||
type="any"
|
||||
>
|
||||
<MarkdownIt
|
||||
class="api-docs__content"
|
||||
:content="$t('apiDocs.sendWebhookEventsDescription')"
|
||||
/>
|
||||
</APIDocsParameter>
|
||||
</ul>
|
||||
<h4 class="api-docs__heading-4">
|
||||
{{ $t('apiDocs.requestBodySchema') }}
|
||||
|
@ -68,6 +78,16 @@
|
|||
:content="$t('apiDocs.userFieldNamesDescription')"
|
||||
/>
|
||||
</APIDocsParameter>
|
||||
<APIDocsParameter
|
||||
name="send_webhook_events"
|
||||
:optional="true"
|
||||
type="any"
|
||||
>
|
||||
<MarkdownIt
|
||||
class="api-docs__content"
|
||||
:content="$t('apiDocs.sendWebhookEventsDescription')"
|
||||
/>
|
||||
</APIDocsParameter>
|
||||
</ul>
|
||||
<h4 class="api-docs__heading-4">
|
||||
{{ $t('apiDocs.requestBodySchema') }}
|
||||
|
|
Loading…
Add table
Reference in a new issue