mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-04 21:25:24 +00:00
Batch webhooks
This commit is contained in:
parent
036dfb8683
commit
f75ed23f4d
36 changed files with 897 additions and 987 deletions
backend
src/baserow
tests/baserow/contrib/database
docs/apis
web-frontend
locales
modules
core/assets/scss/components
database
|
@ -387,7 +387,7 @@ SPECTACULAR_SETTINGS = {
|
|||
"multiple_select_has",
|
||||
"multiple_select_has_not",
|
||||
],
|
||||
"EventTypesEnum": ["row.created", "row.updated", "row.deleted"],
|
||||
"EventTypesEnum": ["rows.created", "rows.updated", "rows.deleted"],
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import logging
|
||||
from copy import deepcopy
|
||||
from typing import Dict
|
||||
from typing import Dict, List
|
||||
|
||||
from django.conf import settings
|
||||
from rest_framework import serializers
|
||||
|
@ -291,7 +291,24 @@ def get_example_row_metadata_field_serializer():
|
|||
)
|
||||
|
||||
|
||||
def remap_serialized_row_to_user_field_names(serialized_row: Dict, model: ModelBase):
|
||||
def remap_serialized_rows_to_user_field_names(
|
||||
serialized_rows: List[Dict], model: ModelBase
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
Remap the values of rows from field ids to the user defined field names.
|
||||
|
||||
:param serialized_rows: The rows whose fields to remap.
|
||||
:param model: The model for which to generate a serializer.
|
||||
"""
|
||||
|
||||
return [
|
||||
remap_serialized_row_to_user_field_names(row, model) for row in serialized_rows
|
||||
]
|
||||
|
||||
|
||||
def remap_serialized_row_to_user_field_names(
|
||||
serialized_row: Dict, model: ModelBase
|
||||
) -> Dict:
|
||||
"""
|
||||
Remap the values of a row from field ids to the user defined field names.
|
||||
|
||||
|
|
|
@ -53,7 +53,13 @@ class TableWebhookCreateRequestSerializer(serializers.ModelSerializer):
|
|||
class TableWebhookUpdateRequestSerializer(serializers.ModelSerializer):
|
||||
events = serializers.ListField(
|
||||
required=False,
|
||||
child=serializers.ChoiceField(choices=webhook_event_type_registry.get_types()),
|
||||
child=serializers.ChoiceField(
|
||||
choices=[
|
||||
t
|
||||
for t in webhook_event_type_registry.get_types()
|
||||
if t not in ["row.created", "row.updated", "row.deleted"]
|
||||
]
|
||||
),
|
||||
help_text="A list containing the events that will trigger this webhook.",
|
||||
)
|
||||
headers = serializers.DictField(
|
||||
|
|
|
@ -348,13 +348,19 @@ class DatabaseConfig(AppConfig):
|
|||
register_formula_functions(formula_function_registry)
|
||||
|
||||
from .rows.webhook_event_types import (
|
||||
RowsCreatedEventType,
|
||||
RowCreatedEventType,
|
||||
RowsUpdatedEventType,
|
||||
RowUpdatedEventType,
|
||||
RowsDeletedEventType,
|
||||
RowDeletedEventType,
|
||||
)
|
||||
|
||||
webhook_event_type_registry.register(RowsCreatedEventType())
|
||||
webhook_event_type_registry.register(RowCreatedEventType())
|
||||
webhook_event_type_registry.register(RowsUpdatedEventType())
|
||||
webhook_event_type_registry.register(RowUpdatedEventType())
|
||||
webhook_event_type_registry.register(RowsDeletedEventType())
|
||||
webhook_event_type_registry.register(RowDeletedEventType())
|
||||
|
||||
from .airtable.airtable_column_types import (
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# Generated by Django 3.2.13 on 2022-07-04 15:16
|
||||
|
||||
from django.db import migrations, transaction
|
||||
from baserow.contrib.database.models import TableWebhook, TableWebhookEvent
|
||||
|
||||
|
||||
def forward(apps, schema_editor):
|
||||
"""
|
||||
This migration will create individual TableWebhookEvent entries
|
||||
for all deprecated single row webhooks that has include_all_events
|
||||
set to True.
|
||||
"""
|
||||
|
||||
with transaction.atomic():
|
||||
webhooks = TableWebhook.objects.filter(include_all_events=True)
|
||||
create_webhooks = []
|
||||
for webhook in webhooks:
|
||||
create_webhooks.append(
|
||||
TableWebhookEvent(webhook=webhook, event_type="row.created")
|
||||
)
|
||||
create_webhooks.append(
|
||||
TableWebhookEvent(webhook=webhook, event_type="row.updated")
|
||||
)
|
||||
create_webhooks.append(
|
||||
TableWebhookEvent(webhook=webhook, event_type="row.deleted")
|
||||
)
|
||||
|
||||
TableWebhookEvent.objects.bulk_create(create_webhooks, batch_size=100)
|
||||
TableWebhook.objects.filter(include_all_events=True).update(
|
||||
include_all_events=False
|
||||
)
|
||||
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
...
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("database", "0080_auto_20220702_1612"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forward, reverse),
|
||||
]
|
|
@ -41,7 +41,7 @@ class CreateRowActionType(ActionType):
|
|||
) -> GeneratedTableModel:
|
||||
"""
|
||||
Creates a new row for a given table with the provided values if the user
|
||||
belongs to the related group. It also calls the row_created signal.
|
||||
belongs to the related group. It also calls the rows_created signal.
|
||||
See the baserow.contrib.database.rows.handler.RowHandler.create_row
|
||||
for more information.
|
||||
Undoing this action trashes the row and redoing restores it.
|
||||
|
|
|
@ -22,15 +22,10 @@ from baserow.core.trash.handler import TrashHandler
|
|||
from baserow.core.utils import get_non_unique_values
|
||||
from .exceptions import RowDoesNotExist, RowIdsNotUnique
|
||||
from .signals import (
|
||||
before_row_update,
|
||||
before_row_delete,
|
||||
before_rows_update,
|
||||
before_rows_delete,
|
||||
row_created,
|
||||
rows_created,
|
||||
row_updated,
|
||||
rows_updated,
|
||||
row_deleted,
|
||||
rows_deleted,
|
||||
)
|
||||
|
||||
|
@ -404,7 +399,7 @@ class RowHandler:
|
|||
) -> GeneratedTableModel:
|
||||
"""
|
||||
Creates a new row for a given table with the provided values if the user
|
||||
belongs to the related group. It also calls the row_created signal.
|
||||
belongs to the related group. It also calls the rows_created signal.
|
||||
|
||||
:param user: The user of whose behalf the row is created.
|
||||
:param table: The table for which to create a row for.
|
||||
|
@ -429,8 +424,13 @@ class RowHandler:
|
|||
table, values, model, before_row, user_field_names
|
||||
)
|
||||
|
||||
row_created.send(
|
||||
self, row=instance, before=before_row, user=user, table=table, model=model
|
||||
rows_created.send(
|
||||
self,
|
||||
rows=[instance],
|
||||
before=before_row,
|
||||
user=user,
|
||||
table=table,
|
||||
model=model,
|
||||
)
|
||||
|
||||
return instance
|
||||
|
@ -617,14 +617,15 @@ class RowHandler:
|
|||
updated_fields_by_name[field["name"]] = field["field"]
|
||||
updated_fields.append(field["field"])
|
||||
|
||||
before_return = before_row_update.send(
|
||||
before_return = before_rows_update.send(
|
||||
self,
|
||||
row=row,
|
||||
rows=[row],
|
||||
user=user,
|
||||
table=table,
|
||||
model=model,
|
||||
updated_field_ids=updated_field_ids,
|
||||
)
|
||||
|
||||
values = self.prepare_values(model._field_objects, values)
|
||||
values, manytomany_values = self.extract_manytomany_values(values, model)
|
||||
|
||||
|
@ -683,9 +684,9 @@ class RowHandler:
|
|||
|
||||
ViewHandler().field_value_updated(updated_fields)
|
||||
|
||||
row_updated.send(
|
||||
rows_updated.send(
|
||||
self,
|
||||
row=row,
|
||||
rows=[row],
|
||||
user=user,
|
||||
table=table,
|
||||
model=model,
|
||||
|
@ -1165,8 +1166,8 @@ class RowHandler:
|
|||
if model is None:
|
||||
model = table.get_model()
|
||||
|
||||
before_return = before_row_update.send(
|
||||
self, row=row, user=user, table=table, model=model, updated_field_ids=[]
|
||||
before_return = before_rows_update.send(
|
||||
self, rows=[row], user=user, table=table, model=model, updated_field_ids=[]
|
||||
)
|
||||
|
||||
row.order = self.get_order_before_row(before_row, model)[0]
|
||||
|
@ -1204,9 +1205,9 @@ class RowHandler:
|
|||
|
||||
ViewHandler().field_value_updated(updated_fields)
|
||||
|
||||
row_updated.send(
|
||||
rows_updated.send(
|
||||
self,
|
||||
row=row,
|
||||
rows=[row],
|
||||
user=user,
|
||||
table=table,
|
||||
model=model,
|
||||
|
@ -1264,12 +1265,10 @@ class RowHandler:
|
|||
if model is None:
|
||||
model = table.get_model()
|
||||
|
||||
before_return = before_row_delete.send(
|
||||
self, row=row, user=user, table=table, model=model
|
||||
before_return = before_rows_delete.send(
|
||||
self, rows=[row], user=user, table=table, model=model
|
||||
)
|
||||
|
||||
row_id = row.id
|
||||
|
||||
TrashHandler.trash(user, group, table.database, row, parent_id=table.id)
|
||||
|
||||
update_collector = FieldUpdateCollector(table, starting_row_ids=[row.id])
|
||||
|
@ -1305,10 +1304,9 @@ class RowHandler:
|
|||
|
||||
ViewHandler().field_value_updated(updated_fields)
|
||||
|
||||
row_deleted.send(
|
||||
rows_deleted.send(
|
||||
self,
|
||||
row_id=row_id,
|
||||
row=row,
|
||||
rows=[row],
|
||||
user=user,
|
||||
table=table,
|
||||
model=model,
|
||||
|
|
|
@ -3,14 +3,9 @@ from django.dispatch import Signal
|
|||
|
||||
# Note that it could happen that this signal is triggered, but the actual update still
|
||||
# fails because of a validation error.
|
||||
before_row_update = Signal()
|
||||
before_row_delete = Signal()
|
||||
before_rows_update = Signal()
|
||||
before_rows_delete = Signal()
|
||||
|
||||
row_created = Signal()
|
||||
rows_created = Signal()
|
||||
row_updated = Signal()
|
||||
rows_updated = Signal()
|
||||
row_deleted = Signal()
|
||||
rows_deleted = Signal()
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
from baserow.contrib.database.api.rows.serializers import (
|
||||
get_row_serializer_class,
|
||||
remap_serialized_row_to_user_field_names,
|
||||
remap_serialized_rows_to_user_field_names,
|
||||
RowSerializer,
|
||||
)
|
||||
from baserow.contrib.database.webhooks.registries import WebhookEventType
|
||||
from baserow.contrib.database.ws.rows.signals import before_row_update
|
||||
from .signals import row_created, row_updated, row_deleted
|
||||
from baserow.contrib.database.ws.rows.signals import before_rows_update
|
||||
from .signals import rows_created, rows_updated, rows_deleted
|
||||
|
||||
|
||||
class RowEventType(WebhookEventType):
|
||||
class RowsEventType(WebhookEventType):
|
||||
def get_row_serializer(self, webhook, model):
|
||||
return get_row_serializer_class(
|
||||
model,
|
||||
|
@ -17,26 +17,67 @@ class RowEventType(WebhookEventType):
|
|||
user_field_names=webhook.use_user_field_names,
|
||||
)
|
||||
|
||||
def get_payload(self, event_id, webhook, model, table, row, **kwargs):
|
||||
def get_payload(self, event_id, webhook, model, table, rows, **kwargs):
|
||||
payload = super().get_payload(event_id, webhook, **kwargs)
|
||||
payload["row_id"] = row.id
|
||||
payload["values"] = self.get_row_serializer(webhook, model)(row).data
|
||||
payload["items"] = self.get_row_serializer(webhook, model)(rows, many=True).data
|
||||
return payload
|
||||
|
||||
|
||||
class RowCreatedEventType(RowEventType):
|
||||
class RowsCreatedEventType(RowsEventType):
|
||||
type = "rows.created"
|
||||
signal = rows_created
|
||||
|
||||
def get_test_call_payload(self, table, model, event_id, webhook):
|
||||
rows = [model(id=0, order=0)]
|
||||
payload = self.get_payload(
|
||||
event_id=event_id,
|
||||
webhook=webhook,
|
||||
model=model,
|
||||
table=table,
|
||||
rows=rows,
|
||||
)
|
||||
return payload
|
||||
|
||||
|
||||
class RowCreatedEventType(RowsCreatedEventType):
|
||||
"""
|
||||
Handling of deprecated single row.created webhook
|
||||
"""
|
||||
|
||||
type = "row.created"
|
||||
signal = row_created
|
||||
signal = rows_created
|
||||
|
||||
def get_payload(self, *args, **kwargs):
|
||||
payload = super().get_payload(*args, **kwargs)
|
||||
payload["row_id"] = payload["items"][0]["id"]
|
||||
payload["values"] = payload["items"][0]
|
||||
del payload["items"]
|
||||
return payload
|
||||
|
||||
|
||||
class RowUpdatedEventType(RowEventType):
|
||||
type = "row.updated"
|
||||
signal = row_updated
|
||||
class RowsUpdatedEventType(RowsEventType):
|
||||
type = "rows.updated"
|
||||
signal = rows_updated
|
||||
|
||||
def get_test_call_before_return(self, table, row, model):
|
||||
return {
|
||||
before_row_update: before_row_update(
|
||||
row=row,
|
||||
def get_payload(
|
||||
self, event_id, webhook, model, table, rows, before_return, **kwargs
|
||||
):
|
||||
payload = super().get_payload(event_id, webhook, model, table, rows, **kwargs)
|
||||
|
||||
old_items = dict(before_return)[before_rows_update]
|
||||
|
||||
if webhook.use_user_field_names:
|
||||
old_items = remap_serialized_rows_to_user_field_names(old_items, model)
|
||||
|
||||
payload["old_items"] = old_items
|
||||
|
||||
return payload
|
||||
|
||||
def get_test_call_payload(self, table, model, event_id, webhook):
|
||||
rows = [model(id=0, order=0)]
|
||||
before_return = {
|
||||
before_rows_update: before_rows_update(
|
||||
rows=rows,
|
||||
model=model,
|
||||
sender=None,
|
||||
user=None,
|
||||
|
@ -44,26 +85,71 @@ class RowUpdatedEventType(RowEventType):
|
|||
updated_field_ids=None,
|
||||
)
|
||||
}
|
||||
payload = self.get_payload(
|
||||
event_id=event_id,
|
||||
webhook=webhook,
|
||||
model=model,
|
||||
table=table,
|
||||
rows=rows,
|
||||
before_return=before_return,
|
||||
)
|
||||
return payload
|
||||
|
||||
|
||||
class RowUpdatedEventType(RowsUpdatedEventType):
|
||||
"""
|
||||
Handling of deprecated single row.updated webhook
|
||||
"""
|
||||
|
||||
type = "row.updated"
|
||||
signal = rows_updated
|
||||
|
||||
def get_payload(
|
||||
self, event_id, webhook, model, table, row, before_return, **kwargs
|
||||
self, event_id, webhook, model, table, rows, before_return, **kwargs
|
||||
):
|
||||
payload = super().get_payload(event_id, webhook, model, table, row, **kwargs)
|
||||
old_values = dict(before_return)[before_row_update]
|
||||
|
||||
if webhook.use_user_field_names:
|
||||
old_values = remap_serialized_row_to_user_field_names(old_values, model)
|
||||
|
||||
payload["old_values"] = old_values
|
||||
|
||||
payload = super().get_payload(
|
||||
event_id, webhook, model, table, rows, before_return, **kwargs
|
||||
)
|
||||
payload["row_id"] = payload["items"][0]["id"]
|
||||
payload["values"] = payload["items"][0]
|
||||
payload["old_values"] = payload["old_items"][0]
|
||||
del payload["items"]
|
||||
del payload["old_items"]
|
||||
return payload
|
||||
|
||||
|
||||
class RowDeletedEventType(WebhookEventType):
|
||||
type = "row.deleted"
|
||||
signal = row_deleted
|
||||
class RowsDeletedEventType(WebhookEventType):
|
||||
type = "rows.deleted"
|
||||
signal = rows_deleted
|
||||
|
||||
def get_payload(self, event_id, webhook, row, **kwargs):
|
||||
def get_payload(self, event_id, webhook, rows, **kwargs):
|
||||
payload = super().get_payload(event_id, webhook, **kwargs)
|
||||
payload["row_id"] = row.id
|
||||
payload["row_ids"] = [row.id for row in rows]
|
||||
return payload
|
||||
|
||||
def get_test_call_payload(self, table, model, event_id, webhook):
|
||||
rows = [model(id=0, order=0)]
|
||||
|
||||
payload = self.get_payload(
|
||||
event_id=event_id,
|
||||
webhook=webhook,
|
||||
model=model,
|
||||
table=table,
|
||||
rows=rows,
|
||||
)
|
||||
return payload
|
||||
|
||||
|
||||
class RowDeletedEventType(RowsDeletedEventType):
|
||||
"""
|
||||
Handling of deprecated single row.deleted webhook
|
||||
"""
|
||||
|
||||
type = "row.deleted"
|
||||
signal = rows_deleted
|
||||
|
||||
def get_payload(self, event_id, webhook, rows, **kwargs):
|
||||
payload = super().get_payload(event_id, webhook, rows, **kwargs)
|
||||
payload["row_id"] = rows[0].id
|
||||
del payload["row_ids"]
|
||||
return payload
|
||||
|
|
|
@ -10,7 +10,7 @@ from baserow.contrib.database.fields.dependencies.update_collector import (
|
|||
from baserow.contrib.database.fields.handler import FieldHandler
|
||||
from baserow.contrib.database.fields.models import Field
|
||||
from baserow.contrib.database.fields.registries import field_type_registry
|
||||
from baserow.contrib.database.rows.signals import row_created, rows_created
|
||||
from baserow.contrib.database.rows.signals import rows_created
|
||||
from baserow.contrib.database.table.models import Table, GeneratedTableModel
|
||||
from baserow.contrib.database.table.signals import table_created
|
||||
from baserow.contrib.database.views.handler import ViewHandler
|
||||
|
@ -258,9 +258,9 @@ class RowTrashableItemType(TrashableItemType):
|
|||
|
||||
ViewHandler().field_value_updated(updated_fields)
|
||||
|
||||
row_created.send(
|
||||
rows_created.send(
|
||||
self,
|
||||
row=trashed_item,
|
||||
rows=[trashed_item],
|
||||
table=table,
|
||||
model=model,
|
||||
before=None,
|
||||
|
|
|
@ -35,7 +35,7 @@ from baserow.contrib.database.fields.field_sortings import AnnotatedOrder
|
|||
from baserow.contrib.database.fields.models import Field
|
||||
from baserow.contrib.database.fields.registries import field_type_registry
|
||||
from baserow.contrib.database.rows.handler import RowHandler
|
||||
from baserow.contrib.database.rows.signals import row_created
|
||||
from baserow.contrib.database.rows.signals import rows_created
|
||||
from baserow.contrib.database.table.models import Table, GeneratedTableModel
|
||||
from baserow.core.trash.handler import TrashHandler
|
||||
from baserow.core.utils import (
|
||||
|
@ -1712,8 +1712,8 @@ class ViewHandler:
|
|||
allowed_values = extract_allowed(values, allowed_field_names)
|
||||
instance = RowHandler().force_create_row(table, allowed_values, model)
|
||||
|
||||
row_created.send(
|
||||
self, row=instance, before=None, user=None, table=table, model=model
|
||||
rows_created.send(
|
||||
self, rows=[instance], before=None, user=None, table=table, model=model
|
||||
)
|
||||
|
||||
return instance
|
||||
|
|
|
@ -365,19 +365,10 @@ class WebhookHandler:
|
|||
|
||||
event_id = str(uuid.uuid4())
|
||||
model = table.get_model()
|
||||
row = model(id=0, order=0)
|
||||
|
||||
event = webhook_event_type_registry.get(event_type)
|
||||
before_return = event.get_test_call_before_return(
|
||||
table=table, row=row, model=model
|
||||
)
|
||||
payload = event.get_payload(
|
||||
event_id=event_id,
|
||||
webhook=webhook,
|
||||
model=model,
|
||||
table=table,
|
||||
row=row,
|
||||
before_return=before_return,
|
||||
)
|
||||
|
||||
payload = event.get_test_call_payload(table, model, event_id, webhook)
|
||||
headers.update(self.get_headers(event_type, event_id))
|
||||
|
||||
return self.make_request(webhook.request_method, webhook.url, headers, payload)
|
||||
|
|
|
@ -39,8 +39,27 @@ class WebhookEventType(Instance):
|
|||
super().__init__()
|
||||
self.signal.connect(self.listener)
|
||||
|
||||
def get_test_call_before_return(self, **kwargs):
|
||||
"""Prepare a `before_return` value for a webhook event."""
|
||||
def get_test_call_payload(self, table, model, event_id, webhook):
|
||||
"""
|
||||
Constructs a test payload for a webhook call.
|
||||
|
||||
:param table: The table with changes.
|
||||
:param model: The table's model.
|
||||
:param event_id: The id of the event.
|
||||
:param webhook: The webhook object related to the call.
|
||||
:return: A JSON serializable dict with the test payload.
|
||||
"""
|
||||
|
||||
row = model(id=0, order=0)
|
||||
payload = self.get_payload(
|
||||
event_id=event_id,
|
||||
webhook=webhook,
|
||||
model=model,
|
||||
table=table,
|
||||
row=row,
|
||||
before_return=None,
|
||||
)
|
||||
return payload
|
||||
|
||||
def get_payload(self, event_id, webhook, **kwargs):
|
||||
"""
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Optional, Any, Dict, Iterable, List
|
||||
from typing import Optional, Any, Dict, List
|
||||
|
||||
from django.db import transaction
|
||||
from django.dispatch import receiver
|
||||
|
@ -11,10 +11,8 @@ from baserow.contrib.database.api.rows.serializers import (
|
|||
from baserow.contrib.database.rows import signals as row_signals
|
||||
from baserow.contrib.database.table.models import GeneratedTableModel
|
||||
from baserow.contrib.database.views.handler import PublicViewRows, ViewHandler
|
||||
from baserow.contrib.database.views.models import View
|
||||
from baserow.contrib.database.views.registries import view_type_registry
|
||||
from baserow.contrib.database.ws.rows.signals import (
|
||||
before_row_update,
|
||||
before_rows_update,
|
||||
RealtimeRowMessages,
|
||||
)
|
||||
|
@ -27,32 +25,6 @@ def _serialize_row(model, row, many=False):
|
|||
).data
|
||||
|
||||
|
||||
def _send_row_created_event_to_views(
|
||||
serialized_row: Dict[Any, Any],
|
||||
before: Optional[GeneratedTableModel],
|
||||
public_views: Iterable[View],
|
||||
):
|
||||
view_page_type = page_registry.get("view")
|
||||
handler = ViewHandler()
|
||||
for public_view in public_views:
|
||||
view_type = view_type_registry.get_by_model(public_view.specific_class)
|
||||
if not view_type.when_shared_publicly_requires_realtime_events:
|
||||
continue
|
||||
|
||||
restricted_serialized_row = handler.restrict_row_for_view(
|
||||
public_view, serialized_row
|
||||
)
|
||||
view_page_type.broadcast(
|
||||
RealtimeRowMessages.row_created(
|
||||
table_id=PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
serialized_row=restricted_serialized_row,
|
||||
metadata={},
|
||||
before=before,
|
||||
),
|
||||
slug=public_view.slug,
|
||||
)
|
||||
|
||||
|
||||
def _send_rows_created_event_to_views(
|
||||
serialized_rows: List[Dict[Any, Any]],
|
||||
before: Optional[GeneratedTableModel],
|
||||
|
@ -80,28 +52,6 @@ def _send_rows_created_event_to_views(
|
|||
)
|
||||
|
||||
|
||||
def _send_row_deleted_event_to_views(
|
||||
serialized_deleted_row: Dict[Any, Any], public_views: Iterable[View]
|
||||
):
|
||||
view_page_type = page_registry.get("view")
|
||||
handler = ViewHandler()
|
||||
for public_view in public_views:
|
||||
view_type = view_type_registry.get_by_model(public_view.specific_class)
|
||||
if not view_type.when_shared_publicly_requires_realtime_events:
|
||||
continue
|
||||
|
||||
restricted_serialized_deleted_row = handler.restrict_row_for_view(
|
||||
public_view, serialized_deleted_row
|
||||
)
|
||||
view_page_type.broadcast(
|
||||
RealtimeRowMessages.row_deleted(
|
||||
table_id=PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
serialized_row=restricted_serialized_deleted_row,
|
||||
),
|
||||
slug=public_view.slug,
|
||||
)
|
||||
|
||||
|
||||
def _send_rows_deleted_event_to_views(
|
||||
serialized_deleted_rows: List[Dict[Any, Any]],
|
||||
public_views: List[PublicViewRows],
|
||||
|
@ -125,20 +75,6 @@ def _send_rows_deleted_event_to_views(
|
|||
)
|
||||
|
||||
|
||||
@receiver(row_signals.row_created)
|
||||
def public_row_created(sender, row, before, user, table, model, **kwargs):
|
||||
row_checker = ViewHandler().get_public_views_row_checker(
|
||||
table, model, only_include_views_which_want_realtime_events=True
|
||||
)
|
||||
transaction.on_commit(
|
||||
lambda: _send_row_created_event_to_views(
|
||||
_serialize_row(model, row),
|
||||
before,
|
||||
row_checker.get_public_views_where_row_is_visible(row),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@receiver(row_signals.rows_created)
|
||||
def public_rows_created(sender, rows, before, user, table, model, **kwargs):
|
||||
row_checker = ViewHandler().get_public_views_row_checker(
|
||||
|
@ -153,19 +89,6 @@ def public_rows_created(sender, rows, before, user, table, model, **kwargs):
|
|||
)
|
||||
|
||||
|
||||
@receiver(row_signals.before_row_delete)
|
||||
def public_before_row_delete(sender, row, user, table, model, **kwargs):
|
||||
row_checker = ViewHandler().get_public_views_row_checker(
|
||||
table, model, only_include_views_which_want_realtime_events=True
|
||||
)
|
||||
return {
|
||||
"deleted_row_public_views": (
|
||||
row_checker.get_public_views_where_row_is_visible(row)
|
||||
),
|
||||
"deleted_row": _serialize_row(model, row),
|
||||
}
|
||||
|
||||
|
||||
@receiver(row_signals.before_rows_delete)
|
||||
def public_before_rows_delete(sender, rows, user, table, model, **kwargs):
|
||||
row_checker = ViewHandler().get_public_views_row_checker(
|
||||
|
@ -179,21 +102,6 @@ def public_before_rows_delete(sender, rows, user, table, model, **kwargs):
|
|||
}
|
||||
|
||||
|
||||
@receiver(row_signals.row_deleted)
|
||||
def public_row_deleted(
|
||||
sender, row_id, row, user, table, model, before_return, **kwargs
|
||||
):
|
||||
public_views = dict(before_return)[public_before_row_delete][
|
||||
"deleted_row_public_views"
|
||||
]
|
||||
serialized_deleted_row = dict(before_return)[public_before_row_delete][
|
||||
"deleted_row"
|
||||
]
|
||||
transaction.on_commit(
|
||||
lambda: _send_row_deleted_event_to_views(serialized_deleted_row, public_views)
|
||||
)
|
||||
|
||||
|
||||
@receiver(row_signals.rows_deleted)
|
||||
def public_rows_deleted(sender, rows, user, table, model, before_return, **kwargs):
|
||||
public_views = dict(before_return)[public_before_rows_delete][
|
||||
|
@ -207,25 +115,6 @@ def public_rows_deleted(sender, rows, user, table, model, before_return, **kwarg
|
|||
)
|
||||
|
||||
|
||||
@receiver(row_signals.before_row_update)
|
||||
def public_before_row_update(
|
||||
sender, row, user, table, model, updated_field_ids, **kwargs
|
||||
):
|
||||
# Generate a serialized version of the row before it is updated. The
|
||||
# `row_updated` receiver needs this serialized version because it can't serialize
|
||||
# the old row after it has been updated.
|
||||
row_checker = ViewHandler().get_public_views_row_checker(
|
||||
table,
|
||||
model,
|
||||
only_include_views_which_want_realtime_events=True,
|
||||
updated_field_ids=updated_field_ids,
|
||||
)
|
||||
return {
|
||||
"old_row_public_views": row_checker.get_public_views_where_row_is_visible(row),
|
||||
"caching_row_checker": row_checker,
|
||||
}
|
||||
|
||||
|
||||
@receiver(row_signals.before_rows_update)
|
||||
def public_before_rows_update(
|
||||
sender, rows, user, table, model, updated_field_ids, **kwargs
|
||||
|
@ -244,73 +133,6 @@ def public_before_rows_update(
|
|||
}
|
||||
|
||||
|
||||
@receiver(row_signals.row_updated)
|
||||
def public_row_updated(
|
||||
sender, row, user, table, model, before_return, updated_field_ids, **kwargs
|
||||
):
|
||||
before_return_dict = dict(before_return)[public_before_row_update]
|
||||
serialized_old_row = dict(before_return)[before_row_update]
|
||||
serialized_updated_row = _serialize_row(model, row)
|
||||
|
||||
old_row_public_views = before_return_dict["old_row_public_views"]
|
||||
existing_checker = before_return_dict["caching_row_checker"]
|
||||
views = existing_checker.get_public_views_where_row_is_visible(row)
|
||||
updated_row_public_views = {view.slug: view for view in views}
|
||||
|
||||
# When a row is updated from the point of view of a public view it might not always
|
||||
# result in a `row_updated` event. For example if the row was previously not visible
|
||||
# in the public view due to its filters, but the row update makes it now match
|
||||
# the filters we want to send a `row_created` event to that views page as the
|
||||
# clients won't know anything about the row and hence a `row_updated` event makes
|
||||
# no sense for them.
|
||||
|
||||
public_views_where_row_was_deleted = []
|
||||
public_views_where_row_was_updated = []
|
||||
for old_row_view in old_row_public_views:
|
||||
updated_row_view = updated_row_public_views.pop(old_row_view.slug, None)
|
||||
if updated_row_view is None:
|
||||
# The updated row is no longer visible in `old_row_view` hence we should
|
||||
# send that view a deleted event.
|
||||
public_views_where_row_was_deleted.append(old_row_view)
|
||||
else:
|
||||
# The updated row is still visible so here we want a normal updated event.
|
||||
public_views_where_row_was_updated.append(old_row_view)
|
||||
# Any remaining views in the updated_row_public_views dict are views which
|
||||
# previously didn't show the old row, but now show the new row, so we want created.
|
||||
public_views_where_row_was_created = updated_row_public_views.values()
|
||||
|
||||
def _send_created_updated_deleted_row_signals_to_views():
|
||||
_send_row_deleted_event_to_views(
|
||||
serialized_old_row, public_views_where_row_was_deleted
|
||||
)
|
||||
_send_row_created_event_to_views(
|
||||
serialized_updated_row,
|
||||
before=None,
|
||||
public_views=public_views_where_row_was_created,
|
||||
)
|
||||
|
||||
view_page_type = page_registry.get("view")
|
||||
handler = ViewHandler()
|
||||
for public_view in public_views_where_row_was_updated:
|
||||
(
|
||||
visible_fields_only_updated_row,
|
||||
visible_fields_only_old_row,
|
||||
) = handler.restrict_rows_for_view(
|
||||
public_view, [serialized_updated_row, serialized_old_row]
|
||||
)
|
||||
view_page_type.broadcast(
|
||||
RealtimeRowMessages.row_updated(
|
||||
table_id=PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
serialized_row_before_update=visible_fields_only_old_row,
|
||||
serialized_row=visible_fields_only_updated_row,
|
||||
metadata={},
|
||||
),
|
||||
slug=public_view.slug,
|
||||
)
|
||||
|
||||
transaction.on_commit(_send_created_updated_deleted_row_signals_to_views)
|
||||
|
||||
|
||||
@receiver(row_signals.rows_updated)
|
||||
def public_rows_updated(
|
||||
sender, rows, user, table, model, before_return, updated_field_ids, **kwargs
|
||||
|
@ -332,10 +154,10 @@ def public_rows_updated(
|
|||
}
|
||||
|
||||
# When a row is updated from the point of view of a public view it might not always
|
||||
# result in a `row_updated` event. For example if the row was previously not visible
|
||||
# result in a `rows_updated` event. For example if a row was previously not visible
|
||||
# in the public view due to its filters, but the row update makes it now match
|
||||
# the filters we want to send a `row_created` event to that views page as the
|
||||
# clients won't know anything about the row and hence a `row_updated` event makes
|
||||
# the filters we want to send a `rows_created` event to that views page as the
|
||||
# clients won't know anything about the row and hence a `rows_updated` event makes
|
||||
# no sense for them.
|
||||
public_views_where_rows_were_created: List[PublicViewRows] = []
|
||||
public_views_where_rows_were_updated: List[PublicViewRows] = []
|
||||
|
|
|
@ -13,27 +13,6 @@ from baserow.contrib.database.table.models import GeneratedTableModel
|
|||
from baserow.ws.registries import page_registry
|
||||
|
||||
|
||||
@receiver(row_signals.row_created)
|
||||
def row_created(sender, row, before, user, table, model, **kwargs):
|
||||
table_page_type = page_registry.get("table")
|
||||
transaction.on_commit(
|
||||
lambda: table_page_type.broadcast(
|
||||
RealtimeRowMessages.row_created(
|
||||
table_id=table.id,
|
||||
serialized_row=get_row_serializer_class(
|
||||
model, RowSerializer, is_response=True
|
||||
)(row).data,
|
||||
metadata=row_metadata_registry.generate_and_merge_metadata_for_row(
|
||||
table, row.id
|
||||
),
|
||||
before=before,
|
||||
),
|
||||
getattr(user, "web_socket_id", None),
|
||||
table_id=table.id,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@receiver(row_signals.rows_created)
|
||||
def rows_created(sender, rows, before, user, table, model, **kwargs):
|
||||
table_page_type = page_registry.get("table")
|
||||
|
@ -55,14 +34,6 @@ def rows_created(sender, rows, before, user, table, model, **kwargs):
|
|||
)
|
||||
|
||||
|
||||
@receiver(row_signals.before_row_update)
|
||||
def before_row_update(sender, row, user, table, model, updated_field_ids, **kwargs):
|
||||
# Generate a serialized version of the row before it is updated. The
|
||||
# `row_updated` receiver needs this serialized version because it can't serialize
|
||||
# the old row after it has been updated.
|
||||
return get_row_serializer_class(model, RowSerializer, is_response=True)(row).data
|
||||
|
||||
|
||||
@receiver(row_signals.before_rows_update)
|
||||
def before_rows_update(sender, rows, user, table, model, updated_field_ids, **kwargs):
|
||||
return get_row_serializer_class(model, RowSerializer, is_response=True)(
|
||||
|
@ -70,29 +41,6 @@ def before_rows_update(sender, rows, user, table, model, updated_field_ids, **kw
|
|||
).data
|
||||
|
||||
|
||||
@receiver(row_signals.row_updated)
|
||||
def row_updated(
|
||||
sender, row, user, table, model, before_return, updated_field_ids, **kwargs
|
||||
):
|
||||
table_page_type = page_registry.get("table")
|
||||
transaction.on_commit(
|
||||
lambda: table_page_type.broadcast(
|
||||
RealtimeRowMessages.row_updated(
|
||||
table_id=table.id,
|
||||
serialized_row_before_update=dict(before_return)[before_row_update],
|
||||
serialized_row=get_row_serializer_class(
|
||||
model, RowSerializer, is_response=True
|
||||
)(row).data,
|
||||
metadata=row_metadata_registry.generate_and_merge_metadata_for_row(
|
||||
table, row.id
|
||||
),
|
||||
),
|
||||
getattr(user, "web_socket_id", None),
|
||||
table_id=table.id,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@receiver(row_signals.rows_updated)
|
||||
def rows_updated(
|
||||
sender, rows, user, table, model, before_return, updated_field_ids, **kwargs
|
||||
|
@ -116,14 +64,6 @@ def rows_updated(
|
|||
)
|
||||
|
||||
|
||||
@receiver(row_signals.before_row_delete)
|
||||
def before_row_delete(sender, row, user, table, model, **kwargs):
|
||||
# Generate a serialized version of the row before it is deleted. The
|
||||
# `row_deleted` receiver needs this serialized version because it can't serialize
|
||||
# the row after is has been deleted.
|
||||
return get_row_serializer_class(model, RowSerializer, is_response=True)(row).data
|
||||
|
||||
|
||||
@receiver(row_signals.before_rows_delete)
|
||||
def before_rows_delete(sender, rows, user, table, model, **kwargs):
|
||||
return get_row_serializer_class(model, RowSerializer, is_response=True)(
|
||||
|
@ -131,20 +71,6 @@ def before_rows_delete(sender, rows, user, table, model, **kwargs):
|
|||
).data
|
||||
|
||||
|
||||
@receiver(row_signals.row_deleted)
|
||||
def row_deleted(sender, row_id, row, user, table, model, before_return, **kwargs):
|
||||
table_page_type = page_registry.get("table")
|
||||
transaction.on_commit(
|
||||
lambda: table_page_type.broadcast(
|
||||
RealtimeRowMessages.row_deleted(
|
||||
table_id=table.id, serialized_row=dict(before_return)[before_row_delete]
|
||||
),
|
||||
getattr(user, "web_socket_id", None),
|
||||
table_id=table.id,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@receiver(row_signals.rows_deleted)
|
||||
def rows_deleted(sender, rows, user, table, model, before_return, **kwargs):
|
||||
table_page_type = page_registry.get("table")
|
||||
|
@ -166,18 +92,6 @@ class RealtimeRowMessages:
|
|||
websocket messages related to rows.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def row_deleted(table_id: int, serialized_row: Dict[str, Any]) -> Dict[str, Any]:
|
||||
return {
|
||||
"type": "row_deleted",
|
||||
"table_id": table_id,
|
||||
"row_id": serialized_row["id"],
|
||||
# The web-frontend expects a serialized version of the row that is
|
||||
# deleted in order the estimate what position the row had in the view,
|
||||
# or find which kanban column the row was in etc.
|
||||
"row": serialized_row,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def rows_deleted(
|
||||
table_id: int, serialized_rows: List[Dict[str, Any]]
|
||||
|
@ -189,21 +103,6 @@ class RealtimeRowMessages:
|
|||
"rows": serialized_rows,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def row_created(
|
||||
table_id: int,
|
||||
serialized_row: Dict[str, Any],
|
||||
metadata: Dict[str, Any],
|
||||
before: Optional[GeneratedTableModel],
|
||||
) -> Dict[str, Any]:
|
||||
return {
|
||||
"type": "row_created",
|
||||
"table_id": table_id,
|
||||
"row": serialized_row,
|
||||
"metadata": metadata,
|
||||
"before_row_id": before.id if before else None,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def rows_created(
|
||||
table_id: int,
|
||||
|
@ -219,24 +118,6 @@ class RealtimeRowMessages:
|
|||
"before_row_id": before.id if before else None,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def row_updated(
|
||||
table_id: int,
|
||||
serialized_row_before_update: Dict[str, Any],
|
||||
serialized_row: Dict[str, Any],
|
||||
metadata: Dict[str, Any],
|
||||
) -> Dict[str, Any]:
|
||||
return {
|
||||
"type": "row_updated",
|
||||
"table_id": table_id,
|
||||
# The web-frontend expects a serialized version of the row before it
|
||||
# was updated in order the estimate what position the row had in the
|
||||
# view.
|
||||
"row_before_update": serialized_row_before_update,
|
||||
"row": serialized_row,
|
||||
"metadata": metadata,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def rows_updated(
|
||||
table_id: int,
|
||||
|
|
|
@ -3,11 +3,9 @@ from django.conf import settings
|
|||
from .table.signals import table_created, table_updated, table_deleted
|
||||
from .views.signals import view_created, views_reordered, view_updated, view_deleted
|
||||
from .rows.signals import (
|
||||
row_created,
|
||||
rows_created,
|
||||
row_updated,
|
||||
rows_updated,
|
||||
row_deleted,
|
||||
rows_deleted,
|
||||
)
|
||||
from .fields.signals import field_created, field_updated, field_deleted
|
||||
|
||||
|
@ -16,9 +14,9 @@ if settings.DISABLE_ANONYMOUS_PUBLIC_VIEW_WS_CONNECTIONS:
|
|||
else:
|
||||
# noinspection PyUnresolvedReferences
|
||||
from .public.rows.signals import ( # noqa: F401
|
||||
public_row_created,
|
||||
public_row_deleted,
|
||||
public_row_updated,
|
||||
public_rows_created,
|
||||
public_rows_deleted,
|
||||
public_rows_updated,
|
||||
)
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
|
@ -39,9 +37,9 @@ else:
|
|||
)
|
||||
|
||||
PUBLIC_SIGNALS = [
|
||||
"public_row_created",
|
||||
"public_row_deleted",
|
||||
"public_row_updated",
|
||||
"public_rows_created",
|
||||
"public_rows_deleted",
|
||||
"public_rows_updated",
|
||||
"public_view_filter_updated",
|
||||
"public_view_filter_deleted",
|
||||
"public_view_filter_created",
|
||||
|
@ -61,11 +59,9 @@ __all__ = [
|
|||
"view_created",
|
||||
"view_updated",
|
||||
"view_deleted",
|
||||
"row_created",
|
||||
"rows_created",
|
||||
"row_updated",
|
||||
"rows_updated",
|
||||
"row_deleted",
|
||||
"rows_deleted",
|
||||
"field_created",
|
||||
"field_updated",
|
||||
"field_deleted",
|
||||
|
|
|
@ -42,7 +42,7 @@ class TableWebhookFixture:
|
|||
kwargs["webhook"] = self.create_table_webhook(user=None)
|
||||
|
||||
if "event_type" not in kwargs:
|
||||
kwargs["event_type"] = "row.created"
|
||||
kwargs["event_type"] = "rows.created"
|
||||
|
||||
if "called_url" not in kwargs:
|
||||
kwargs["called_url"] = self.fake.url()
|
||||
|
|
|
@ -24,7 +24,7 @@ def test_list_webhooks(api_client, data_fixture):
|
|||
)
|
||||
call_1 = data_fixture.create_table_webhook_call(webhook=webhook_1)
|
||||
webhook_2 = data_fixture.create_table_webhook(
|
||||
table=table, include_all_events=False, events=["row.created"]
|
||||
table=table, include_all_events=False, events=["rows.created"]
|
||||
)
|
||||
data_fixture.create_table_webhook()
|
||||
|
||||
|
@ -74,7 +74,7 @@ def test_list_webhooks(api_client, data_fixture):
|
|||
}
|
||||
|
||||
assert response_json[1]["id"] == webhook_2.id
|
||||
assert response_json[1]["events"] == ["row.created"]
|
||||
assert response_json[1]["events"] == ["rows.created"]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
@ -138,7 +138,7 @@ def test_create_webhooks(api_client, data_fixture):
|
|||
"url": "https://mydomain.com/endpoint",
|
||||
"name": "My Webhook 2",
|
||||
"include_all_events": False,
|
||||
"events": ["row.created"],
|
||||
"events": ["rows.created"],
|
||||
"headers": {"Baserow-add-1": "Value 1"},
|
||||
"request_method": "PATCH",
|
||||
"use_user_field_names": False,
|
||||
|
@ -154,7 +154,7 @@ def test_create_webhooks(api_client, data_fixture):
|
|||
assert response_json["name"] == "My Webhook 2"
|
||||
assert response_json["include_all_events"] is False
|
||||
assert response_json["failed_triggers"] == 0
|
||||
assert response_json["events"] == ["row.created"]
|
||||
assert response_json["events"] == ["rows.created"]
|
||||
assert response_json["headers"] == {"Baserow-add-1": "Value 1"}
|
||||
assert response_json["calls"] == []
|
||||
assert TableWebhook.objects.all().count() == 2
|
||||
|
@ -164,7 +164,7 @@ def test_create_webhooks(api_client, data_fixture):
|
|||
{
|
||||
"url": "https://mydomain.com/endpoint",
|
||||
"name": "My Webhook 2",
|
||||
"events": ["row.created"],
|
||||
"events": ["rows.created"],
|
||||
"headers": {"Test:": "Value 1"},
|
||||
"request_method": "PATCH",
|
||||
},
|
||||
|
@ -197,7 +197,7 @@ def test_create_webhooks(api_client, data_fixture):
|
|||
{
|
||||
"url": "https://mydomain.com:8a/endpoint",
|
||||
"name": "My Webhook 2",
|
||||
"events": ["row.created"],
|
||||
"events": ["rows.created"],
|
||||
"request_method": "PATCH",
|
||||
},
|
||||
format="json",
|
||||
|
@ -214,7 +214,7 @@ def test_create_webhooks(api_client, data_fixture):
|
|||
{
|
||||
"url": "https://md.com/" + (2001 - len("https://md.com/")) * "a",
|
||||
"name": "My Webhook 2",
|
||||
"events": ["row.created"],
|
||||
"events": ["rows.created"],
|
||||
"request_method": "PATCH",
|
||||
},
|
||||
format="json",
|
||||
|
@ -318,7 +318,7 @@ def test_update_webhook(api_client, data_fixture):
|
|||
"url": "https://mydomain.com/endpoint",
|
||||
"name": "My Webhook 2",
|
||||
"include_all_events": False,
|
||||
"events": ["row.created"],
|
||||
"events": ["rows.created"],
|
||||
"headers": {"Baserow-add-1": "Value 1"},
|
||||
"request_method": "PATCH",
|
||||
"use_user_field_names": False,
|
||||
|
@ -335,7 +335,7 @@ def test_update_webhook(api_client, data_fixture):
|
|||
assert response_json["name"] == "My Webhook 2"
|
||||
assert response_json["include_all_events"] is False
|
||||
assert response_json["failed_triggers"] == 0
|
||||
assert response_json["events"] == ["row.created"]
|
||||
assert response_json["events"] == ["rows.created"]
|
||||
assert response_json["headers"] == {"Baserow-add-1": "Value 1"}
|
||||
assert response_json["calls"] == []
|
||||
|
||||
|
@ -401,7 +401,7 @@ def test_trigger_test_call(api_client, data_fixture):
|
|||
reverse("api:database:webhooks:test", kwargs={"table_id": 0}),
|
||||
{
|
||||
"url": "http://baserow.io",
|
||||
"event_type": "row.created",
|
||||
"event_type": "rows.created",
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {jwt_token}",
|
||||
|
@ -413,7 +413,7 @@ def test_trigger_test_call(api_client, data_fixture):
|
|||
reverse("api:database:webhooks:test", kwargs={"table_id": table.id}),
|
||||
{
|
||||
"url": "http://baserow.io",
|
||||
"event_type": "row.created",
|
||||
"event_type": "rows.created",
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {jwt_token_2}",
|
||||
|
@ -425,7 +425,7 @@ def test_trigger_test_call(api_client, data_fixture):
|
|||
reverse("api:database:webhooks:test", kwargs={"table_id": table.id}),
|
||||
{
|
||||
"url": "http://baserow.io",
|
||||
"event_type": "row.created",
|
||||
"event_type": "rows.created",
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {jwt_token}",
|
||||
|
@ -441,7 +441,7 @@ def test_trigger_test_call(api_client, data_fixture):
|
|||
reverse("api:database:webhooks:test", kwargs={"table_id": table.id}),
|
||||
{
|
||||
"url": "http://baserow.io/invalid",
|
||||
"event_type": "row.created",
|
||||
"event_type": "rows.created",
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {jwt_token}",
|
||||
|
|
|
@ -3,10 +3,11 @@ import pytest
|
|||
from baserow.contrib.database.webhooks.registries import webhook_event_type_registry
|
||||
from baserow.contrib.database.fields.handler import FieldHandler
|
||||
from baserow.contrib.database.rows.handler import RowHandler
|
||||
from baserow.contrib.database.ws.rows.signals import before_rows_update
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_row_created_event_type(data_fixture):
|
||||
def test_rows_created_event_type(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
field = data_fixture.create_text_field(table=table, primary=True, name="Test 1")
|
||||
|
@ -19,41 +20,43 @@ def test_row_created_event_type(data_fixture):
|
|||
url="http://localhost",
|
||||
use_user_field_names=False,
|
||||
)
|
||||
payload = webhook_event_type_registry.get("row.created").get_payload(
|
||||
event_id="1", webhook=webhook, model=model, table=table, row=row
|
||||
payload = webhook_event_type_registry.get("rows.created").get_payload(
|
||||
event_id="1", webhook=webhook, model=model, table=table, rows=[row]
|
||||
)
|
||||
assert payload == {
|
||||
"table_id": table.id,
|
||||
"event_id": "1",
|
||||
"event_type": "row.created",
|
||||
"row_id": row.id,
|
||||
"values": {
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{field.id}": None,
|
||||
},
|
||||
"event_type": "rows.created",
|
||||
"items": [
|
||||
{
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{field.id}": None,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
webhook.use_user_field_names = True
|
||||
webhook.save()
|
||||
payload = webhook_event_type_registry.get("row.created").get_payload(
|
||||
event_id="1", webhook=webhook, model=model, table=table, row=row
|
||||
payload = webhook_event_type_registry.get("rows.created").get_payload(
|
||||
event_id="1", webhook=webhook, model=model, table=table, rows=[row]
|
||||
)
|
||||
assert payload == {
|
||||
"table_id": table.id,
|
||||
"event_id": "1",
|
||||
"event_type": "row.created",
|
||||
"row_id": row.id,
|
||||
"values": {
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
"Test 1": None,
|
||||
},
|
||||
"event_type": "rows.created",
|
||||
"items": [
|
||||
{
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
"Test 1": None,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_row_updated_event_type(data_fixture):
|
||||
def test_rows_updated_event_type(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
table_2 = data_fixture.create_database_table(database=table.database)
|
||||
|
@ -85,9 +88,16 @@ def test_row_updated_event_type(data_fixture):
|
|||
row = model.objects.create(**{f"field_{text_field.id}": "Old Test value"})
|
||||
getattr(row, f"field_{link_row_field.id}").add(i1.id)
|
||||
|
||||
before_return = webhook_event_type_registry.get(
|
||||
"row.updated"
|
||||
).get_test_call_before_return(table, row, model)
|
||||
before_return = {
|
||||
before_rows_update: before_rows_update(
|
||||
rows=[row],
|
||||
model=model,
|
||||
table=table,
|
||||
sender=None,
|
||||
user=None,
|
||||
updated_field_ids=None,
|
||||
)
|
||||
}
|
||||
|
||||
row = RowHandler().update_row_by_id(
|
||||
user=user,
|
||||
|
@ -103,65 +113,71 @@ def test_row_updated_event_type(data_fixture):
|
|||
url="http://localhost",
|
||||
use_user_field_names=False,
|
||||
)
|
||||
payload = webhook_event_type_registry.get("row.updated").get_payload(
|
||||
payload = webhook_event_type_registry.get("rows.updated").get_payload(
|
||||
event_id="1",
|
||||
webhook=webhook,
|
||||
model=model,
|
||||
table=table,
|
||||
row=row,
|
||||
rows=[row],
|
||||
before_return=before_return,
|
||||
)
|
||||
assert payload == {
|
||||
"table_id": table.id,
|
||||
"event_id": "1",
|
||||
"event_type": "row.updated",
|
||||
"row_id": row.id,
|
||||
"values": {
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{text_field.id}": "New Test value",
|
||||
f"field_{link_row_field.id}": [{"id": 1, "value": "Lookup 1"}],
|
||||
},
|
||||
"old_values": {
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{text_field.id}": "Old Test value",
|
||||
f"field_{link_row_field.id}": [{"id": 1, "value": "Lookup 1"}],
|
||||
},
|
||||
"event_type": "rows.updated",
|
||||
"items": [
|
||||
{
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{text_field.id}": "New Test value",
|
||||
f"field_{link_row_field.id}": [{"id": 1, "value": "Lookup 1"}],
|
||||
}
|
||||
],
|
||||
"old_items": [
|
||||
{
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{text_field.id}": "Old Test value",
|
||||
f"field_{link_row_field.id}": [{"id": 1, "value": "Lookup 1"}],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
webhook.use_user_field_names = True
|
||||
webhook.save()
|
||||
payload = webhook_event_type_registry.get("row.updated").get_payload(
|
||||
payload = webhook_event_type_registry.get("rows.updated").get_payload(
|
||||
event_id="1",
|
||||
webhook=webhook,
|
||||
model=model,
|
||||
table=table,
|
||||
row=row,
|
||||
rows=[row],
|
||||
before_return=before_return,
|
||||
)
|
||||
assert payload == {
|
||||
"table_id": table.id,
|
||||
"event_id": "1",
|
||||
"event_type": "row.updated",
|
||||
"row_id": row.id,
|
||||
"values": {
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"{text_field.name}": "New Test value",
|
||||
f"{link_row_field.name}": [{"id": 1, "value": "Lookup 1"}],
|
||||
},
|
||||
"old_values": {
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"{text_field.name}": "Old Test value",
|
||||
f"{link_row_field.name}": [{"id": 1, "value": "Lookup 1"}],
|
||||
},
|
||||
"event_type": "rows.updated",
|
||||
"items": [
|
||||
{
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"{text_field.name}": "New Test value",
|
||||
f"{link_row_field.name}": [{"id": 1, "value": "Lookup 1"}],
|
||||
}
|
||||
],
|
||||
"old_items": [
|
||||
{
|
||||
"id": 1,
|
||||
"order": "1.00000000000000000000",
|
||||
f"{text_field.name}": "Old Test value",
|
||||
f"{link_row_field.name}": [{"id": 1, "value": "Lookup 1"}],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_row_deleted_event_type(data_fixture):
|
||||
def test_rows_deleted_event_type(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
data_fixture.create_text_field(table=table, primary=True, name="Test 1")
|
||||
|
@ -174,17 +190,17 @@ def test_row_deleted_event_type(data_fixture):
|
|||
url="http://localhost",
|
||||
use_user_field_names=False,
|
||||
)
|
||||
payload = webhook_event_type_registry.get("row.deleted").get_payload(
|
||||
payload = webhook_event_type_registry.get("rows.deleted").get_payload(
|
||||
event_id="1",
|
||||
webhook=webhook,
|
||||
model=model,
|
||||
table=table,
|
||||
row=row,
|
||||
rows=[row],
|
||||
)
|
||||
|
||||
assert payload == {
|
||||
"table_id": table.id,
|
||||
"event_id": "1",
|
||||
"event_type": "row.deleted",
|
||||
"row_id": row.id,
|
||||
"event_type": "rows.deleted",
|
||||
"row_ids": [row.id],
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ def test_extract_manytomany_values(data_fixture):
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("baserow.contrib.database.rows.signals.row_created.send")
|
||||
@patch("baserow.contrib.database.rows.signals.rows_created.send")
|
||||
def test_create_row(send_mock, data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
user_2 = data_fixture.create_user()
|
||||
|
@ -152,7 +152,7 @@ def test_create_row(send_mock, data_fixture):
|
|||
assert row_1.order == Decimal("1.00000000000000000000")
|
||||
|
||||
send_mock.assert_called_once()
|
||||
assert send_mock.call_args[1]["row"].id == row_1.id
|
||||
assert send_mock.call_args[1]["rows"][0].id == row_1.id
|
||||
assert send_mock.call_args[1]["user"].id == user.id
|
||||
assert send_mock.call_args[1]["table"].id == table.id
|
||||
assert send_mock.call_args[1]["before"] is None
|
||||
|
@ -287,7 +287,7 @@ def test_get_row(data_fixture):
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("baserow.contrib.database.rows.signals.row_updated.send")
|
||||
@patch("baserow.contrib.database.rows.signals.rows_updated.send")
|
||||
def test_update_row(send_mock, data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
user_2 = data_fixture.create_user()
|
||||
|
@ -320,7 +320,7 @@ def test_update_row(send_mock, data_fixture):
|
|||
)
|
||||
|
||||
with patch(
|
||||
"baserow.contrib.database.rows.signals.before_row_update.send"
|
||||
"baserow.contrib.database.rows.signals.before_rows_update.send"
|
||||
) as before_send_mock:
|
||||
handler.update_row_by_id(
|
||||
user=user,
|
||||
|
@ -339,13 +339,13 @@ def test_update_row(send_mock, data_fixture):
|
|||
assert getattr(row, f"field_{price_field.id}") == Decimal("59999.99")
|
||||
|
||||
before_send_mock.assert_called_once()
|
||||
assert before_send_mock.call_args[1]["row"].id == row.id
|
||||
assert before_send_mock.call_args[1]["rows"][0].id == row.id
|
||||
assert before_send_mock.call_args[1]["user"].id == user.id
|
||||
assert before_send_mock.call_args[1]["table"].id == table.id
|
||||
assert before_send_mock.call_args[1]["model"]._generated_table_model
|
||||
|
||||
send_mock.assert_called_once()
|
||||
assert send_mock.call_args[1]["row"].id == row.id
|
||||
assert send_mock.call_args[1]["rows"][0].id == row.id
|
||||
assert send_mock.call_args[1]["user"].id == user.id
|
||||
assert send_mock.call_args[1]["table"].id == table.id
|
||||
assert send_mock.call_args[1]["model"]._generated_table_model
|
||||
|
@ -387,8 +387,8 @@ def test_update_rows_created_on_and_last_modified(data_fixture):
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("baserow.contrib.database.rows.signals.row_updated.send")
|
||||
@patch("baserow.contrib.database.rows.signals.before_row_update.send")
|
||||
@patch("baserow.contrib.database.rows.signals.rows_updated.send")
|
||||
@patch("baserow.contrib.database.rows.signals.before_rows_update.send")
|
||||
def test_move_row(before_send_mock, send_mock, data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
user_2 = data_fixture.create_user()
|
||||
|
@ -414,13 +414,13 @@ def test_move_row(before_send_mock, send_mock, data_fixture):
|
|||
assert row_3.order == Decimal("3.00000000000000000000")
|
||||
|
||||
before_send_mock.assert_called_once()
|
||||
assert before_send_mock.call_args[1]["row"].id == row_1.id
|
||||
assert before_send_mock.call_args[1]["rows"][0].id == row_1.id
|
||||
assert before_send_mock.call_args[1]["user"].id == user.id
|
||||
assert before_send_mock.call_args[1]["table"].id == table.id
|
||||
assert before_send_mock.call_args[1]["model"]._generated_table_model
|
||||
|
||||
send_mock.assert_called_once()
|
||||
assert send_mock.call_args[1]["row"].id == row_1.id
|
||||
assert send_mock.call_args[1]["rows"][0].id == row_1.id
|
||||
assert send_mock.call_args[1]["user"].id == user.id
|
||||
assert send_mock.call_args[1]["table"].id == table.id
|
||||
assert send_mock.call_args[1]["model"]._generated_table_model
|
||||
|
@ -441,8 +441,8 @@ def test_move_row(before_send_mock, send_mock, data_fixture):
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("baserow.contrib.database.rows.signals.row_deleted.send")
|
||||
@patch("baserow.contrib.database.rows.signals.before_row_delete.send")
|
||||
@patch("baserow.contrib.database.rows.signals.rows_deleted.send")
|
||||
@patch("baserow.contrib.database.rows.signals.before_rows_delete.send")
|
||||
def test_delete_row(before_send_mock, send_mock, data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
user_2 = data_fixture.create_user()
|
||||
|
@ -468,14 +468,13 @@ def test_delete_row(before_send_mock, send_mock, data_fixture):
|
|||
assert row.trashed
|
||||
|
||||
before_send_mock.assert_called_once()
|
||||
assert before_send_mock.call_args[1]["row"]
|
||||
assert before_send_mock.call_args[1]["rows"]
|
||||
assert before_send_mock.call_args[1]["user"].id == user.id
|
||||
assert before_send_mock.call_args[1]["table"].id == table.id
|
||||
assert before_send_mock.call_args[1]["model"]._generated_table_model
|
||||
|
||||
send_mock.assert_called_once()
|
||||
assert send_mock.call_args[1]["row_id"] == row_id
|
||||
assert send_mock.call_args[1]["row"]
|
||||
assert send_mock.call_args[1]["rows"][0].id == row_id
|
||||
assert send_mock.call_args[1]["user"].id == user.id
|
||||
assert send_mock.call_args[1]["table"].id == table.id
|
||||
assert send_mock.call_args[1]["model"]._generated_table_model
|
||||
|
@ -483,7 +482,7 @@ def test_delete_row(before_send_mock, send_mock, data_fixture):
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("baserow.contrib.database.rows.signals.row_created.send")
|
||||
@patch("baserow.contrib.database.rows.signals.rows_created.send")
|
||||
def test_restore_row(send_mock, data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(name="Car", user=user)
|
||||
|
@ -516,7 +515,7 @@ def test_restore_row(send_mock, data_fixture):
|
|||
TrashHandler.restore_item(user, "row", row_1.id, parent_trash_item_id=table.id)
|
||||
|
||||
assert len(send_mock.call_args) == 2
|
||||
assert send_mock.call_args[1]["row"].id == row_1.id
|
||||
assert send_mock.call_args[1]["rows"][0].id == row_1.id
|
||||
assert send_mock.call_args[1]["user"] is None
|
||||
assert send_mock.call_args[1]["table"].id == table.id
|
||||
assert send_mock.call_args[1]["before"] is None
|
||||
|
|
|
@ -475,16 +475,18 @@ def test_trash_and_restore_rows_in_batch(send_mock, data_fixture):
|
|||
customers_primary_field = field_handler.create_field(
|
||||
user=user, table=customers_table, type_name="text", name="Name", primary=True
|
||||
)
|
||||
row1 = row_handler.create_row(
|
||||
user=user,
|
||||
table=customers_table,
|
||||
values={f"field_{customers_primary_field.id}": "Row A"},
|
||||
)
|
||||
row2 = row_handler.create_row(
|
||||
user=user,
|
||||
table=customers_table,
|
||||
values={f"field_{customers_primary_field.id}": ""},
|
||||
)
|
||||
|
||||
with patch("baserow.contrib.database.rows.signals.rows_created.send"):
|
||||
row1 = row_handler.create_row(
|
||||
user=user,
|
||||
table=customers_table,
|
||||
values={f"field_{customers_primary_field.id}": "Row A"},
|
||||
)
|
||||
row2 = row_handler.create_row(
|
||||
user=user,
|
||||
table=customers_table,
|
||||
values={f"field_{customers_primary_field.id}": ""},
|
||||
)
|
||||
|
||||
trashed_rows = TrashedRows.objects.create(
|
||||
table=customers_table, row_ids=[row1.id, row2.id]
|
||||
|
|
|
@ -1419,7 +1419,7 @@ def test_get_public_view_by_slug(data_fixture):
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("baserow.contrib.database.rows.signals.row_created.send")
|
||||
@patch("baserow.contrib.database.rows.signals.rows_created.send")
|
||||
def test_submit_form_view(send_mock, data_fixture):
|
||||
table = data_fixture.create_database_table()
|
||||
form = data_fixture.create_form_view(table=table)
|
||||
|
@ -1451,7 +1451,7 @@ def test_submit_form_view(send_mock, data_fixture):
|
|||
)
|
||||
|
||||
send_mock.assert_called_once()
|
||||
assert send_mock.call_args[1]["row"].id == instance.id
|
||||
assert send_mock.call_args[1]["rows"][0].id == instance.id
|
||||
assert send_mock.call_args[1]["user"] is None
|
||||
assert send_mock.call_args[1]["table"].id == table.id
|
||||
assert send_mock.call_args[1]["before"] is None
|
||||
|
|
|
@ -23,15 +23,15 @@ def test_find_webhooks_to_call(data_fixture):
|
|||
table=table_1, include_all_events=True, active=True
|
||||
)
|
||||
webhook_2 = data_fixture.create_table_webhook(
|
||||
table=table_1, include_all_events=False, events=["row.created"], active=True
|
||||
table=table_1, include_all_events=False, events=["rows.created"], active=True
|
||||
)
|
||||
data_fixture.create_table_webhook(
|
||||
table=table_1, include_all_events=False, events=["row.updated"], active=False
|
||||
table=table_1, include_all_events=False, events=["rows.updated"], active=False
|
||||
)
|
||||
webhook_4 = data_fixture.create_table_webhook(
|
||||
table=table_1,
|
||||
include_all_events=False,
|
||||
events=["row.updated", "row.deleted"],
|
||||
events=["rows.updated", "rows.deleted"],
|
||||
active=True,
|
||||
)
|
||||
webhook_5 = data_fixture.create_table_webhook(
|
||||
|
@ -42,42 +42,42 @@ def test_find_webhooks_to_call(data_fixture):
|
|||
webhook_6 = data_fixture.create_table_webhook(
|
||||
table=table_2,
|
||||
include_all_events=False,
|
||||
events=["row.updated"],
|
||||
events=["rows.updated"],
|
||||
active=True,
|
||||
)
|
||||
|
||||
handler = WebhookHandler()
|
||||
|
||||
webhooks = handler.find_webhooks_to_call(table_1.id, "row.created")
|
||||
webhooks = handler.find_webhooks_to_call(table_1.id, "rows.created")
|
||||
webhook_ids = [webhook.id for webhook in webhooks]
|
||||
assert len(webhook_ids) == 2
|
||||
assert webhook_1.id in webhook_ids
|
||||
assert webhook_2.id in webhook_ids
|
||||
|
||||
webhooks = handler.find_webhooks_to_call(table_1.id, "row.updated")
|
||||
webhooks = handler.find_webhooks_to_call(table_1.id, "rows.updated")
|
||||
webhook_ids = [webhook.id for webhook in webhooks]
|
||||
assert len(webhook_ids) == 2
|
||||
assert webhook_1.id in webhook_ids
|
||||
assert webhook_4.id in webhook_ids
|
||||
|
||||
webhooks = handler.find_webhooks_to_call(table_1.id, "row.deleted")
|
||||
webhooks = handler.find_webhooks_to_call(table_1.id, "rows.deleted")
|
||||
webhook_ids = [webhook.id for webhook in webhooks]
|
||||
assert len(webhook_ids) == 2
|
||||
assert webhook_1.id in webhook_ids
|
||||
assert webhook_4.id in webhook_ids
|
||||
|
||||
webhooks = handler.find_webhooks_to_call(table_2.id, "row.created")
|
||||
webhooks = handler.find_webhooks_to_call(table_2.id, "rows.created")
|
||||
webhook_ids = [webhook.id for webhook in webhooks]
|
||||
assert len(webhook_ids) == 1
|
||||
assert webhook_5.id in webhook_ids
|
||||
|
||||
webhooks = handler.find_webhooks_to_call(table_2.id, "row.updated")
|
||||
webhooks = handler.find_webhooks_to_call(table_2.id, "rows.updated")
|
||||
webhook_ids = [webhook.id for webhook in webhooks]
|
||||
assert len(webhook_ids) == 2
|
||||
assert webhook_5.id in webhook_ids
|
||||
assert webhook_6.id in webhook_ids
|
||||
|
||||
webhooks = handler.find_webhooks_to_call(table_2.id, "row.deleted")
|
||||
webhooks = handler.find_webhooks_to_call(table_2.id, "rows.deleted")
|
||||
webhook_ids = [webhook.id for webhook in webhooks]
|
||||
assert len(webhook_ids) == 1
|
||||
assert webhook_5.id in webhook_ids
|
||||
|
@ -118,7 +118,7 @@ def test_get_all_table_webhooks(data_fixture, django_assert_num_queries):
|
|||
|
||||
webhook_1 = data_fixture.create_table_webhook(
|
||||
table=table,
|
||||
events=["row.created", "row.updated"],
|
||||
events=["rows.created", "rows.updated"],
|
||||
headers={"Baserow-test-2": "Value 2"},
|
||||
include_all_events=False,
|
||||
)
|
||||
|
@ -184,7 +184,7 @@ def test_create_webhook(data_fixture):
|
|||
|
||||
# if "include_all_events" is True and we pass in events that are not empty
|
||||
# the handler will not create the entry in the events table.
|
||||
events = ["row.created"]
|
||||
events = ["rows.created"]
|
||||
webhook = webhook_handler.create_table_webhook(
|
||||
user=user, table=table, events=events, headers={}, **dict(webhook_data)
|
||||
)
|
||||
|
@ -202,7 +202,7 @@ def test_create_webhook(data_fixture):
|
|||
assert webhook.include_all_events is False
|
||||
webhook_events = webhook.events.all()
|
||||
assert len(webhook_events) == 1
|
||||
assert webhook_events[0].event_type == "row.created"
|
||||
assert webhook_events[0].event_type == "rows.created"
|
||||
webhook_headers = webhook.headers.all()
|
||||
assert len(webhook_headers) == 2
|
||||
assert webhook_headers[0].name == "Baserow-test-1"
|
||||
|
@ -232,7 +232,7 @@ def test_update_webhook(data_fixture):
|
|||
table = data_fixture.create_database_table(user=user)
|
||||
webhook = data_fixture.create_table_webhook(
|
||||
table=table,
|
||||
events=["row.created"],
|
||||
events=["rows.created"],
|
||||
headers={"Baserow-test-1": "Value 1", "Baserow-test-2": "Value 2"},
|
||||
)
|
||||
|
||||
|
@ -262,24 +262,24 @@ def test_update_webhook(data_fixture):
|
|||
|
||||
events_before = list(webhook.events.all())
|
||||
webhook = handler.update_table_webhook(
|
||||
user=user, webhook=webhook, events=["row.created", "row.updated"]
|
||||
user=user, webhook=webhook, events=["rows.created", "rows.updated"]
|
||||
)
|
||||
events = webhook.events.all().order_by("id")
|
||||
assert len(events) == 2
|
||||
assert events[0].id == events_before[0].id
|
||||
assert events[0].event_type == events_before[0].event_type == "row.created"
|
||||
assert events[1].event_type == "row.updated"
|
||||
assert events[0].event_type == events_before[0].event_type == "rows.created"
|
||||
assert events[1].event_type == "rows.updated"
|
||||
|
||||
webhook = handler.update_table_webhook(
|
||||
user=user, webhook=webhook, events=["row.updated"]
|
||||
user=user, webhook=webhook, events=["rows.updated"]
|
||||
)
|
||||
events_2 = webhook.events.all().order_by("id")
|
||||
assert len(events_2) == 1
|
||||
assert events_2[0].id == events[1].id
|
||||
assert events_2[0].event_type == events[1].event_type == "row.updated"
|
||||
assert events_2[0].event_type == events[1].event_type == "rows.updated"
|
||||
|
||||
webhook = handler.update_table_webhook(
|
||||
user=user, webhook=webhook, include_all_events=True, events=["row.created"]
|
||||
user=user, webhook=webhook, include_all_events=True, events=["rows.created"]
|
||||
)
|
||||
assert webhook.events.all().count() == 0
|
||||
|
||||
|
@ -305,7 +305,7 @@ def test_delete_webhook(data_fixture):
|
|||
webhook = data_fixture.create_table_webhook(
|
||||
user=user,
|
||||
headers={"A": "B"},
|
||||
events=["row.created"],
|
||||
events=["rows.created"],
|
||||
include_all_events=False,
|
||||
)
|
||||
data_fixture.create_table_webhook()
|
||||
|
@ -330,23 +330,24 @@ def test_trigger_test_call(data_fixture):
|
|||
handler = WebhookHandler()
|
||||
|
||||
with pytest.raises(UserNotInGroup):
|
||||
handler.trigger_test_call(user=user_2, table=table, event_type="row.created")
|
||||
handler.trigger_test_call(user=user_2, table=table, event_type="rows.created")
|
||||
|
||||
responses.add(responses.POST, "http://localhost", json={}, status=200)
|
||||
|
||||
request, response = handler.trigger_test_call(
|
||||
user=user,
|
||||
table=table,
|
||||
event_type="row.created",
|
||||
event_type="rows.created",
|
||||
headers={"Baserow-add-1": "Value 1"},
|
||||
request_method="POST",
|
||||
url="http://localhost",
|
||||
use_user_field_names=False,
|
||||
)
|
||||
assert response.ok
|
||||
assert request.headers["Baserow-add-1"] == "Value 1"
|
||||
assert request.headers["Content-type"] == "application/json"
|
||||
assert request.headers["X-Baserow-Event"] == "row.created"
|
||||
|
||||
assert response.request.headers["Baserow-add-1"] == "Value 1"
|
||||
assert response.request.headers["Content-type"] == "application/json"
|
||||
assert response.request.headers["X-Baserow-Event"] == "rows.created"
|
||||
assert "X-Baserow-Delivery" in request.headers
|
||||
|
||||
assert request.method == "POST"
|
||||
|
@ -354,13 +355,14 @@ def test_trigger_test_call(data_fixture):
|
|||
|
||||
request_body = json.loads(request.body)
|
||||
assert request_body["table_id"] == table.id
|
||||
assert request_body["event_type"] == "row.created"
|
||||
assert request_body["row_id"] == 0
|
||||
assert request_body["values"] == {
|
||||
"id": 0,
|
||||
f"field_{field.id}": None,
|
||||
"order": "0.00000000000000000000",
|
||||
}
|
||||
assert request_body["event_type"] == "rows.created"
|
||||
assert request_body["items"] == [
|
||||
{
|
||||
"id": 0,
|
||||
f"field_{field.id}": None,
|
||||
"order": "0.00000000000000000000",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
|
@ -16,26 +16,26 @@ def test_signal_listener(mock_call_webhook, data_fixture):
|
|||
table=table,
|
||||
url="http://localhost/",
|
||||
include_all_events=False,
|
||||
events=["row.created"],
|
||||
events=["rows.created"],
|
||||
headers={"Baserow-header-1": "Value 1"},
|
||||
)
|
||||
|
||||
RowHandler().create_row(user=user, table=table, values={})
|
||||
|
||||
mock_call_webhook.delay.assert_called_once()
|
||||
args, kwargs = mock_call_webhook.delay.call_args
|
||||
assert kwargs["webhook_id"] == webhook.id
|
||||
assert isinstance(kwargs["event_id"], uuid.UUID)
|
||||
assert kwargs["event_type"] == "row.created"
|
||||
assert kwargs["event_type"] == "rows.created"
|
||||
assert kwargs["headers"]["Baserow-header-1"] == "Value 1"
|
||||
assert kwargs["headers"]["Content-type"] == "application/json"
|
||||
assert kwargs["headers"]["X-Baserow-Event"] == "row.created"
|
||||
assert kwargs["headers"]["X-Baserow-Event"] == "rows.created"
|
||||
assert len(kwargs["headers"]["X-Baserow-Delivery"]) > 1
|
||||
assert kwargs["method"] == "POST"
|
||||
assert kwargs["url"] == "http://localhost/"
|
||||
assert kwargs["payload"] == {
|
||||
"table_id": table.id,
|
||||
"event_id": kwargs["payload"]["event_id"],
|
||||
"event_type": "row.created",
|
||||
"row_id": 1,
|
||||
"values": {"id": 1, "order": "1.00000000000000000000"},
|
||||
"event_type": "rows.created",
|
||||
"items": [{"id": 1, "order": "1.00000000000000000000"}],
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@ def test_call_webhook(data_fixture):
|
|||
call_webhook.run(
|
||||
webhook_id=0,
|
||||
event_id="00000000-0000-0000-0000-000000000000",
|
||||
event_type="row.created",
|
||||
event_type="rows.created",
|
||||
method="POST",
|
||||
url="http://localhost/",
|
||||
headers={"Baserow-header-1": "Value 1"},
|
||||
payload={"type": "row.created"},
|
||||
payload={"type": "rows.created"},
|
||||
)
|
||||
assert TableWebhookCall.objects.all().count() == 0
|
||||
|
||||
|
@ -32,11 +32,11 @@ def test_call_webhook(data_fixture):
|
|||
call_webhook.run(
|
||||
webhook_id=webhook.id,
|
||||
event_id="00000000-0000-0000-0000-000000000000",
|
||||
event_type="row.created",
|
||||
event_type="rows.created",
|
||||
method="POST",
|
||||
url="http://localhost/",
|
||||
headers={"Baserow-header-1": "Value 1"},
|
||||
payload={"type": "row.created"},
|
||||
payload={"type": "rows.created"},
|
||||
)
|
||||
transaction.commit()
|
||||
|
||||
|
@ -44,7 +44,7 @@ def test_call_webhook(data_fixture):
|
|||
created_call = TableWebhookCall.objects.all().first()
|
||||
called_time_1 = created_call.called_time
|
||||
assert created_call.webhook_id == webhook.id
|
||||
assert created_call.event_type == "row.created"
|
||||
assert created_call.event_type == "rows.created"
|
||||
assert created_call.called_time
|
||||
assert created_call.called_url == "http://localhost/"
|
||||
assert "POST http://localhost/" in created_call.request
|
||||
|
@ -56,11 +56,11 @@ def test_call_webhook(data_fixture):
|
|||
call_webhook.run(
|
||||
webhook_id=webhook.id,
|
||||
event_id="00000000-0000-0000-0000-000000000000",
|
||||
event_type="row.created",
|
||||
event_type="rows.created",
|
||||
method="POST",
|
||||
url="http://localhost/",
|
||||
headers={"Baserow-header-1": "Value 1"},
|
||||
payload={"type": "row.created"},
|
||||
payload={"type": "rows.created"},
|
||||
)
|
||||
|
||||
webhook.refresh_from_db()
|
||||
|
@ -74,11 +74,11 @@ def test_call_webhook(data_fixture):
|
|||
call_webhook.run(
|
||||
webhook_id=webhook.id,
|
||||
event_id="00000000-0000-0000-0000-000000000001",
|
||||
event_type="row.created",
|
||||
event_type="rows.created",
|
||||
method="POST",
|
||||
url="http://localhost/",
|
||||
headers={"Baserow-header-1": "Value 1"},
|
||||
payload={"type": "row.created"},
|
||||
payload={"type": "rows.created"},
|
||||
)
|
||||
|
||||
webhook.refresh_from_db()
|
||||
|
@ -92,17 +92,17 @@ def test_call_webhook(data_fixture):
|
|||
call_webhook(
|
||||
webhook_id=webhook.id,
|
||||
event_id="00000000-0000-0000-0000-000000000002",
|
||||
event_type="row.created",
|
||||
event_type="rows.created",
|
||||
method="POST",
|
||||
url="http://localhost/",
|
||||
headers={"Baserow-header-1": "Value 1"},
|
||||
payload={"type": "row.created"},
|
||||
payload={"type": "rows.created"},
|
||||
)
|
||||
|
||||
assert TableWebhookCall.objects.all().count() == 3
|
||||
created_call = TableWebhookCall.objects.all().first()
|
||||
assert created_call.webhook_id == webhook.id
|
||||
assert created_call.event_type == "row.created"
|
||||
assert created_call.event_type == "rows.created"
|
||||
assert created_call.called_time
|
||||
assert created_call.called_url == "http://localhost/"
|
||||
assert "POST http://localhost/" in created_call.request
|
||||
|
@ -120,17 +120,17 @@ def test_call_webhook(data_fixture):
|
|||
call_webhook(
|
||||
webhook_id=webhook.id,
|
||||
event_id="00000000-0000-0000-0000-000000000003",
|
||||
event_type="row.created",
|
||||
event_type="rows.created",
|
||||
method="POST",
|
||||
url="http://localhost2/",
|
||||
headers={"Baserow-header-1": "Value 1"},
|
||||
payload={"type": "row.created"},
|
||||
payload={"type": "rows.created"},
|
||||
)
|
||||
|
||||
assert TableWebhookCall.objects.all().count() == 4
|
||||
created_call = TableWebhookCall.objects.all().first()
|
||||
assert created_call.webhook_id == webhook.id
|
||||
assert created_call.event_type == "row.created"
|
||||
assert created_call.event_type == "rows.created"
|
||||
assert created_call.called_time
|
||||
assert created_call.called_url == "http://localhost2/"
|
||||
assert "POST http://localhost2/" in created_call.request
|
||||
|
|
|
@ -45,14 +45,16 @@ def test_when_row_created_public_views_receive_restricted_row_created_ws_event(
|
|||
call(
|
||||
f"view-{public_view_only_showing_one_field.slug}",
|
||||
{
|
||||
"type": "row_created",
|
||||
"type": "rows_created",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row": {
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"before_row_id": None,
|
||||
},
|
||||
|
@ -61,16 +63,18 @@ def test_when_row_created_public_views_receive_restricted_row_created_ws_event(
|
|||
call(
|
||||
f"view-{public_view_showing_all_fields.slug}",
|
||||
{
|
||||
"type": "row_created",
|
||||
"type": "rows_created",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row": {
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
# This field is not hidden for this public view and so should be
|
||||
# included
|
||||
f"field_{hidden_field.id}": "Hidden",
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
# This field is not hidden for this public view and so
|
||||
# should be included
|
||||
f"field_{hidden_field.id}": "Hidden",
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"before_row_id": None,
|
||||
},
|
||||
|
@ -137,14 +141,16 @@ def test_when_row_created_public_views_receive_row_created_only_when_filters_mat
|
|||
call(
|
||||
f"view-{public_view_showing_row.slug}",
|
||||
{
|
||||
"type": "row_created",
|
||||
"type": "rows_created",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row": {
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"before_row_id": None,
|
||||
},
|
||||
|
@ -360,32 +366,36 @@ def test_when_row_deleted_public_views_receive_restricted_row_deleted_ws_event(
|
|||
call(
|
||||
f"view-{public_view_only_showing_one_field.slug}",
|
||||
{
|
||||
"type": "row_deleted",
|
||||
"row_id": row.id,
|
||||
"type": "rows_deleted",
|
||||
"row_ids": [row.id],
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row": {
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
},
|
||||
None,
|
||||
),
|
||||
call(
|
||||
f"view-{public_view_showing_all_fields.slug}",
|
||||
{
|
||||
"type": "row_deleted",
|
||||
"row_id": row.id,
|
||||
"type": "rows_deleted",
|
||||
"row_ids": [row.id],
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row": {
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
# This field is not hidden for this public view
|
||||
# and so should be included
|
||||
f"field_{hidden_field.id}": "Hidden",
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
# This field is not hidden for this public view
|
||||
# and so should be included
|
||||
f"field_{hidden_field.id}": "Hidden",
|
||||
}
|
||||
],
|
||||
},
|
||||
None,
|
||||
),
|
||||
|
@ -450,15 +460,17 @@ def test_when_row_deleted_public_views_receive_row_deleted_only_when_filters_mat
|
|||
call(
|
||||
f"view-{public_view_showing_row.slug}",
|
||||
{
|
||||
"type": "row_deleted",
|
||||
"type": "rows_deleted",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row_id": row.id,
|
||||
"row": {
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"row_ids": [row.id],
|
||||
"rows": [
|
||||
{
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
},
|
||||
None,
|
||||
),
|
||||
|
@ -706,14 +718,16 @@ def test_given_row_not_visible_in_public_view_when_updated_to_be_visible_event_s
|
|||
{
|
||||
# The row should appear as a created event as for the public view
|
||||
# it effectively has been created as it didn't exist before.
|
||||
"type": "row_created",
|
||||
"type": "rows_created",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row": {
|
||||
"id": initially_hidden_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": initially_hidden_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"before_row_id": None,
|
||||
},
|
||||
|
@ -1154,16 +1168,18 @@ def test_given_row_visible_in_public_view_when_updated_to_be_not_visible_event_s
|
|||
{
|
||||
# The row should appear as a deleted event as for the public view
|
||||
# it effectively has been.
|
||||
"type": "row_deleted",
|
||||
"type": "rows_deleted",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row_id": initially_visible_row.id,
|
||||
"row": {
|
||||
"id": initially_visible_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent in its state before it
|
||||
# was updated
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"row_ids": [initially_visible_row.id],
|
||||
"rows": [
|
||||
{
|
||||
"id": initially_visible_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent in its state before
|
||||
# it was updated
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
},
|
||||
None,
|
||||
),
|
||||
|
@ -1353,20 +1369,24 @@ def test_given_row_visible_in_public_view_when_updated_to_still_be_visible_event
|
|||
call(
|
||||
f"view-{public_view_with_row_showing.slug}",
|
||||
{
|
||||
"type": "row_updated",
|
||||
"type": "rows_updated",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row_before_update": {
|
||||
"id": initially_visible_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"row": {
|
||||
"id": initially_visible_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "StillVisibleButUpdated",
|
||||
},
|
||||
"rows_before_update": [
|
||||
{
|
||||
"id": initially_visible_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"id": initially_visible_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "StillVisibleButUpdated",
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
},
|
||||
None,
|
||||
|
@ -1620,14 +1640,16 @@ def test_when_row_restored_public_views_receive_restricted_row_created_ws_event(
|
|||
call(
|
||||
f"view-{public_view_only_showing_one_field.slug}",
|
||||
{
|
||||
"type": "row_created",
|
||||
"type": "rows_created",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row": {
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"before_row_id": None,
|
||||
},
|
||||
|
@ -1636,16 +1658,18 @@ def test_when_row_restored_public_views_receive_restricted_row_created_ws_event(
|
|||
call(
|
||||
f"view-{public_view_showing_all_fields.slug}",
|
||||
{
|
||||
"type": "row_created",
|
||||
"type": "rows_created",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row": {
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
# This field is not hidden for this public view and so should be
|
||||
# included
|
||||
f"field_{hidden_field.id}": "Hidden",
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
# This field is not hidden for this public view and so
|
||||
# should be included
|
||||
f"field_{hidden_field.id}": "Hidden",
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"before_row_id": None,
|
||||
},
|
||||
|
@ -1715,14 +1739,16 @@ def test_when_row_restored_public_views_receive_row_created_only_when_filters_ma
|
|||
call(
|
||||
f"view-{public_view_showing_row.slug}",
|
||||
{
|
||||
"type": "row_created",
|
||||
"type": "rows_created",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row": {
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"before_row_id": None,
|
||||
},
|
||||
|
@ -1900,20 +1926,24 @@ def test_given_row_visible_in_public_view_when_moved_row_updated_sent(
|
|||
{
|
||||
# The row should appear as a deleted event as for the public view
|
||||
# it effectively has been.
|
||||
"type": "row_updated",
|
||||
"type": "rows_updated",
|
||||
"table_id": PUBLIC_PLACEHOLDER_ENTITY_ID,
|
||||
"row_before_update": {
|
||||
"id": visible_moving_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"row": {
|
||||
"id": visible_moving_row.id,
|
||||
"order": "0.99999999999999999999",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
},
|
||||
"rows_before_update": [
|
||||
{
|
||||
"id": visible_moving_row.id,
|
||||
"order": "1.00000000000000000000",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"id": visible_moving_row.id,
|
||||
"order": "0.99999999999999999999",
|
||||
# Only the visible field should be sent
|
||||
f"field_{visible_field.id}": "Visible",
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
},
|
||||
None,
|
||||
|
|
|
@ -28,11 +28,11 @@ def test_row_created(mock_broadcast_to_channel_group, data_fixture):
|
|||
mock_broadcast_to_channel_group.delay.assert_called_once()
|
||||
args = mock_broadcast_to_channel_group.delay.call_args
|
||||
assert args[0][0] == f"table-{table.id}"
|
||||
assert args[0][1]["type"] == "row_created"
|
||||
assert args[0][1]["type"] == "rows_created"
|
||||
assert args[0][1]["table_id"] == table.id
|
||||
assert args[0][1]["row"]["id"] == row.id
|
||||
assert args[0][1]["rows"][0]["id"] == row.id
|
||||
assert args[0][1]["before_row_id"] is None
|
||||
assert args[0][1]["row"][f"field_{field.id}"] == "Test"
|
||||
assert args[0][1]["rows"][0][f"field_{field.id}"] == "Test"
|
||||
assert args[0][1]["metadata"] == {}
|
||||
|
||||
row_2 = RowHandler().create_row(
|
||||
|
@ -40,11 +40,11 @@ def test_row_created(mock_broadcast_to_channel_group, data_fixture):
|
|||
)
|
||||
args = mock_broadcast_to_channel_group.delay.call_args
|
||||
assert args[0][0] == f"table-{table.id}"
|
||||
assert args[0][1]["type"] == "row_created"
|
||||
assert args[0][1]["type"] == "rows_created"
|
||||
assert args[0][1]["table_id"] == table.id
|
||||
assert args[0][1]["row"]["id"] == row_2.id
|
||||
assert args[0][1]["rows"][0]["id"] == row_2.id
|
||||
assert args[0][1]["before_row_id"] == row.id
|
||||
assert args[0][1]["row"][f"field_{field.id}"] == "Test2"
|
||||
assert args[0][1]["rows"][0][f"field_{field.id}"] == "Test2"
|
||||
assert args[0][1]["metadata"] == {}
|
||||
|
||||
|
||||
|
@ -64,12 +64,12 @@ def test_row_created_with_metadata(mock_broadcast_to_channel_group, data_fixture
|
|||
mock_broadcast_to_channel_group.delay.assert_called_once()
|
||||
args = mock_broadcast_to_channel_group.delay.call_args
|
||||
assert args[0][0] == f"table-{table.id}"
|
||||
assert args[0][1]["type"] == "row_created"
|
||||
assert args[0][1]["type"] == "rows_created"
|
||||
assert args[0][1]["table_id"] == table.id
|
||||
assert args[0][1]["row"]["id"] == row.id
|
||||
assert args[0][1]["rows"][0]["id"] == row.id
|
||||
assert args[0][1]["before_row_id"] is None
|
||||
assert args[0][1]["row"][f"field_{field.id}"] == "Test"
|
||||
assert args[0][1]["metadata"] == {"row_id": row.id}
|
||||
assert args[0][1]["rows"][0][f"field_{field.id}"] == "Test"
|
||||
assert args[0][1]["metadata"] == {1: {"row_id": row.id}}
|
||||
|
||||
|
||||
def test_populates_with_row_id_metadata():
|
||||
|
@ -102,14 +102,14 @@ def test_row_updated(mock_broadcast_to_channel_group, data_fixture):
|
|||
mock_broadcast_to_channel_group.delay.assert_called_once()
|
||||
args = mock_broadcast_to_channel_group.delay.call_args
|
||||
assert args[0][0] == f"table-{table.id}"
|
||||
assert args[0][1]["type"] == "row_updated"
|
||||
assert args[0][1]["type"] == "rows_updated"
|
||||
assert args[0][1]["table_id"] == table.id
|
||||
assert args[0][1]["row_before_update"]["id"] == row.id
|
||||
assert args[0][1]["row_before_update"][f"field_{field.id}"] is None
|
||||
assert args[0][1]["row_before_update"][f"field_{field_2.id}"] is None
|
||||
assert args[0][1]["row"]["id"] == row.id
|
||||
assert args[0][1]["row"][f"field_{field.id}"] == "Test"
|
||||
assert args[0][1]["row"][f"field_{field_2.id}"] is None
|
||||
assert args[0][1]["rows_before_update"][0]["id"] == row.id
|
||||
assert args[0][1]["rows_before_update"][0][f"field_{field.id}"] is None
|
||||
assert args[0][1]["rows_before_update"][0][f"field_{field_2.id}"] is None
|
||||
assert args[0][1]["rows"][0]["id"] == row.id
|
||||
assert args[0][1]["rows"][0][f"field_{field.id}"] == "Test"
|
||||
assert args[0][1]["rows"][0][f"field_{field_2.id}"] is None
|
||||
assert args[0][1]["metadata"] == {}
|
||||
|
||||
row.refresh_from_db()
|
||||
|
@ -121,11 +121,11 @@ def test_row_updated(mock_broadcast_to_channel_group, data_fixture):
|
|||
|
||||
args = mock_broadcast_to_channel_group.delay.call_args
|
||||
assert args[0][0] == f"table-{table.id}"
|
||||
assert args[0][1]["type"] == "row_updated"
|
||||
assert args[0][1]["type"] == "rows_updated"
|
||||
assert args[0][1]["table_id"] == table.id
|
||||
assert args[0][1]["row"]["id"] == row.id
|
||||
assert args[0][1]["row"][f"field_{field.id}"] == "First"
|
||||
assert args[0][1]["row"][f"field_{field_2.id}"] == "Second"
|
||||
assert args[0][1]["rows"][0]["id"] == row.id
|
||||
assert args[0][1]["rows"][0][f"field_{field.id}"] == "First"
|
||||
assert args[0][1]["rows"][0][f"field_{field_2.id}"] == "Second"
|
||||
assert args[0][1]["metadata"] == {}
|
||||
|
||||
|
||||
|
@ -148,15 +148,15 @@ def test_row_updated_with_metadata(mock_broadcast_to_channel_group, data_fixture
|
|||
mock_broadcast_to_channel_group.delay.assert_called_once()
|
||||
args = mock_broadcast_to_channel_group.delay.call_args
|
||||
assert args[0][0] == f"table-{table.id}"
|
||||
assert args[0][1]["type"] == "row_updated"
|
||||
assert args[0][1]["type"] == "rows_updated"
|
||||
assert args[0][1]["table_id"] == table.id
|
||||
assert args[0][1]["row_before_update"]["id"] == row.id
|
||||
assert args[0][1]["row_before_update"][f"field_{field.id}"] is None
|
||||
assert args[0][1]["row_before_update"][f"field_{field_2.id}"] is None
|
||||
assert args[0][1]["row"]["id"] == row.id
|
||||
assert args[0][1]["row"][f"field_{field.id}"] == "Test"
|
||||
assert args[0][1]["row"][f"field_{field_2.id}"] is None
|
||||
assert args[0][1]["metadata"] == {"row_id": row.id}
|
||||
assert args[0][1]["rows_before_update"][0]["id"] == row.id
|
||||
assert args[0][1]["rows_before_update"][0][f"field_{field.id}"] is None
|
||||
assert args[0][1]["rows_before_update"][0][f"field_{field_2.id}"] is None
|
||||
assert args[0][1]["rows"][0]["id"] == row.id
|
||||
assert args[0][1]["rows"][0][f"field_{field.id}"] == "Test"
|
||||
assert args[0][1]["rows"][0][f"field_{field_2.id}"] is None
|
||||
assert args[0][1]["metadata"] == {1: {"row_id": row.id}}
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
|
@ -174,8 +174,8 @@ def test_row_deleted(mock_broadcast_to_channel_group, data_fixture):
|
|||
mock_broadcast_to_channel_group.delay.assert_called_once()
|
||||
args = mock_broadcast_to_channel_group.delay.call_args
|
||||
assert args[0][0] == f"table-{table.id}"
|
||||
assert args[0][1]["type"] == "row_deleted"
|
||||
assert args[0][1]["row_id"] == row_id
|
||||
assert args[0][1]["type"] == "rows_deleted"
|
||||
assert args[0][1]["row_ids"] == [row_id]
|
||||
assert args[0][1]["table_id"] == table.id
|
||||
assert args[0][1]["row"]["id"] == row_id
|
||||
assert args[0][1]["row"][f"field_{field.id}"] == "Value"
|
||||
assert args[0][1]["rows"][0]["id"] == row_id
|
||||
assert args[0][1]["rows"][0][f"field_{field.id}"] == "Value"
|
||||
|
|
|
@ -24,6 +24,12 @@ For example:
|
|||
|
||||
### Breaking Changes
|
||||
|
||||
* **breaking change** Webhooks `row.created`, `row.updated` and `row.deleted` are
|
||||
replaced with `rows.created`, `rows.updated` and `rows.deleted`, containing multiple
|
||||
changed rows at once. Already created webhooks will still be called, but the received
|
||||
body will contain only the first changed row instead of all rows. It is highly
|
||||
recommended to convert all webhooks to the new types.
|
||||
|
||||
|
||||
## Released (2022-07-05 1.10.2)
|
||||
|
||||
|
@ -75,7 +81,6 @@ For example:
|
|||
* Fix get_human_readable_value crashing for some formula types. [#1042](https://gitlab.com/bramw/baserow/-/issues/1042)
|
||||
* Fix import form that gets stuck in a spinning state when it hits an error.
|
||||
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
|
||||
|
|
|
@ -145,13 +145,9 @@ are subscribed to the page.
|
|||
* `field_updated`
|
||||
* `field_deleted`
|
||||
* `field_restored`
|
||||
* `row_created`
|
||||
* `rows_created`
|
||||
* `row_updated`
|
||||
* `rows_updated`
|
||||
* `row_deleted`
|
||||
* `before_row_update`
|
||||
* `before_row_delete`
|
||||
* `rows_deleted`
|
||||
* `before_rows_update`
|
||||
* `before_rows_delete`
|
||||
* `view_created`
|
||||
|
|
|
@ -175,8 +175,11 @@
|
|||
},
|
||||
"eventType": {
|
||||
"rowCreated": "When a row is created",
|
||||
"rowsCreated": "Rows are created",
|
||||
"rowUpdated": "When a row is updated",
|
||||
"rowDeleted": "When a row is deleted"
|
||||
"rowsUpdated": "Rows are updated",
|
||||
"rowDeleted": "When a row is deleted",
|
||||
"rowsDeleted": "Rows are deleted"
|
||||
}
|
||||
},
|
||||
"clientHandler": {
|
||||
|
|
|
@ -306,3 +306,8 @@
|
|||
color: $color-error-500;
|
||||
}
|
||||
}
|
||||
|
||||
.webhook__convert {
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
|
|
@ -1,221 +1,248 @@
|
|||
<template>
|
||||
<form @submit.prevent="submit" @input="$emit('formchange')">
|
||||
<div
|
||||
v-if="!values.active"
|
||||
class="alert alert--simple alert--primary alert--has-icon"
|
||||
>
|
||||
<div class="alert__icon">
|
||||
<i class="fas fa-exclamation"></i>
|
||||
</div>
|
||||
<div class="alert__title">{{ $t('webhookForm.deactivated.title') }}</div>
|
||||
<p class="alert__content">
|
||||
{{ $t('webhookForm.deactivated.content') }}
|
||||
</p>
|
||||
<a
|
||||
class="button button--ghost margin-top-1"
|
||||
@click="values.active = true"
|
||||
>{{ $t('webhookForm.deactivated.activate') }}</a
|
||||
<div v-if="!isDeprecated">
|
||||
<div
|
||||
v-if="!values.active"
|
||||
class="alert alert--simple alert--primary alert--has-icon"
|
||||
>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-12">
|
||||
<FormElement :error="fieldHasErrors('name')" class="control">
|
||||
<label class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.name') }}
|
||||
</label>
|
||||
<div class="control__elements">
|
||||
<input
|
||||
v-model="values.name"
|
||||
class="input"
|
||||
:class="{ 'input--error': fieldHasErrors('name') }"
|
||||
@blur="$v.values.name.$touch()"
|
||||
/>
|
||||
<div v-if="fieldHasErrors('name')" class="error">
|
||||
{{ $t('error.requiredField') }}
|
||||
</div>
|
||||
</div>
|
||||
</FormElement>
|
||||
</div>
|
||||
<div class="col col-12">
|
||||
<div class="control">
|
||||
<label class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.userFieldNames') }}
|
||||
</label>
|
||||
<div class="control__elements">
|
||||
<Checkbox v-model="values.use_user_field_names">{{
|
||||
$t('webhookForm.checkbox.sendUserFieldNames')
|
||||
}}</Checkbox>
|
||||
</div>
|
||||
<div class="alert__icon">
|
||||
<i class="fas fa-exclamation"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-4">
|
||||
<div class="control">
|
||||
<div class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.requestMethod') }}
|
||||
</div>
|
||||
<div class="control__elements">
|
||||
<Dropdown v-model="values.request_method">
|
||||
<DropdownItem name="GET" value="GET"></DropdownItem>
|
||||
<DropdownItem name="POST" value="POST"></DropdownItem>
|
||||
<DropdownItem name="PATCH" value="PATCH"></DropdownItem>
|
||||
<DropdownItem name="PUT" value="PUT"></DropdownItem>
|
||||
<DropdownItem name="DELETE" value="DELETE"></DropdownItem>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div class="alert__title">
|
||||
{{ $t('webhookForm.deactivated.title') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-8">
|
||||
<FormElement :error="fieldHasErrors('url')" class="control">
|
||||
<label class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.url') }}
|
||||
</label>
|
||||
<div class="control__elements">
|
||||
<input
|
||||
v-model="values.url"
|
||||
:placeholder="$t('webhookForm.inputLabels.url')"
|
||||
class="input"
|
||||
:class="{ 'input--error': fieldHasErrors('url') }"
|
||||
@blur="$v.values.url.$touch()"
|
||||
/>
|
||||
<div
|
||||
v-if="
|
||||
fieldHasErrors('url') &&
|
||||
(!$v.values.url.required || !$v.values.url.url)
|
||||
"
|
||||
class="error"
|
||||
>
|
||||
{{ $t('webhookForm.errors.urlField') }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="$v.values.url.$error && !$v.values.url.maxLength"
|
||||
class="error"
|
||||
>
|
||||
{{
|
||||
$t('error.maxLength', {
|
||||
max: $v.values.url.$params.maxLength.max,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</FormElement>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<label class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.events') }}
|
||||
</label>
|
||||
<div class="control__elements">
|
||||
<Radio v-model="values.include_all_events" :value="true">{{
|
||||
$t('webhookForm.radio.allEvents')
|
||||
}}</Radio>
|
||||
<Radio v-model="values.include_all_events" :value="false">
|
||||
{{ $t('webhookForm.radio.customEvents') }}
|
||||
</Radio>
|
||||
<div v-if="!values.include_all_events" class="webhook__types">
|
||||
<Checkbox
|
||||
v-for="webhookEvent in webhookEventTypes"
|
||||
:key="webhookEvent.type"
|
||||
:value="values.events.includes(webhookEvent.type)"
|
||||
class="webhook__type"
|
||||
@input="
|
||||
$event
|
||||
? values.events.push(webhookEvent.type)
|
||||
: values.events.splice(
|
||||
values.events.indexOf(webhookEvent.type),
|
||||
1
|
||||
)
|
||||
"
|
||||
>{{ webhookEvent.getName() }}</Checkbox
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<div class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.headers') }}
|
||||
</div>
|
||||
<div class="control__elements">
|
||||
<div
|
||||
v-for="(header, index) in headers.concat({
|
||||
name: '',
|
||||
value: '',
|
||||
})"
|
||||
:key="`header-input-${index}`"
|
||||
class="webhook__header"
|
||||
<p class="alert__content">
|
||||
{{ $t('webhookForm.deactivated.content') }}
|
||||
</p>
|
||||
<a
|
||||
class="button button--ghost margin-top-1"
|
||||
@click="values.active = true"
|
||||
>{{ $t('webhookForm.deactivated.activate') }}</a
|
||||
>
|
||||
<div class="webhook__header-row">
|
||||
<input
|
||||
v-model="header.name"
|
||||
class="input webhook__header-key"
|
||||
:class="{
|
||||
'input--error':
|
||||
!lastHeader(index) && $v.headers.$each[index].name.$error,
|
||||
}"
|
||||
:placeholder="$t('webhookForm.inputLabels.name')"
|
||||
@input="lastHeader(index) && addHeader(header.name, header.value)"
|
||||
@blur="
|
||||
!lastHeader(index) && $v.headers.$each[index].name.$touch()
|
||||
"
|
||||
/>
|
||||
<input
|
||||
v-model="header.value"
|
||||
class="input webhook__header-value"
|
||||
:class="{
|
||||
'input--error':
|
||||
!lastHeader(index) && $v.headers.$each[index].value.$error,
|
||||
}"
|
||||
:placeholder="$t('webhookForm.inputLabels.value')"
|
||||
@input="lastHeader(index) && addHeader(header.name, header.value)"
|
||||
@blur="
|
||||
!lastHeader(index) && $v.headers.$each[index].value.$touch()
|
||||
"
|
||||
/>
|
||||
<a
|
||||
v-if="!lastHeader(index)"
|
||||
class="button button--error webhook__header-delete"
|
||||
@click="removeHeader(index)"
|
||||
>
|
||||
<i class="fas fa-trash button__icon"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-12">
|
||||
<FormElement :error="fieldHasErrors('name')" class="control">
|
||||
<label class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.name') }}
|
||||
</label>
|
||||
<div class="control__elements">
|
||||
<input
|
||||
v-model="values.name"
|
||||
class="input"
|
||||
:class="{ 'input--error': fieldHasErrors('name') }"
|
||||
@blur="$v.values.name.$touch()"
|
||||
/>
|
||||
<div v-if="fieldHasErrors('name')" class="error">
|
||||
{{ $t('error.requiredField') }}
|
||||
</div>
|
||||
</div>
|
||||
</FormElement>
|
||||
</div>
|
||||
<div class="col col-12">
|
||||
<div class="control">
|
||||
<label class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.userFieldNames') }}
|
||||
</label>
|
||||
<div class="control__elements">
|
||||
<Checkbox v-model="values.use_user_field_names">{{
|
||||
$t('webhookForm.checkbox.sendUserFieldNames')
|
||||
}}</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="$v.headers.$anyError" class="error">
|
||||
{{ $t('webhookForm.errors.invalidHeaders') }}
|
||||
<div class="col col-4">
|
||||
<div class="control">
|
||||
<div class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.requestMethod') }}
|
||||
</div>
|
||||
<div class="control__elements">
|
||||
<Dropdown v-model="values.request_method">
|
||||
<DropdownItem name="GET" value="GET"></DropdownItem>
|
||||
<DropdownItem name="POST" value="POST"></DropdownItem>
|
||||
<DropdownItem name="PATCH" value="PATCH"></DropdownItem>
|
||||
<DropdownItem name="PUT" value="PUT"></DropdownItem>
|
||||
<DropdownItem name="DELETE" value="DELETE"></DropdownItem>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-8">
|
||||
<FormElement :error="fieldHasErrors('url')" class="control">
|
||||
<label class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.url') }}
|
||||
</label>
|
||||
<div class="control__elements">
|
||||
<input
|
||||
v-model="values.url"
|
||||
:placeholder="$t('webhookForm.inputLabels.url')"
|
||||
class="input"
|
||||
:class="{ 'input--error': fieldHasErrors('url') }"
|
||||
@blur="$v.values.url.$touch()"
|
||||
/>
|
||||
<div
|
||||
v-if="
|
||||
fieldHasErrors('url') &&
|
||||
(!$v.values.url.required || !$v.values.url.url)
|
||||
"
|
||||
class="error"
|
||||
>
|
||||
{{ $t('webhookForm.errors.urlField') }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="$v.values.url.$error && !$v.values.url.maxLength"
|
||||
class="error"
|
||||
>
|
||||
{{
|
||||
$t('error.maxLength', {
|
||||
max: $v.values.url.$params.maxLength.max,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</FormElement>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<label class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.events') }}
|
||||
</label>
|
||||
<div class="control__elements">
|
||||
<Radio v-model="values.include_all_events" :value="true">{{
|
||||
$t('webhookForm.radio.allEvents')
|
||||
}}</Radio>
|
||||
<Radio v-model="values.include_all_events" :value="false">
|
||||
{{ $t('webhookForm.radio.customEvents') }}
|
||||
</Radio>
|
||||
<div v-if="!values.include_all_events" class="webhook__types">
|
||||
<Checkbox
|
||||
v-for="webhookEvent in webhookEventTypes"
|
||||
:key="webhookEvent.type"
|
||||
:value="values.events.includes(webhookEvent.type)"
|
||||
class="webhook__type"
|
||||
@input="
|
||||
$event
|
||||
? values.events.push(webhookEvent.type)
|
||||
: values.events.splice(
|
||||
values.events.indexOf(webhookEvent.type),
|
||||
1
|
||||
)
|
||||
"
|
||||
>{{ webhookEvent.getName() }}</Checkbox
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<div class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.headers') }}
|
||||
</div>
|
||||
<div class="control__elements">
|
||||
<div
|
||||
v-for="(header, index) in headers.concat({
|
||||
name: '',
|
||||
value: '',
|
||||
})"
|
||||
:key="`header-input-${index}`"
|
||||
class="webhook__header"
|
||||
>
|
||||
<div class="webhook__header-row">
|
||||
<input
|
||||
v-model="header.name"
|
||||
class="input webhook__header-key"
|
||||
:class="{
|
||||
'input--error':
|
||||
!lastHeader(index) && $v.headers.$each[index].name.$error,
|
||||
}"
|
||||
:placeholder="$t('webhookForm.inputLabels.name')"
|
||||
@input="
|
||||
lastHeader(index) && addHeader(header.name, header.value)
|
||||
"
|
||||
@blur="
|
||||
!lastHeader(index) && $v.headers.$each[index].name.$touch()
|
||||
"
|
||||
/>
|
||||
<input
|
||||
v-model="header.value"
|
||||
class="input webhook__header-value"
|
||||
:class="{
|
||||
'input--error':
|
||||
!lastHeader(index) && $v.headers.$each[index].value.$error,
|
||||
}"
|
||||
:placeholder="$t('webhookForm.inputLabels.value')"
|
||||
@input="
|
||||
lastHeader(index) && addHeader(header.name, header.value)
|
||||
"
|
||||
@blur="
|
||||
!lastHeader(index) && $v.headers.$each[index].value.$touch()
|
||||
"
|
||||
/>
|
||||
<a
|
||||
v-if="!lastHeader(index)"
|
||||
class="button button--error webhook__header-delete"
|
||||
@click="removeHeader(index)"
|
||||
>
|
||||
<i class="fas fa-trash button__icon"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="$v.headers.$anyError" class="error">
|
||||
{{ $t('webhookForm.errors.invalidHeaders') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<div class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.example') }}
|
||||
</div>
|
||||
<div class="control__elements">
|
||||
<div class="webhook__code-with-dropdown">
|
||||
<div class="webhook__code-dropdown">
|
||||
<Dropdown
|
||||
v-model="exampleWebhookEventType"
|
||||
class="dropdown--floating-left"
|
||||
>
|
||||
<DropdownItem
|
||||
v-for="webhookEvent in webhookEventTypes"
|
||||
:key="webhookEvent.type"
|
||||
:name="webhookEvent.getName()"
|
||||
:value="webhookEvent.type"
|
||||
></DropdownItem>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div class="webhook__code-container">
|
||||
<pre
|
||||
class="webhook__code"
|
||||
><code>{{ JSON.stringify(testExample, null, 4)}}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="button button--ghost" @click="openTestModal()">{{
|
||||
$t('webhookForm.triggerButton')
|
||||
}}</a>
|
||||
<slot></slot>
|
||||
<TestWebhookModal ref="testModal" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="alert alert--error alert--has-icon">
|
||||
<div class="alert__icon">
|
||||
<i class="fas fa-exclamation"></i>
|
||||
</div>
|
||||
<div class="alert__title">
|
||||
{{ $t('webhookForm.deprecatedEventType.title') }}
|
||||
</div>
|
||||
<p class="alert__content">
|
||||
{{ $t('webhookForm.deprecatedEventType.description') }}
|
||||
<button
|
||||
class="button webhook__convert"
|
||||
@click="convertFromDeprecated"
|
||||
>
|
||||
{{ $t('webhookForm.deprecatedEventType.convert') }}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<div class="control__label">
|
||||
{{ $t('webhookForm.inputLabels.example') }}
|
||||
</div>
|
||||
<div class="control__elements">
|
||||
<div class="webhook__code-with-dropdown">
|
||||
<div class="webhook__code-dropdown">
|
||||
<Dropdown
|
||||
v-model="exampleWebhookEventType"
|
||||
class="dropdown--floating-left"
|
||||
>
|
||||
<DropdownItem
|
||||
v-for="webhookEvent in webhookEventTypes"
|
||||
:key="webhookEvent.type"
|
||||
:name="webhookEvent.getName()"
|
||||
:value="webhookEvent.type"
|
||||
></DropdownItem>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div class="webhook__code-container">
|
||||
<pre
|
||||
class="webhook__code"
|
||||
><code>{{ JSON.stringify(testExample, null, 4)}}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="button button--ghost" @click="openTestModal()">{{
|
||||
$t('webhookForm.triggerButton')
|
||||
}}</a>
|
||||
<slot></slot>
|
||||
<TestWebhookModal ref="testModal" />
|
||||
</form>
|
||||
</template>
|
||||
|
||||
|
@ -272,6 +299,11 @@ export default {
|
|||
webhookEventTypes() {
|
||||
return this.$registry.getAll('webhookEvent')
|
||||
},
|
||||
isDeprecated() {
|
||||
return this.values.events.some((eventName) =>
|
||||
['row.created', 'row.updated', 'row.deleted'].includes(eventName)
|
||||
)
|
||||
},
|
||||
/**
|
||||
* Generates an example payload of the webhook event based on the chosen webhook
|
||||
* event type.
|
||||
|
@ -376,6 +408,12 @@ export default {
|
|||
lastHeader(index) {
|
||||
return index === this.headers.length
|
||||
},
|
||||
convertFromDeprecated() {
|
||||
this.values.events = this.values.events.map((eventName) =>
|
||||
eventName.replace('row.', 'rows.')
|
||||
)
|
||||
this.submit()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -43,6 +43,11 @@
|
|||
"title": "Webhook is deactivated",
|
||||
"content": "This webhook has been deactivated because there have been too many consecutive failures. Please check the call log for more details. Click on the button below to activate it again. Don't forgot to save the webhook after activating.",
|
||||
"activate": "Activate"
|
||||
},
|
||||
"deprecatedEventType": {
|
||||
"title": "Deprecated event type",
|
||||
"description": "This webhook doesn't receive information about all changed rows at once. Please convert it to batch-style event type. This changes the JSON body payload to a format that contains multiple rows.",
|
||||
"convert": "Convert"
|
||||
}
|
||||
},
|
||||
"webhook": {
|
||||
|
|
|
@ -64,9 +64,9 @@ import {
|
|||
JSONImporterType,
|
||||
} from '@baserow/modules/database/importerTypes'
|
||||
import {
|
||||
RowCreatedWebhookEventType,
|
||||
RowUpdatedWebhookEventType,
|
||||
RowDeletedWebhookEventType,
|
||||
RowsCreatedWebhookEventType,
|
||||
RowsUpdatedWebhookEventType,
|
||||
RowsDeletedWebhookEventType,
|
||||
} from '@baserow/modules/database/webhookEventTypes'
|
||||
import {
|
||||
ImageFilePreview,
|
||||
|
@ -321,15 +321,15 @@ export default (context) => {
|
|||
app.$registry.register('exporter', new CSVTableExporterType(context))
|
||||
app.$registry.register(
|
||||
'webhookEvent',
|
||||
new RowCreatedWebhookEventType(context)
|
||||
new RowsCreatedWebhookEventType(context)
|
||||
)
|
||||
app.$registry.register(
|
||||
'webhookEvent',
|
||||
new RowUpdatedWebhookEventType(context)
|
||||
new RowsUpdatedWebhookEventType(context)
|
||||
)
|
||||
app.$registry.register(
|
||||
'webhookEvent',
|
||||
new RowDeletedWebhookEventType(context)
|
||||
new RowsDeletedWebhookEventType(context)
|
||||
)
|
||||
|
||||
// Text functions
|
||||
|
|
|
@ -155,21 +155,6 @@ export const registerRealtimeEvents = (realtime) => {
|
|||
}
|
||||
})
|
||||
|
||||
realtime.registerEvent('row_created', (context, data) => {
|
||||
const { app, store } = context
|
||||
for (const viewType of Object.values(app.$registry.getAll('view'))) {
|
||||
viewType.rowCreated(
|
||||
context,
|
||||
data.table_id,
|
||||
store.getters['field/getAll'],
|
||||
store.getters['field/getPrimary'],
|
||||
data.row,
|
||||
data.metadata,
|
||||
'page/'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
realtime.registerEvent('rows_created', (context, data) => {
|
||||
const { app, store } = context
|
||||
for (const viewType of Object.values(app.$registry.getAll('view'))) {
|
||||
|
@ -187,30 +172,7 @@ export const registerRealtimeEvents = (realtime) => {
|
|||
}
|
||||
})
|
||||
|
||||
realtime.registerEvent('row_updated', async (context, data) => {
|
||||
const { app, store } = context
|
||||
for (const viewType of Object.values(app.$registry.getAll('view'))) {
|
||||
await viewType.rowUpdated(
|
||||
context,
|
||||
data.table_id,
|
||||
store.getters['field/getAll'],
|
||||
store.getters['field/getPrimary'],
|
||||
data.row_before_update,
|
||||
data.row,
|
||||
data.metadata,
|
||||
'page/'
|
||||
)
|
||||
}
|
||||
|
||||
store.dispatch('rowModal/updated', {
|
||||
tableId: data.table_id,
|
||||
values: data.row,
|
||||
})
|
||||
})
|
||||
|
||||
realtime.registerEvent('rows_updated', async (context, data) => {
|
||||
// TODO: Rewrite
|
||||
// This is currently a naive implementation of batch rows updates.
|
||||
const { app, store } = context
|
||||
for (const viewType of Object.values(app.$registry.getAll('view'))) {
|
||||
for (let i = 0; i < data.rows.length; i++) {
|
||||
|
@ -234,20 +196,6 @@ export const registerRealtimeEvents = (realtime) => {
|
|||
}
|
||||
})
|
||||
|
||||
realtime.registerEvent('row_deleted', (context, data) => {
|
||||
const { app, store } = context
|
||||
for (const viewType of Object.values(app.$registry.getAll('view'))) {
|
||||
viewType.rowDeleted(
|
||||
context,
|
||||
data.table_id,
|
||||
store.getters['field/getAll'],
|
||||
store.getters['field/getPrimary'],
|
||||
data.row,
|
||||
'page/'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
realtime.registerEvent('rows_deleted', (context, data) => {
|
||||
const { app, store } = context
|
||||
for (const viewType of Object.values(app.$registry.getAll('view'))) {
|
||||
|
|
|
@ -32,56 +32,54 @@ export class WebhookEventType extends Registerable {
|
|||
}
|
||||
}
|
||||
|
||||
export class RowCreatedWebhookEventType extends WebhookEventType {
|
||||
export class RowsCreatedWebhookEventType extends WebhookEventType {
|
||||
getType() {
|
||||
return 'row.created'
|
||||
return 'rows.created'
|
||||
}
|
||||
|
||||
getName() {
|
||||
const { i18n } = this.app
|
||||
return i18n.t('webhook.eventType.rowCreated')
|
||||
return i18n.t('webhook.eventType.rowsCreated')
|
||||
}
|
||||
|
||||
getExamplePayload(table, rowExample) {
|
||||
const payload = super.getExamplePayload(table, rowExample)
|
||||
payload.row_id = rowExample.id
|
||||
payload.values = rowExample
|
||||
payload.items = [rowExample]
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
export class RowUpdatedWebhookEventType extends WebhookEventType {
|
||||
export class RowsUpdatedWebhookEventType extends WebhookEventType {
|
||||
getType() {
|
||||
return 'row.updated'
|
||||
return 'rows.updated'
|
||||
}
|
||||
|
||||
getName() {
|
||||
const { i18n } = this.app
|
||||
return i18n.t('webhook.eventType.rowUpdated')
|
||||
return i18n.t('webhook.eventType.rowsUpdated')
|
||||
}
|
||||
|
||||
getExamplePayload(table, rowExample) {
|
||||
const payload = super.getExamplePayload(table, rowExample)
|
||||
payload.row_id = rowExample.id
|
||||
payload.values = rowExample
|
||||
payload.old_values = rowExample
|
||||
payload.items = [rowExample]
|
||||
payload.old_items = [rowExample]
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
export class RowDeletedWebhookEventType extends WebhookEventType {
|
||||
export class RowsDeletedWebhookEventType extends WebhookEventType {
|
||||
getType() {
|
||||
return 'row.deleted'
|
||||
return 'rows.deleted'
|
||||
}
|
||||
|
||||
getName() {
|
||||
const { i18n } = this.app
|
||||
return i18n.t('webhook.eventType.rowDeleted')
|
||||
return i18n.t('webhook.eventType.rowsDeleted')
|
||||
}
|
||||
|
||||
getExamplePayload(table, rowExample) {
|
||||
const payload = super.getExamplePayload(table, rowExample)
|
||||
payload.row_id = rowExample.id
|
||||
payload.row_ids = [rowExample.id]
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue