diff --git a/src/viur/core/bones/base.py b/src/viur/core/bones/base.py index aae4d81a9..4e94d2893 100644 --- a/src/viur/core/bones/base.py +++ b/src/viur/core/bones/base.py @@ -781,29 +781,7 @@ def serialize(self, skel: 'SkeletonInstance', name: str, parentIndexed: bool) -> :param parentIndexed: A boolean indicating whether the parent bone is indexed. :return: A boolean indicating whether the serialization was successful. """ - # Handle compute on write - if self.compute: - match self.compute.interval.method: - case ComputeMethod.OnWrite: - skel.accessedValues[name] = self._compute(skel, name) - - case ComputeMethod.Lifetime: - now = utils.utcNow() - - last_update = \ - skel.accessedValues.get(f"_viur_compute_{name}_") \ - or skel.dbEntity.get(f"_viur_compute_{name}_") - - if not last_update or last_update + self.compute.interval.lifetime < now: - skel.accessedValues[name] = self._compute(skel, name) - skel.dbEntity[f"_viur_compute_{name}_"] = now - - case ComputeMethod.Once: - if name not in skel.dbEntity: - skel.accessedValues[name] = self._compute(skel, name) - - # logging.debug(f"WRITE {name=} {skel.accessedValues=}") - # logging.debug(f"WRITE {name=} {skel.dbEntity=}") + self.serialize_compute(skel, name) if name in skel.accessedValues: newVal = skel.accessedValues[name] @@ -847,6 +825,36 @@ def serialize(self, skel: 'SkeletonInstance', name: str, parentIndexed: bool) -> return True return False + def serialize_compute(self, skel: "SkeletonInstance", name: str) -> None: + """ + This function checks whether a bone is computed and if this is the case, it attempts to serialize the + value with the appropriate calculation method + + :param skel: The SkeletonInstance where the current bone is located + :param name: The name of the bone in the Skeleton + """ + if not self.compute: + return None + match self.compute.interval.method: + case ComputeMethod.OnWrite: + skel.accessedValues[name] = self._compute(skel, name) + + case ComputeMethod.Lifetime: + now = utils.utcNow() + + last_update = \ + skel.accessedValues.get(f"_viur_compute_{name}_") \ + or skel.dbEntity.get(f"_viur_compute_{name}_") + + if not last_update or last_update + self.compute.interval.lifetime < now: + skel.accessedValues[name] = self._compute(skel, name) + skel.dbEntity[f"_viur_compute_{name}_"] = now + + case ComputeMethod.Once: + if name not in skel.dbEntity: + skel.accessedValues[name] = self._compute(skel, name) + + def singleValueUnserialize(self, val): """ Unserializes a single value of the bone from the stored database value. @@ -881,43 +889,8 @@ def unserialize(self, skel: 'viur.core.skeleton.SkeletonInstance', name: str) -> skel.accessedValues[name] = self.getDefaultValue(skel) return False - # Is this value computed? - # In this case, check for configured compute method and if recomputation is required. - # Otherwise, the value from the DB is used as is. - if self.compute and not self._prevent_compute: - match self.compute.interval.method: - # Computation is bound to a lifetime? - case ComputeMethod.Lifetime: - now = utils.utcNow() - - # check if lifetime exceeded - last_update = skel.dbEntity.get(f"_viur_compute_{name}_") - skel.accessedValues[f"_viur_compute_{name}_"] = last_update or now - - # logging.debug(f"READ {name=} {skel.dbEntity=}") - # logging.debug(f"READ {name=} {skel.accessedValues=}") - - if not last_update or last_update + self.compute.interval.lifetime <= now: - # if so, recompute and refresh updated value - skel.accessedValues[name] = value = self._compute(skel, name) - - def transact(): - db_obj = db.Get(skel["key"]) - db_obj[f"_viur_compute_{name}_"] = now - db_obj[name] = value - db.Put(db_obj) - - if db.IsInTransaction(): - transact() - else: - db.RunInTransaction(transact) - - return True - - # Compute on every deserialization - case ComputeMethod.Always: - skel.accessedValues[name] = self._compute(skel, name) - return True + if self.unserialize_compute(skel, name, loadVal): + return True # unserialize value to given config if self.languages and self.multiple: @@ -995,6 +968,52 @@ def transact(): skel.accessedValues[name] = res return True + def unserialize_compute(self, skel: "SkeletonInstance", name: str, loaded_value: t.Any) -> bool: + """ + This function checks whether a bone is computed and if this is the case, it attempts to deserialise the + value with the appropriate calculation method + + :param skel : The SkeletonInstance where the current Bone is located + :param name: The name of the Bone in the Skeleton + :param loaded_value: The value from the DB Entity + :return: True if the Bone was unserialized, False otherwise + """ + if not self.compute or self._prevent_compute: + return False + + match self.compute.interval.method: + # Computation is bound to a lifetime? + case ComputeMethod.Lifetime: + now = utils.utcNow() + + # check if lifetime exceeded + last_update = skel.dbEntity.get(f"_viur_compute_{name}_") + skel.accessedValues[f"_viur_compute_{name}_"] = last_update or now + + if not last_update or last_update + self.compute.interval.lifetime <= now: + # if so, recompute and refresh updated value + skel.accessedValues[name] = value = self._compute(skel, name) + + def transact(): + db_obj = db.Get(skel["key"]) + db_obj[f"_viur_compute_{name}_"] = now + db_obj[name] = value + db.Put(db_obj) + + if db.IsInTransaction(): + transact() + else: + db.RunInTransaction(transact) + + return True + + # Compute on every deserialization + case ComputeMethod.Always: + skel.accessedValues[name] = self._compute(skel, name) + return True + + return False + def delete(self, skel: 'viur.core.skeleton.SkeletonInstance', name: str): """ Like postDeletedHandler, but runs inside the transaction diff --git a/src/viur/core/bones/credential.py b/src/viur/core/bones/credential.py index a19146b63..efc4af16e 100644 --- a/src/viur/core/bones/credential.py +++ b/src/viur/core/bones/credential.py @@ -49,6 +49,7 @@ def serialize(self, skel: 'SkeletonInstance', name: str, parentIndexed: bool) -> :return: True if the value was updated in the database, False otherwise. :rtype: bool """ + self.serialize_compute(skel, name) skel.dbEntity.exclude_from_indexes.add(name) # Ensure we are never indexed if name in skel.accessedValues and skel.accessedValues[name]: skel.dbEntity[name] = skel.accessedValues[name] diff --git a/src/viur/core/bones/key.py b/src/viur/core/bones/key.py index 413946ec7..810f2fcf5 100644 --- a/src/viur/core/bones/key.py +++ b/src/viur/core/bones/key.py @@ -94,13 +94,11 @@ def unserialize(self, skel: 'SkeletonInstance', name: str) -> bool: ): skel.accessedValues[name] = skel.dbEntity.key return True - return super().unserialize(skel, name) def serialize(self, skel: 'SkeletonInstance', name: str, parentIndexed: bool) -> bool: if name not in skel.accessedValues: return False - if name == "key": skel.dbEntity.key = skel.accessedValues["key"] return True diff --git a/src/viur/core/bones/password.py b/src/viur/core/bones/password.py index b08e0ae19..893898385 100644 --- a/src/viur/core/bones/password.py +++ b/src/viur/core/bones/password.py @@ -163,6 +163,7 @@ def serialize(self, skel: 'SkeletonInstance', name: str, parentIndexed: bool) -> otherwise, a list of ReadFromClientErrors containing detailed information about the errors. :rtype: Union[None, List[ReadFromClientError]] """ + self.serialize_compute(skel, name) if not (value := skel.accessedValues.get(name)): return False