From 0c27a318513c74ec513fc69fd0f6594ab2f722ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Pardou?= <571533+jrmi@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:08:27 +0100 Subject: [PATCH 1/2] Fix formula migration data source filter compat on template import (#4125) * Fix formula migration data source filter compat * Remove formula error log to prevent noise --- .../integrations/local_baserow/mixins.py | 44 ++++++----- backend/src/baserow/core/formula/types.py | 24 +++++- .../integrations/local_baserow/test_mixins.py | 74 ++++++++++++++++++- web-frontend/modules/core/formula/index.js | 1 - 4 files changed, 118 insertions(+), 25 deletions(-) diff --git a/backend/src/baserow/contrib/integrations/local_baserow/mixins.py b/backend/src/baserow/contrib/integrations/local_baserow/mixins.py index 5ab46cba4a..4c4de6f253 100644 --- a/backend/src/baserow/contrib/integrations/local_baserow/mixins.py +++ b/backend/src/baserow/contrib/integrations/local_baserow/mixins.py @@ -130,26 +130,32 @@ def deserialize_filters(self, value, id_mapping): :return: the deserialized version for the filter. """ - return [ - { - **f, - "field_id": ( - id_mapping["database_fields"][f["field_id"]] - if "database_fields" in id_mapping - else f["field_id"] - ), - "value": ( - id_mapping["database_field_select_options"].get( - int(f["value"]["formula"]), f["value"]["formula"] + result = [] + + for f in value: + formula = BaserowFormulaObject.to_formula(f["value"]) + field_id = id_mapping.get("database_fields", {}).get( + f["field_id"], f["field_id"] + ) + + if ( + f["value_is_formula"] + or not formula["formula"].isdigit() + or "database_field_select_options" not in id_mapping + ): + val = formula + else: + val = BaserowFormulaObject.create( + formula=str( + id_mapping["database_field_select_options"].get( + int(formula["formula"]), formula["formula"] + ) ) - if "database_field_select_options" in id_mapping - and f["value"]["formula"].isdigit() - and not f["value_is_formula"] - else f["value"]["formula"] - ), - } - for f in value - ] + ) + + result.append({**f, "field_id": field_id, "value": val}) + + return result def create_instance_from_serialized( self, diff --git a/backend/src/baserow/core/formula/types.py b/backend/src/baserow/core/formula/types.py index 205664a767..6d9c11cf4e 100644 --- a/backend/src/baserow/core/formula/types.py +++ b/backend/src/baserow/core/formula/types.py @@ -78,9 +78,29 @@ def execute(self, context: FormulaContext, args: FormulaArgs) -> Any: class BaserowFormulaObject(TypedDict): - version: str - mode: BaserowFormulaMode formula: BaserowFormula + mode: BaserowFormulaMode + version: str + + @classmethod + def create( + cls, + formula: str = "", + mode: BaserowFormulaMode = BASEROW_FORMULA_MODE_SIMPLE, + version: str = "0.1", + ) -> "BaserowFormulaObject": + return BaserowFormulaObject(formula=formula, mode=mode, version=version) + + @classmethod + def to_formula(cls, value) -> "BaserowFormulaObject": + """ + Return a formula object even if it was a string. + """ + + if isinstance(value, dict): + return value + else: + return cls.create(formula=value) class BaserowFormulaMinified(TypedDict): diff --git a/backend/tests/baserow/contrib/integrations/local_baserow/test_mixins.py b/backend/tests/baserow/contrib/integrations/local_baserow/test_mixins.py index 60e474aecc..e2b8c3cd8f 100644 --- a/backend/tests/baserow/contrib/integrations/local_baserow/test_mixins.py +++ b/backend/tests/baserow/contrib/integrations/local_baserow/test_mixins.py @@ -132,14 +132,23 @@ def test_local_baserow_table_service_filterable_mixin_import_export(data_fixture page=page, table=table, integration=integration ) data_fixture.create_local_baserow_table_service_filter( - service=data_source.service, field=text_field, value="'foobar'", order=0 + service=data_source.service, + field=text_field, + value="'foobar'", + order=0, + value_is_formula=True, ) data_fixture.create_local_baserow_table_service_filter( - service=data_source.service, field=text_field, value="123", order=1 + service=data_source.service, + field=text_field, + value="123", + order=1, + value_is_formula=True, ) data_fixture.create_local_baserow_table_service_filter( service=data_source.service, field=single_select_field, + value_is_formula=False, value=str(single_option.id), order=2, ) @@ -181,7 +190,11 @@ def test_local_baserow_table_service_filterable_mixin_import_export(data_fixture imported_page = imported_builder.visible_pages.get() imported_datasource = imported_page.datasource_set.get() imported_filters = [ - {"field_id": sf.field_id, "value": sf.value} + { + "field_id": sf.field_id, + "value": sf.value, + "value_is_formula": sf.value_is_formula, + } for sf in imported_datasource.service.service_filters.all() ] @@ -189,10 +202,12 @@ def test_local_baserow_table_service_filterable_mixin_import_export(data_fixture { "field_id": imported_text_field.id, "value": {"mode": "simple", "version": "0.1", "formula": "'foobar'"}, + "value_is_formula": True, }, { "field_id": imported_text_field.id, "value": {"mode": "simple", "version": "0.1", "formula": "123"}, + "value_is_formula": True, }, { "field_id": imported_single_select_field.id, @@ -201,6 +216,59 @@ def test_local_baserow_table_service_filterable_mixin_import_export(data_fixture "version": "0.1", "formula": str(imported_select_option.id), }, + "value_is_formula": False, + }, + ] + + +@pytest.mark.django_db() +def test_local_baserow_table_service_filterable_mixin_compat(): + mixin = LocalBaserowTableServiceFilterableMixin() + + id_mapping = { + "database_field_select_options": {1: 42}, + "database_fields": {1: 41, 2: 42, 3: 43, 4: 44}, + } + + value_to_test = [ + {"field_id": 1, "value": "'Formula as string'", "value_is_formula": True}, + {"field_id": 2, "value": "1", "value_is_formula": False}, + {"field_id": 3, "value": "", "value_is_formula": True}, + { + "field_id": 4, + "value": {"mode": "simple", "version": "0.1", "formula": "'foobar'"}, + "value_is_formula": True, + }, + ] + + result = json.loads( + json.dumps(mixin.deserialize_filters(value_to_test, id_mapping)) + ) + + assert result == [ + { + "field_id": 41, + "value": { + "formula": "'Formula as string'", + "mode": "simple", + "version": "0.1", + }, + "value_is_formula": True, + }, + { + "field_id": 42, + "value": {"formula": "42", "mode": "simple", "version": "0.1"}, + "value_is_formula": False, + }, + { + "field_id": 43, + "value": {"formula": "", "mode": "simple", "version": "0.1"}, + "value_is_formula": True, + }, + { + "field_id": 44, + "value": {"mode": "simple", "version": "0.1", "formula": "'foobar'"}, + "value_is_formula": True, }, ] diff --git a/web-frontend/modules/core/formula/index.js b/web-frontend/modules/core/formula/index.js index 007a4e25fe..8a0cdfdd17 100644 --- a/web-frontend/modules/core/formula/index.js +++ b/web-frontend/modules/core/formula/index.js @@ -23,7 +23,6 @@ export const resolveFormula = ( const tree = parseBaserowFormula(formulaCtx.formula) return new JavascriptExecutor(functions, RuntimeFormulaContext).visit(tree) } catch (err) { - console.log('Error while parsing or executing formula:', err) return '' } } From 523b5d5676993b01b1d27e95d3cf7c26077ab723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Pardou?= <571533+jrmi@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:09:13 +0100 Subject: [PATCH 2/2] Add feature flag to advanced formulas checkbox (#4127) * Add feature flag to advanced formulas checkbox * Revert css change for advanced formula --- .../modules/core/assets/scss/components/form.scss | 2 +- .../core/components/formula/FormulaInputField.vue | 9 ++++++++- web-frontend/modules/core/plugins/featureFlags.js | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/web-frontend/modules/core/assets/scss/components/form.scss b/web-frontend/modules/core/assets/scss/components/form.scss index 76b561fdb6..e12ce36b07 100644 --- a/web-frontend/modules/core/assets/scss/components/form.scss +++ b/web-frontend/modules/core/assets/scss/components/form.scss @@ -33,7 +33,7 @@ .control__elements--flex { display: flex; - align-items: flex-start; + align-items: center; gap: 5px; } diff --git a/web-frontend/modules/core/components/formula/FormulaInputField.vue b/web-frontend/modules/core/components/formula/FormulaInputField.vue index 8abb09db6a..3e8500048e 100644 --- a/web-frontend/modules/core/components/formula/FormulaInputField.vue +++ b/web-frontend/modules/core/components/formula/FormulaInputField.vue @@ -31,7 +31,7 @@ @input="emitAdvancedChange" /> -