diff --git a/backend/src/baserow/contrib/builder/api/workflow_actions/views.py b/backend/src/baserow/contrib/builder/api/workflow_actions/views.py index 8d7ee3264..bb1eb4c3d 100644 --- a/backend/src/baserow/contrib/builder/api/workflow_actions/views.py +++ b/backend/src/baserow/contrib/builder/api/workflow_actions/views.py @@ -396,7 +396,6 @@ class DispatchBuilderWorkflowActionView(APIView): dispatch_context = BuilderDispatchContext( request, workflow_action.page, - element=workflow_action.element, workflow_action=workflow_action, ) diff --git a/backend/src/baserow/contrib/builder/data_providers/data_provider_types.py b/backend/src/baserow/contrib/builder/data_providers/data_provider_types.py index 23689d00a..400baa3b4 100644 --- a/backend/src/baserow/contrib/builder/data_providers/data_provider_types.py +++ b/backend/src/baserow/contrib/builder/data_providers/data_provider_types.py @@ -298,7 +298,9 @@ class CurrentRecordDataProviderType(DataProviderType): """ try: - current_record = dispatch_context.request.data["current_record"] + current_record_data = dispatch_context.request.data["current_record"] + current_record = current_record_data["index"] + current_record_id = current_record_data["record_id"] except KeyError: return None @@ -318,8 +320,9 @@ class CurrentRecordDataProviderType(DataProviderType): # Narrow down our range to just our record index. dispatch_context = dispatch_context.from_context( dispatch_context, - offset=current_record, + offset=0, count=1, + only_record_id=current_record_id, ) return DataSourceDataProviderType().get_data_chunk( diff --git a/backend/src/baserow/contrib/builder/data_sources/builder_dispatch_context.py b/backend/src/baserow/contrib/builder/data_sources/builder_dispatch_context.py index 28402be09..1b30f0bc7 100644 --- a/backend/src/baserow/contrib/builder/data_sources/builder_dispatch_context.py +++ b/backend/src/baserow/contrib/builder/data_sources/builder_dispatch_context.py @@ -33,6 +33,7 @@ class BuilderDispatchContext(DispatchContext): "element", "offset", "count", + "only_record_id", "only_expose_public_allowed_properties", ] @@ -44,12 +45,29 @@ class BuilderDispatchContext(DispatchContext): element: Optional["Element"] = None, offset: Optional[int] = None, count: Optional[int] = None, + only_record_id: Optional[int | str] = None, only_expose_public_allowed_properties: Optional[bool] = True, ): + """ + Dispatch context used in the builder. + + :param request: The HTTP request from the view. + :param page: The page related to the dispatch. + :param workflow_action: The workflow action being executed, if any. + :param element: An optional element that triggered the dispatch. + :param offset: When we dispatch a list service, starts by that offset. + :param count: When we dispatch a list service returns that max amount of record. + :param record_id: If we want to narrow down the results of a list service to + only the record with this Id. + :param only_expose_public_allowed_properties: Determines whether only public + allowed properties should be exposed. Defaults to True. + """ + self.request = request self.page = page self.workflow_action = workflow_action self.element = element + self.only_record_id = only_record_id # Overrides the `request` GET offset/count values. self.offset = offset diff --git a/backend/src/baserow/contrib/builder/elements/mixins.py b/backend/src/baserow/contrib/builder/elements/mixins.py index 1b8f6856c..2bc98ca0c 100644 --- a/backend/src/baserow/contrib/builder/elements/mixins.py +++ b/backend/src/baserow/contrib/builder/elements/mixins.py @@ -517,9 +517,17 @@ class CollectionElementTypeMixin: .items() if any(options.values()) ] + if data_source and property_options: properties.setdefault(data_source.service_id, []).extend(property_options) + # We need the id for the element + if data_source and data_source.service_id: + service = data_source.service.specific + id_property = service.get_type().get_id_property(service) + if id_property not in properties.setdefault(service.id, []): + properties[service.id].append(id_property) + return properties diff --git a/backend/src/baserow/contrib/integrations/local_baserow/service_types.py b/backend/src/baserow/contrib/integrations/local_baserow/service_types.py index 55efbd031..a3ca3fe34 100644 --- a/backend/src/baserow/contrib/integrations/local_baserow/service_types.py +++ b/backend/src/baserow/contrib/integrations/local_baserow/service_types.py @@ -1082,6 +1082,9 @@ class LocalBaserowListRowsUserServiceType( # Ensure that only used fields are fetched from the database. queryset = queryset.only(*available_fields.intersection(only_field_names)) + if dispatch_context.only_record_id is not None: + queryset = queryset.filter(id=dispatch_context.only_record_id) + offset, count = dispatch_context.range(service) # We query one more row to be able to know if there is another page that can be diff --git a/backend/src/baserow/core/services/dispatch_context.py b/backend/src/baserow/core/services/dispatch_context.py index 92b675969..99181cbc8 100644 --- a/backend/src/baserow/core/services/dispatch_context.py +++ b/backend/src/baserow/core/services/dispatch_context.py @@ -10,6 +10,13 @@ from baserow.core.services.utils import ServiceAdhocRefinements class DispatchContext(RuntimeFormulaContext, ABC): own_properties = [] + """ + Should return the record id requested for the given service. Used by list + services to select only one record. For instance by the builder current record + data provider to narrow down the result of a list service. + """ + only_record_id = None + def __init__(self): self.cache = {} # can be used by data providers to save queries super().__init__() diff --git a/backend/src/baserow/core/services/registries.py b/backend/src/baserow/core/services/registries.py index 1dd48d867..706337fcb 100644 --- a/backend/src/baserow/core/services/registries.py +++ b/backend/src/baserow/core/services/registries.py @@ -68,6 +68,29 @@ class ServiceType( # `DISPATCH_WORKFLOW_ACTION` should be chosen. dispatch_type = None + def get_id_property(self, service: Service) -> str: + """ + Returns the property name that contains the unique `ID` of a row for this + service. + + :param service: the instance of the service. + :return: a string identifying the ID property name. + """ + + # Sane default + return "id" + + def get_name_property(self, service: Service) -> Optional[str]: + """ + We need the name of the records for some elements (like the record selector). + This method returns it depending on the service. + + :param service: the instance of the service. + :return: a string identifying the name property name. + """ + + return None + def prepare_values( self, values: Dict[str, Any], @@ -343,29 +366,6 @@ class ListServiceTypeMixin: returns_list = True - def get_id_property(self, service: Service) -> str: - """ - Returns the property name that contains the unique `ID` of a row for this - service. - - :param service: the instance of the service. - :return: a string identifying the ID property name. - """ - - # Sane default - return "id" - - def get_name_property(self, service: Service) -> Optional[str]: - """ - We need the name of the records for some elements (like the record selector). - This method returns it depending on the service. - - :param service: the instance of the service. - :return: a string identifying the name property name. - """ - - return None - @abstractmethod def get_record_names( self, diff --git a/backend/tests/baserow/contrib/builder/api/data_sources/test_data_source_views.py b/backend/tests/baserow/contrib/builder/api/data_sources/test_data_source_views.py index 502fb8cf7..f82ebac6a 100644 --- a/backend/tests/baserow/contrib/builder/api/data_sources/test_data_source_views.py +++ b/backend/tests/baserow/contrib/builder/api/data_sources/test_data_source_views.py @@ -1961,6 +1961,7 @@ def test_dispatch_data_sources_list_rows_with_elements( # Although this Data Source has 2 Fields/Columns, only one is # returned since only one field_id is used by the Table. f"field_{field_id}": getattr(row, f"field_{field_id}"), + "id": row.id, } ) @@ -2043,6 +2044,7 @@ def test_dispatch_data_sources_get_row_with_elements( assert response.json() == { str(data_source.id): { f"field_{field_id}": getattr(rows[db_row_id], f"field_{field_id}"), + "id": rows[db_row_id].id, } } @@ -2148,6 +2150,7 @@ def test_dispatch_data_sources_get_and_list_rows_with_elements( assert response.json() == { str(data_source_1.id): { f"field_{fields_1[0].id}": getattr(rows_1[0], f"field_{fields_1[0].id}"), + "id": rows_1[0].id, }, # Although this Data Source has 2 Fields/Columns, only one is returned # since only one field_id is used by the Table. @@ -2158,6 +2161,7 @@ def test_dispatch_data_sources_get_and_list_rows_with_elements( f"field_{fields_2[0].id}": getattr( rows_2[0], f"field_{fields_2[0].id}" ), + "id": rows_2[0].id, }, ], }, diff --git a/backend/tests/baserow/contrib/builder/api/data_sources/test_public_domain_views.py b/backend/tests/baserow/contrib/builder/api/data_sources/test_public_domain_views.py index e2d0b3799..9fa768cec 100644 --- a/backend/tests/baserow/contrib/builder/api/data_sources/test_public_domain_views.py +++ b/backend/tests/baserow/contrib/builder/api/data_sources/test_public_domain_views.py @@ -251,9 +251,7 @@ def test_dispatch_data_sources_list_rows_with_elements( ) expected_results = [ - { - f"field_{field_id}": getattr(row, f"field_{field_id}"), - } + {f"field_{field_id}": getattr(row, f"field_{field_id}"), "id": row.id} for row in data_source_fixture["rows"] ] @@ -332,6 +330,7 @@ def test_dispatch_data_sources_get_row_with_elements( assert response.json() == { str(data_source.id): { f"field_{field_id}": getattr(rows[db_row_id], f"field_{field_id}"), + "id": rows[db_row_id].id, } } @@ -431,6 +430,7 @@ def test_dispatch_data_sources_get_and_list_rows_with_elements( assert response.json() == { str(data_source_1.id): { f"field_{fields_1[0].id}": getattr(rows_1[0], f"field_{fields_1[0].id}"), + "id": rows_1[0].id, }, # Although this Data Source has 2 Fields/Columns, only one is returned # since only one field_id is used by the Table. @@ -441,6 +441,7 @@ def test_dispatch_data_sources_get_and_list_rows_with_elements( f"field_{fields_2[0].id}": getattr( rows_2[0], f"field_{fields_2[0].id}" ), + "id": rows_2[0].id, }, ], }, @@ -537,7 +538,9 @@ def test_dispatch_data_sources_list_rows_with_elements_and_role( # Field should only be visible if the user's role allows them # to see the data source fields. - expected_results.append({field_name: getattr(row, field_name)}) + expected_results.append( + {field_name: getattr(row, field_name), "id": row.id} + ) else: expected_results.append({}) diff --git a/backend/tests/baserow/contrib/builder/api/domains/test_domain_public_views.py b/backend/tests/baserow/contrib/builder/api/domains/test_domain_public_views.py index 83f253773..d5a008cd8 100644 --- a/backend/tests/baserow/contrib/builder/api/domains/test_domain_public_views.py +++ b/backend/tests/baserow/contrib/builder/api/domains/test_domain_public_views.py @@ -857,10 +857,12 @@ def test_public_dispatch_data_source_view_returns_all_fields( "has_next_page": False, "results": [ { + "id": rows[0].id, f"field_{fields[0].id}": "Paneer Tikka", f"field_{fields[1].id}": "5", }, { + "id": rows[1].id, f"field_{fields[0].id}": "Gobi Manchurian", f"field_{fields[1].id}": "8", }, @@ -1134,7 +1136,7 @@ def test_public_dispatch_data_sources_list_rows_with_elements_and_role( expected_results = [] for row in data_source_element_roles_fixture["rows"]: - result = {} + result = {"id": row.id} if expect_fields: # Field should only be visible if the user's role allows them # to see the data source fields. @@ -1318,15 +1320,17 @@ def test_public_dispatch_data_sources_list_rows_with_page_visibility_all( assert response.status_code == HTTP_200_OK + rows = data_source_element_roles_fixture["rows"] + if expect_fields: field_name = f"field_{field_id}" assert response.json() == { str(data_source.id): { "has_next_page": False, "results": [ - {field_name: "Apple"}, - {field_name: "Banana"}, - {field_name: "Cherry"}, + {field_name: "Apple", "id": rows[0].id}, + {field_name: "Banana", "id": rows[1].id}, + {field_name: "Cherry", "id": rows[2].id}, ], }, } @@ -1632,15 +1636,17 @@ def test_public_dispatch_data_sources_list_rows_with_page_visibility_logged_in( assert response.status_code == HTTP_200_OK + rows = data_source_element_roles_fixture["rows"] + if expect_fields: field_name = f"field_{field_id}" assert response.json() == { str(data_source.id): { "has_next_page": False, "results": [ - {field_name: "Apple"}, - {field_name: "Banana"}, - {field_name: "Cherry"}, + {field_name: "Apple", "id": rows[0].id}, + {field_name: "Banana", "id": rows[1].id}, + {field_name: "Cherry", "id": rows[2].id}, ], }, } diff --git a/backend/tests/baserow/contrib/builder/api/workflow_actions/test_workflow_actions_views.py b/backend/tests/baserow/contrib/builder/api/workflow_actions/test_workflow_actions_views.py index 360677696..862e78110 100644 --- a/backend/tests/baserow/contrib/builder/api/workflow_actions/test_workflow_actions_views.py +++ b/backend/tests/baserow/contrib/builder/api/workflow_actions/test_workflow_actions_views.py @@ -1,4 +1,3 @@ -import json from unittest.mock import patch from django.db import transaction @@ -677,7 +676,7 @@ def test_dispatch_local_baserow_upsert_row_workflow_action_with_current_record( } response = api_client.post( url, - {"current_record": 123}, + {"current_record": {"index": 123, "record_id": 123}}, format="json", HTTP_AUTHORIZATION=f"JWT {token}", ) @@ -689,7 +688,7 @@ def test_dispatch_local_baserow_upsert_row_workflow_action_with_current_record( @pytest.mark.django_db(transaction=True) -def test_dispatch_local_baserow_upsert_row_workflow_action_with_adhoc_refinements( +def test_dispatch_local_baserow_upsert_row_workflow_action_with_unmatching_index_and_record_id( api_client, data_fixture ): with transaction.atomic(): @@ -705,7 +704,7 @@ def test_dispatch_local_baserow_upsert_row_workflow_action_with_adhoc_refinement ], ) field = table.field_set.get() - RowHandler().create_rows( + rows = RowHandler().create_rows( user, table, rows_values=[ @@ -766,17 +765,6 @@ def test_dispatch_local_baserow_upsert_row_workflow_action_with_adhoc_refinement kwargs={"workflow_action_id": workflow_action.id}, ) - advanced_filters = { - "filter_type": "OR", - "filters": [ - { - "field": field.id, - "type": "contains", - "value": "construction", - } - ], - } - with patch( "baserow.contrib.builder.handler.get_builder_used_property_names" ) as used_properties_mock: @@ -786,36 +774,34 @@ def test_dispatch_local_baserow_upsert_row_workflow_action_with_adhoc_refinement } model = table.get_model() - # 1. The filters reduce it to 3 results. - # 2. The search query reduces it to 2 results. - # 3. We sort alphabetically, and dispatch the first one, - # "Complex Construction Design". - url_with_querystring = ( - f"{url}?filters={json.dumps(advanced_filters)}" - f"&search_query=design&order_by={field.db_column}" - ) - - # Dispatch at index=0, this will be "Complex Construction Design". + # Dispatch at index=0 but row 3 id, this will be "Complex Construction Design". response = api_client.post( - url_with_querystring, - {"current_record": 0, "data_source": {"element": table_element.id}}, + url, + { + "current_record": {"index": 0, "record_id": rows[2].id}, + "data_source": {"element": table_element.id}, + }, format="json", HTTP_AUTHORIZATION=f"JWT {token}", ) assert response.status_code == HTTP_200_OK - row3 = model.objects.get(pk=3) - assert getattr(row3, f"field_{field.id}") == "Updated row 3" + row3 = model.objects.get(pk=rows[2].id) + assert getattr(row3, f"field_{field.id}") == f"Updated row {rows[2].id}" - # Dispatch at index=0, this will now be "Simple Construction Design". + # Dispatch at index=0 but row 4 id, + # this will now be "Simple Construction Design". response = api_client.post( - url_with_querystring, - {"current_record": 0, "data_source": {"element": table_element.id}}, + url, + { + "current_record": {"index": 0, "record_id": rows[3].id}, + "data_source": {"element": table_element.id}, + }, format="json", HTTP_AUTHORIZATION=f"JWT {token}", ) assert response.status_code == HTTP_200_OK - row4 = model.objects.get(pk=4) - assert getattr(row4, f"field_{field.id}") == "Updated row 4" + row4 = model.objects.get(pk=rows[3].id) + assert getattr(row4, f"field_{field.id}") == f"Updated row {rows[3].id}" @pytest.mark.django_db diff --git a/backend/tests/baserow/contrib/builder/data_providers/test_data_provider_types.py b/backend/tests/baserow/contrib/builder/data_providers/test_data_provider_types.py index c09163296..504bdb915 100644 --- a/backend/tests/baserow/contrib/builder/data_providers/test_data_provider_types.py +++ b/backend/tests/baserow/contrib/builder/data_providers/test_data_provider_types.py @@ -1208,21 +1208,15 @@ def test_current_record_provider_get_data_chunk_without_record_index(data_fixtur def test_current_record_provider_get_data_chunk_for_idx(): current_record_provider = CurrentRecordDataProviderType() fake_request = HttpRequest() - fake_request.data = {"current_record": 123} + fake_request.data = {"current_record": {"index": 123, "record_id": 123}} dispatch_context = BuilderDispatchContext(fake_request, None) assert current_record_provider.get_data_chunk(dispatch_context, ["__idx__"]) == 123 @pytest.mark.django_db def test_current_record_provider_get_data_chunk(data_fixture): - current_record_provider = CurrentRecordDataProviderType() - user, token = data_fixture.create_user_and_token() - fake_request = HttpRequest() - fake_request.user = user - fake_request.data = {"current_record": 0} - table, fields, rows = data_fixture.build_table( user=user, columns=[ @@ -1252,10 +1246,16 @@ def test_current_record_provider_get_data_chunk(data_fixture): page=page, element=button_element, event=EventTypes.CLICK, user=user ) + fake_request = HttpRequest() + fake_request.user = user + fake_request.data = {"current_record": {"index": 0, "record_id": rows[0].id}} + dispatch_context = BuilderDispatchContext( fake_request, page, workflow_action, only_expose_public_allowed_properties=False ) + current_record_provider = CurrentRecordDataProviderType() + assert ( current_record_provider.get_data_chunk(dispatch_context, [field.db_column]) == "Badger" diff --git a/backend/tests/baserow/contrib/builder/elements/test_repeat_element_type.py b/backend/tests/baserow/contrib/builder/elements/test_repeat_element_type.py index ef97d03b0..8d8099130 100644 --- a/backend/tests/baserow/contrib/builder/elements/test_repeat_element_type.py +++ b/backend/tests/baserow/contrib/builder/elements/test_repeat_element_type.py @@ -208,7 +208,7 @@ def test_extract_properties_includes_schema_property_for_nested_collection( ) properties = RepeatElementType().extract_properties(parent_repeat) - assert properties == {} + assert properties == {data_source.service_id: ["id"]} # Create a child Repeat with a schema_property child_repeat = data_fixture.create_builder_repeat_element( @@ -223,8 +223,10 @@ def test_extract_properties_includes_schema_property_for_nested_collection( properties = RepeatElementType().extract_properties(child_repeat, **formula_context) - # We expect that the schema_property field ID to be present - assert properties == {data_source.service_id: [f"field_{multiple_select_field.id}"]} + # We expect that the schema_property field to be present and the ID + assert properties == { + data_source.service_id: [f"field_{multiple_select_field.id}", "id"] + } @pytest.mark.django_db @@ -283,4 +285,6 @@ def test_extract_properties_includes_schema_property_for_single_row( ) properties = RepeatElementType().extract_properties(repeat) - assert properties == {data_source.service_id: [f"field_{multiple_select_field.id}"]} + assert properties == { + data_source.service_id: [f"field_{multiple_select_field.id}", "id"] + } diff --git a/backend/tests/baserow/contrib/builder/test_formula_property_extractor.py b/backend/tests/baserow/contrib/builder/test_formula_property_extractor.py index ef24d290a..207d5bded 100644 --- a/backend/tests/baserow/contrib/builder/test_formula_property_extractor.py +++ b/backend/tests/baserow/contrib/builder/test_formula_property_extractor.py @@ -110,10 +110,10 @@ def test_get_builder_used_property_names_returns_all_property_names(data_fixture assert list(results) == unordered(["all", "external", "internal"]) assert results["all"][data_source.service_id] == unordered( - [f"field_{field.id}" for field in fields] + [f"field_{field.id}" for field in fields] + ["id"] ) assert results["external"][data_source.service_id] == unordered( - [f"field_{field.id}" for field in fields] + [f"field_{field.id}" for field in fields] + ["id"] ) assert results["internal"] == {} @@ -169,10 +169,10 @@ def test_get_builder_used_property_names_returns_some_property_names(data_fixtur # only one property, ensure that specific property is the only one returned. assert results == { "all": { - data_source.service_id: [f"field_{fields[0].id}"], + data_source.service_id: [f"field_{fields[0].id}", "id"], }, "external": { - data_source.service_id: [f"field_{fields[0].id}"], + data_source.service_id: [f"field_{fields[0].id}", "id"], }, "internal": {}, } @@ -997,6 +997,7 @@ def test_get_builder_used_property_names_returns_merged_property_names_integrati "all": { data_source.service_id: sorted( [ + "id", f"field_{fields[0].id}", f"field_{fields[2].id}", ] @@ -1019,6 +1020,7 @@ def test_get_builder_used_property_names_returns_merged_property_names_integrati "external": { data_source.service_id: [ f"field_{fields[0].id}", # From heading_element_1 + "id", ], data_source_2.service_id: [ f"field_{fields[2].id}" diff --git a/web-frontend/modules/builder/components/elements/baseComponents/ABTable.vue b/web-frontend/modules/builder/components/elements/baseComponents/ABTable.vue index 2b657c740..fdc54e5db 100644 --- a/web-frontend/modules/builder/components/elements/baseComponents/ABTable.vue +++ b/web-frontend/modules/builder/components/elements/baseComponents/ABTable.vue @@ -10,12 +10,13 @@ <slot name="field-name" :field="field">{{ field.name }}</slot> </th> </template> - <template #cell-content="{ rowIndex, value, field }"> + <template #cell-content="{ rowIndex, value, field, row }"> <slot name="cell-content" :value="value" :field="field" :row-index="rowIndex" + :row="row" > <td :key="field.id" class="ab-table__cell"> <div class="ab-table__cell-content"> diff --git a/web-frontend/modules/builder/components/elements/components/BaserowTable.vue b/web-frontend/modules/builder/components/elements/components/BaserowTable.vue index 8f70061a3..4d57ad968 100644 --- a/web-frontend/modules/builder/components/elements/components/BaserowTable.vue +++ b/web-frontend/modules/builder/components/elements/components/BaserowTable.vue @@ -25,6 +25,7 @@ :value="row[field.name]" :field="field" :row-index="index" + :row="row" > <td :key="field.id" class="baserow-table__cell"> {{ row[field.name] }} @@ -55,6 +56,7 @@ :value="row[field.name]" :field="field" :row-index="rowIndex" + :row="row" > <td class="baserow-table__cell" diff --git a/web-frontend/modules/builder/components/elements/components/RepeatElement.vue b/web-frontend/modules/builder/components/elements/components/RepeatElement.vue index 93d0481dd..4f8e4cce7 100644 --- a/web-frontend/modules/builder/components/elements/components/RepeatElement.vue +++ b/web-frontend/modules/builder/components/elements/components/RepeatElement.vue @@ -28,12 +28,13 @@ v-if="index === 0 && isEditMode" :key="`${child.id}-${index}`" :element="child" - :application-context-additions="{ - recordIndexPath: [ - ...applicationContext.recordIndexPath, - index, - ], - }" + :application-context-additions=" + getPerRecordApplicationContextAddition({ + applicationContext, + row: content, + rowIndex: index, + }) + " @move="$emit('move', $event)" /> <!-- Other iterations are not editable --> @@ -44,12 +45,13 @@ :key="`${child.id}_${index}`" :element="child" :force-mode="isEditMode ? 'public' : mode" - :application-context-additions="{ - recordIndexPath: [ - ...applicationContext.recordIndexPath, - index, - ], - }" + :application-context-additions=" + getPerRecordApplicationContextAddition({ + applicationContext, + row: content, + rowIndex: index, + }) + " :class="{ 'repeat-element__preview': index > 0 && isEditMode, }" diff --git a/web-frontend/modules/builder/components/elements/components/TableElement.vue b/web-frontend/modules/builder/components/elements/components/TableElement.vue index 1c43522e1..89279de4a 100644 --- a/web-frontend/modules/builder/components/elements/components/TableElement.vue +++ b/web-frontend/modules/builder/components/elements/components/TableElement.vue @@ -13,7 +13,7 @@ :style="getStyleOverride('table')" :orientation="orientation" > - <template #cell-content="{ rowIndex, field, value }"> + <template #cell-content="{ rowIndex, field, value, row }"> <!-- -- We force-self-alignment to `auto` here to prevent some self-positioning -- like in buttons or links. we want to position the content through the table @@ -34,14 +34,14 @@ :is="collectionFieldTypes[field.type].component" :element="element" :field="field" - :application-context-additions="{ - recordIndexPath: [ - ...applicationContext.recordIndexPath, + :application-context-additions=" + getPerRecordApplicationContextAddition({ + applicationContext, + row, rowIndex, - ], - field, - dispatchRefinements: adhocRefinements, - }" + field, + }) + " v-bind="value" /> </div> @@ -117,6 +117,7 @@ export default { }) ) newRow.__id__ = uuid() + newRow.__recordId__ = row.__recordId__ return newRow }) }, diff --git a/web-frontend/modules/builder/dataProviderTypes.js b/web-frontend/modules/builder/dataProviderTypes.js index 2ec8a58d0..6840709f8 100644 --- a/web-frontend/modules/builder/dataProviderTypes.js +++ b/web-frontend/modules/builder/dataProviderTypes.js @@ -384,7 +384,10 @@ export class CurrentRecordDataProviderType extends DataProviderType { } getActionDispatchContext(applicationContext) { - return applicationContext.recordIndexPath.at(-1) + return { + record_id: applicationContext.recordId, + index: applicationContext.recordIndexPath.at(-1), + } } getDataChunk(applicationContext, path) { diff --git a/web-frontend/modules/builder/eventTypes.js b/web-frontend/modules/builder/eventTypes.js index 7c6f63971..09d871366 100644 --- a/web-frontend/modules/builder/eventTypes.js +++ b/web-frontend/modules/builder/eventTypes.js @@ -64,19 +64,6 @@ export class Event { ) } - // If we're firing a workflow action, and the collection element it's associated - // with is currently being filtered, we must forward this on to the workflow - // action dispatch so that the backend fires at the correct current_record. - if ( - Object.prototype.hasOwnProperty.call( - applicationContext, - 'dispatchRefinements' - ) - ) { - workflowActionContext.dispatchRefinements = - applicationContext.dispatchRefinements - } - const localResolveFormula = (formula) => { const formulaFunctions = { get: (name) => { diff --git a/web-frontend/modules/builder/mixins/collectionElement.js b/web-frontend/modules/builder/mixins/collectionElement.js index c70be12f9..28e5b0acb 100644 --- a/web-frontend/modules/builder/mixins/collectionElement.js +++ b/web-frontend/modules/builder/mixins/collectionElement.js @@ -176,5 +176,22 @@ export default { this.contentFetchEnabled = false } }, + getPerRecordApplicationContextAddition({ + applicationContext, + row, + rowIndex, + field = null, + }) { + const newApplicationContext = { + recordIndexPath: [...applicationContext.recordIndexPath, rowIndex], + } + if (field) { + newApplicationContext.field = field + } + if (this.element.data_source_id) { + newApplicationContext.recordId = row.__recordId__ + } + return newApplicationContext + }, }, } diff --git a/web-frontend/modules/builder/services/workflowAction.js b/web-frontend/modules/builder/services/workflowAction.js index d891fd143..ae7930b80 100644 --- a/web-frontend/modules/builder/services/workflowAction.js +++ b/web-frontend/modules/builder/services/workflowAction.js @@ -1,5 +1,3 @@ -import { prepareDispatchParams } from '@baserow/modules/builder/utils/params' - export default (client) => { return { create(pageId, workflowActionType, eventType, configuration = null) { @@ -35,12 +33,10 @@ export default (client) => { payload ) }, - dispatch(workflowActionId, data, dispatchRefinements) { - const params = prepareDispatchParams(dispatchRefinements) + dispatch(workflowActionId, data) { return client.post( `builder/workflow_action/${workflowActionId}/dispatch/`, - data, - { params } + data ) }, } diff --git a/web-frontend/modules/builder/store/elementContent.js b/web-frontend/modules/builder/store/elementContent.js index dc98fcc13..68739d5ea 100644 --- a/web-frontend/modules/builder/store/elementContent.js +++ b/web-frontend/modules/builder/store/elementContent.js @@ -246,7 +246,10 @@ const actions = { // using the results key and set the range for future paging. commit('SET_CONTENT', { element, - value: data.results, + value: data.results.map((row) => ({ + ...row, + __recordId__: row[serviceType.getIdProperty(service, row)], + })), range, }) } else { diff --git a/web-frontend/modules/builder/store/workflowAction.js b/web-frontend/modules/builder/store/workflowAction.js index 39fb18762..844214804 100644 --- a/web-frontend/modules/builder/store/workflowAction.js +++ b/web-frontend/modules/builder/store/workflowAction.js @@ -200,15 +200,10 @@ const actions = { updateContext.promiseResolve = resolve }) }, - async dispatchAction( - { dispatch }, - { workflowActionId, workflowActionContext, data } - ) { - const { dispatchRefinements = {} } = workflowActionContext + async dispatchAction({ dispatch }, { workflowActionId, data }) { const { data: result } = await WorkflowActionService(this.$client).dispatch( workflowActionId, - data, - dispatchRefinements + data ) return result }, diff --git a/web-frontend/modules/builder/workflowActionTypes.js b/web-frontend/modules/builder/workflowActionTypes.js index 00e76001e..74b86f7d8 100644 --- a/web-frontend/modules/builder/workflowActionTypes.js +++ b/web-frontend/modules/builder/workflowActionTypes.js @@ -180,10 +180,8 @@ export class RefreshDataSourceWorkflowActionType extends WorkflowActionType { export class WorkflowActionServiceType extends WorkflowActionType { execute({ workflowAction: { id }, applicationContext, resolveFormula }) { - const { workflowActionContext } = applicationContext return this.app.store.dispatch('workflowAction/dispatchAction', { workflowActionId: id, - workflowActionContext, data: DataProviderType.getAllActionDispatchContext( this.app.$registry.getAll('builderDataProvider'), applicationContext diff --git a/web-frontend/modules/core/serviceTypes.js b/web-frontend/modules/core/serviceTypes.js index 773765080..6b65ccca2 100644 --- a/web-frontend/modules/core/serviceTypes.js +++ b/web-frontend/modules/core/serviceTypes.js @@ -35,6 +35,22 @@ export class ServiceType extends Registerable { return false } + /** + * In a service which returns a list, this method is used to + * return the name of the given record. + */ + getRecordName(service, record) { + throw new Error('Must be set on the type.') + } + + /** + * In a service which returns a list, this method is used to + * return the id of the given record. + */ + getIdProperty(service, record) { + throw new Error('Must be set on the type.') + } + /** * The maximum number of records that can be returned by this service */ diff --git a/web-frontend/modules/integrations/serviceTypes.js b/web-frontend/modules/integrations/serviceTypes.js index 64d0f5074..ba72332b4 100644 --- a/web-frontend/modules/integrations/serviceTypes.js +++ b/web-frontend/modules/integrations/serviceTypes.js @@ -38,12 +38,8 @@ export class LocalBaserowTableServiceType extends ServiceType { return service.context_data_schema } - /** - * In a Local Baserow service which returns a list, this method is used to - * return the name of the given record. - */ - getRecordName(service, record) { - return '' + getIdProperty(service, record) { + return 'id' } /**