Skip to content
Snippets Groups Projects
Verified Commit 2456a5a1 authored by Timm Fitschen's avatar Timm Fitschen
Browse files

Merge branch 'dev' into f-http-proxy

parents 837dccbd 9900131a
No related branches found
No related tags found
2 merge requests!79Release 0.10.0,!75F http proxy
Pipeline #30213 passed
...@@ -34,6 +34,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ...@@ -34,6 +34,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
th above `empty_diff` function). Formerly this would have caused a merge th above `empty_diff` function). Formerly this would have caused a merge
conflict if the referenced record(s) were identical, but stored in different conflict if the referenced record(s) were identical, but stored in different
Python objects. Python objects.
* `apiutils.merge_entities` now has an optional `force` argument (defaults to
`False`, i.e., the old behavior) which determines whether in case of merge
conflicts errors will be raised or the properties and attributes of entity A
will be overwritten by entity B.
### Deprecated ### ### Deprecated ###
......
...@@ -361,7 +361,7 @@ def empty_diff(old_entity: Entity, new_entity: Entity, compare_referenced_record ...@@ -361,7 +361,7 @@ def empty_diff(old_entity: Entity, new_entity: Entity, compare_referenced_record
return True return True
def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_empty_diffs=True): def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_empty_diffs=True, force=False):
""" """
Merge entity_b into entity_a such that they have the same parents and properties. Merge entity_b into entity_a such that they have the same parents and properties.
...@@ -385,6 +385,10 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp ...@@ -385,6 +385,10 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp
record(s) that may be different Python objects but have empty diffs. If record(s) that may be different Python objects but have empty diffs. If
set to `False` a merge conflict will be raised in this case set to `False` a merge conflict will be raised in this case
instead. Default is True. instead. Default is True.
force : bool, optional
If True, in case `entity_a` and `entity_b` have the same properties, the
values of `entity_a` are replaced by those of `entity_b` in the merge.
If `False`, a RuntimeError is raised instead. Default is False.
Returns Returns
------- -------
...@@ -421,6 +425,9 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp ...@@ -421,6 +425,9 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp
if (diff_r1["properties"][key][attribute] is None): if (diff_r1["properties"][key][attribute] is None):
setattr(entity_a.get_property(key), attribute, setattr(entity_a.get_property(key), attribute,
diff_r2["properties"][key][attribute]) diff_r2["properties"][key][attribute])
elif force:
setattr(entity_a.get_property(key), attribute,
diff_r2["properties"][key][attribute])
else: else:
raise RuntimeError( raise RuntimeError(
f"Merge conflict:\nEntity a ({entity_a.id}, {entity_a.name}) " f"Merge conflict:\nEntity a ({entity_a.id}, {entity_a.name}) "
...@@ -448,6 +455,9 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp ...@@ -448,6 +455,9 @@ def merge_entities(entity_a: Entity, entity_b: Entity, merge_references_with_emp
if sa_a != sa_b: if sa_a != sa_b:
if sa_a is None: if sa_a is None:
setattr(entity_a, special_attribute, sa_b) setattr(entity_a, special_attribute, sa_b)
elif force:
# force overwrite
setattr(entity_a, special_attribute, sa_b)
else: else:
raise RuntimeError("Merge conflict.") raise RuntimeError("Merge conflict.")
return entity_a return entity_a
......
# -*- encoding: utf-8 -*-
# #
# This file is a part of the CaosDB Project. # This file is a part of the CaosDB Project.
# #
# Copyright (C) 2018 Research Group Biomedical Physics,
# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
# Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> # Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
# Copyright (C) 2022 Florian Spreckelsen <f.spreckelsen@indiscale.com>
# Copyright (C) 2020-2022 IndiScale GmbH <info@indiscale.com> # Copyright (C) 2020-2022 IndiScale GmbH <info@indiscale.com>
# Copyright (C) 2018 Research Group Biomedical Physics,
# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
# #
# ** end header
# #
# Test apiutils # Test apiutils
# A. Schlemmer, 02/2018 # A. Schlemmer, 02/2018
...@@ -466,3 +465,90 @@ def test_empty_diff(): ...@@ -466,3 +465,90 @@ def test_empty_diff():
"RefType"), value=[ref_rec_b, ref_rec_b]) "RefType"), value=[ref_rec_b, ref_rec_b])
assert not empty_diff(rec_a, rec_b) assert not empty_diff(rec_a, rec_b)
assert empty_diff(rec_a, rec_b, compare_referenced_records=True) assert empty_diff(rec_a, rec_b, compare_referenced_records=True)
def test_force_merge():
"""Test whether a forced merge overwrites existing properties correctly."""
# name overwrite
recA = db.Record(name="A")
recB = db.Record(name="B")
with pytest.raises(RuntimeError) as re:
merge_entities(recA, recB)
assert "Merge conflict" in str(re.value)
merge_entities(recA, recB, force=True)
assert "B" == recA.name
# unchanged
assert "B" == recB.name
# description overwrite
recA = db.Record()
recA.description = "something"
recB = db.Record()
recB.description = "something else"
with pytest.raises(RuntimeError) as re:
merge_entities(recA, recB)
assert "Merge conflict" in str(re.value)
merge_entities(recA, recB, force=True)
assert recA.description == "something else"
# unchanged
assert recB.description == "something else"
# property overwrite
recA = db.Record()
recA.add_property(name="propA", value="something")
recB = db.Record()
recB.add_property(name="propA", value="something else")
with pytest.raises(RuntimeError) as re:
merge_entities(recA, recB)
assert "Merge conflict" in str(re.value)
merge_entities(recA, recB, force=True)
assert recA.get_property("propA").value == "something else"
# unchanged
assert recB.get_property("propA").value == "something else"
# don't remove a property that's not in recB
recA = db.Record()
recA.add_property(name="propA", value="something")
recA.add_property(name="propB", value=5.0)
recB = db.Record()
recB.add_property(name="propA", value="something else")
merge_entities(recA, recB, force=True)
assert recA.get_property("propA").value == "something else"
assert recA.get_property("propB").value == 5.0
# also overwrite datatypes ...
rtA = db.RecordType()
rtA.add_property(name="propA", datatype=db.INTEGER)
rtB = db.RecordType()
rtB.add_property(name="propA", datatype=db.TEXT)
with pytest.raises(RuntimeError) as re:
merge_entities(rtA, rtB)
assert "Merge conflict" in str(re.value)
merge_entities(rtA, rtB, force=True)
assert rtA.get_property("propA").datatype == db.TEXT
# unchanged
assert rtB.get_property("propA").datatype == db.TEXT
# ... and units
recA = db.Record()
recA.add_property(name="propA", value=5, unit="m")
recB = db.Record()
recB.add_property(name="propA", value=5, unit="cm")
with pytest.raises(RuntimeError) as re:
merge_entities(recA, recB)
assert "Merge conflict" in str(re.value)
merge_entities(recA, recB, force=True)
assert recA.get_property("propA").unit == "cm"
# unchanged
assert recB.get_property("propA").unit == "cm"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment