From bc53b3684f4fa90f3d4a3e4e4f39e009fea58105 Mon Sep 17 00:00:00 2001 From: Nirmit Damania Date: Sat, 28 Mar 2026 17:13:38 -0400 Subject: [PATCH 1/4] fix update entity schema --- liminal/entity_schemas/operations.py | 8 +-- liminal/entity_schemas/tag_schema_models.py | 58 +++++++++++---------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/liminal/entity_schemas/operations.py b/liminal/entity_schemas/operations.py index 8f068bc..d6163a6 100644 --- a/liminal/entity_schemas/operations.py +++ b/liminal/entity_schemas/operations.py @@ -18,7 +18,6 @@ from liminal.entity_schemas.tag_schema_models import ( CreateTagSchemaFieldModel, TagSchemaModel, - UpdateTagSchemaModel, ) from liminal.entity_schemas.utils import ( convert_tag_schema_field_to_field_properties, @@ -217,11 +216,12 @@ def __init__( def execute(self, benchling_service: BenchlingService) -> dict[str, Any]: tag_schema = self._validate(benchling_service) - updated_tag_schema = tag_schema.update_schema_props( + update = tag_schema.to_update_tag_schema_model( self.update_props.model_dump(exclude_unset=True) ) - update = UpdateTagSchemaModel(**updated_tag_schema.model_dump()) - return update_tag_schema(benchling_service, tag_schema.id, update.model_dump()) + return update_tag_schema( + benchling_service, tag_schema.id, update.model_dump(exclude_unset=True) + ) def describe_operation(self) -> str: return f"{self.wh_schema_name}: Updating schema properties to: {str(self.update_props)}." diff --git a/liminal/entity_schemas/tag_schema_models.py b/liminal/entity_schemas/tag_schema_models.py index 9351baa..99e3a0e 100644 --- a/liminal/entity_schemas/tag_schema_models.py +++ b/liminal/entity_schemas/tag_schema_models.py @@ -473,28 +473,39 @@ def get_internal_name_template_parts(self) -> list[NameTemplatePart]: part.to_name_template_part(self.fields) for part in self.nameTemplateParts ] - def update_schema_props(self, update_diff: dict[str, Any]) -> TagSchemaModel: - """Updates the schema properties given the schema properties defined in code.""" + def to_update_tag_schema_model( + self, update_diff: dict[str, Any] + ) -> UpdateTagSchemaModel: + """Converts the tag schema model to an UpdateTagSchemaModel, with only the updated properties.""" update_diff_names = list(update_diff.keys()) update_props = BaseSchemaProperties(**update_diff) + update_tag_schema_model = UpdateTagSchemaModel() if update_props.entity_type and "entity_type" in update_diff_names: folder_item_type, sequence_type = convert_entity_type_to_api_entity_type( update_props.entity_type ) - self.folderItemType = folder_item_type - self.sequenceType = sequence_type + update_tag_schema_model.folderItemType = folder_item_type + update_tag_schema_model.sequenceType = sequence_type if "naming_strategies" in update_diff_names: - self.labelingStrategies = [o.value for o in update_props.naming_strategies] + update_tag_schema_model.labelingStrategies = [ + o.value for o in update_props.naming_strategies + ] if "mixture_schema_config" in update_diff_names: - self.mixtureSchemaConfig = update_props.mixture_schema_config + update_tag_schema_model.mixtureSchemaConfig = ( + update_props.mixture_schema_config + ) if "use_registry_id_as_label" in update_diff_names: - self.useOrganizationCollectionAliasForDisplayLabel = ( + update_tag_schema_model.useOrganizationCollectionAliasForDisplayLabel = ( update_props.use_registry_id_as_label ) if "include_registry_id_in_chips" in update_diff_names: - self.includeRegistryIdInChips = update_props.include_registry_id_in_chips + update_tag_schema_model.includeRegistryIdInChips = ( + update_props.include_registry_id_in_chips + ) if "show_bases_in_expanded_view" in update_diff_names: - self.showResidues = update_props.show_bases_in_expanded_view + update_tag_schema_model.showResidues = ( + update_props.show_bases_in_expanded_view + ) if "constraint_fields" in update_diff_names: if update_props.constraint_fields: @@ -516,25 +527,18 @@ def update_schema_props(self, update_diff: dict[str, Any]) -> TagSchemaModel: for f in self.fields if f.systemName in update_props.constraint_fields ] - self.constraint = TagSchemaConstraint.from_constraint_fields( - constraint_fields, sequence_constraint + update_tag_schema_model.constraint = ( + TagSchemaConstraint.from_constraint_fields( + constraint_fields, sequence_constraint + ) ) - else: - self.constraint = None - - self.prefix = ( - update_props.prefix if "prefix" in update_diff_names else self.prefix - ) - - set_sql_identifier = ( - update_props.warehouse_name - if "warehouse_name" in update_diff_names - else self.sqlIdentifier - ) - assert type(set_sql_identifier) is str - self.sqlIdentifier = set_sql_identifier - self.name = update_props.name if "name" in update_diff_names else self.name - return self + if "prefix" in update_diff_names: + update_tag_schema_model.prefix = update_props.prefix + if "warehouse_name" in update_diff_names: + update_tag_schema_model.sqlIdentifier = update_props.warehouse_name + if "name" in update_diff_names: + update_tag_schema_model.name = update_props.name + return update_tag_schema_model def update_name_template( self, update_name_template: BaseNameTemplate From 22ed451aa629b77dd2ba348f1de3bb79f0ca5d4d Mon Sep 17 00:00:00 2001 From: Nirmit Damania Date: Sat, 28 Mar 2026 17:30:24 -0400 Subject: [PATCH 2/4] add back set to None --- liminal/entity_schemas/tag_schema_models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/liminal/entity_schemas/tag_schema_models.py b/liminal/entity_schemas/tag_schema_models.py index 99e3a0e..468a5de 100644 --- a/liminal/entity_schemas/tag_schema_models.py +++ b/liminal/entity_schemas/tag_schema_models.py @@ -532,6 +532,8 @@ def to_update_tag_schema_model( constraint_fields, sequence_constraint ) ) + else: + update_tag_schema_model.constraint = None if "prefix" in update_diff_names: update_tag_schema_model.prefix = update_props.prefix if "warehouse_name" in update_diff_names: From ee606a645cecb0837e24268d066064ef421e5ff4 Mon Sep 17 00:00:00 2001 From: Nirmit Damania Date: Sun, 29 Mar 2026 09:30:34 -0400 Subject: [PATCH 3/4] fix constraint fields updating --- liminal/entity_schemas/compare.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/liminal/entity_schemas/compare.py b/liminal/entity_schemas/compare.py index 1d43d0c..fdf3d1c 100644 --- a/liminal/entity_schemas/compare.py +++ b/liminal/entity_schemas/compare.py @@ -232,6 +232,27 @@ def compare_entity_schemas( ) ) if benchling_schema_props != model.__schema_properties__: + needs_constraint_clear = ( + len(benchling_schema_props.constraint_fields) > 0 + and len(model.__schema_properties__.constraint_fields) > 0 + ) + if needs_constraint_clear: + ops.append( + CompareOperation( + op=UpdateEntitySchema( + model.__schema_properties__.warehouse_name, + BaseSchemaProperties(constraint_fields=set()), + ), + reverse_op=UpdateEntitySchema( + model.__schema_properties__.warehouse_name, + BaseSchemaProperties( + **model.__schema_properties__.merge( + benchling_schema_props + ) + ), + ), + ) + ) ops.append( CompareOperation( op=UpdateEntitySchema( @@ -244,7 +265,9 @@ def compare_entity_schemas( ), reverse_op=UpdateEntitySchema( model.__schema_properties__.warehouse_name, - BaseSchemaProperties( + BaseSchemaProperties(constraint_fields=set()) + if needs_constraint_clear + else BaseSchemaProperties( **model.__schema_properties__.merge( benchling_schema_props ) From 85e7823d8d2922db484fb7fbe4897cb34bbe8fc9 Mon Sep 17 00:00:00 2001 From: Nirmit Damania Date: Sun, 29 Mar 2026 09:46:09 -0400 Subject: [PATCH 4/4] fix tests --- liminal/entity_schemas/compare.py | 5 +++-- liminal/tests/test_entity_schema_compare.py | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/liminal/entity_schemas/compare.py b/liminal/entity_schemas/compare.py index fdf3d1c..b20c1d3 100644 --- a/liminal/entity_schemas/compare.py +++ b/liminal/entity_schemas/compare.py @@ -233,8 +233,9 @@ def compare_entity_schemas( ) if benchling_schema_props != model.__schema_properties__: needs_constraint_clear = ( - len(benchling_schema_props.constraint_fields) > 0 - and len(model.__schema_properties__.constraint_fields) > 0 + benchling_schema_props.constraint_fields + != model.__schema_properties__.constraint_fields + and len(benchling_schema_props.constraint_fields) > 0 ) if needs_constraint_clear: ops.append( diff --git a/liminal/tests/test_entity_schema_compare.py b/liminal/tests/test_entity_schema_compare.py index a13ad3b..0d0ddd5 100644 --- a/liminal/tests/test_entity_schema_compare.py +++ b/liminal/tests/test_entity_schema_compare.py @@ -361,13 +361,21 @@ def test_compare_benchling_schema_fields( # type: ignore[no-untyped-def] benchling_mismatch_constraint_fields ) invalid_models = compare_entity_schemas(mock_benchling_sdk) - assert len(invalid_models["mock_entity"]) == 1 + assert len(invalid_models["mock_entity"]) == 2 assert isinstance(invalid_models["mock_entity"][0].op, UpdateEntitySchema) - assert invalid_models["mock_entity"][ + clear_props_dict = invalid_models["mock_entity"][ 0 - ].op.update_props.constraint_fields == { - "string_field_req", - "enum_field", + ].op.update_props.model_dump(exclude_unset=True) + assert clear_props_dict == {"constraint_fields": set()} + assert isinstance(invalid_models["mock_entity"][1].op, UpdateEntitySchema) + update_props_dict = invalid_models["mock_entity"][ + 1 + ].op.update_props.model_dump(exclude_unset=True) + assert update_props_dict == { + "constraint_fields": { + "string_field_req", + "enum_field", + }, } # Test when the Benchling schema has different display naming fields