Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/bones/uid.py: 15%
49 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-27 07:59 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-27 07:59 +0000
1import time
2import typing as t
3from viur.core import db
4from viur.core.bones.base import BaseBone, Compute, ComputeInterval, ComputeMethod, UniqueValue, UniqueLockMethod
7def generate_number(db_key: db.Key) -> int:
8 """
9 The generate_number method generates a leading number that is always unique per entry.
10 """
12 def transact(_key: db.Key):
13 for i in range(3):
14 try:
15 if db_obj := db.Get(_key):
16 db_obj["count"] += 1
17 else:
18 db_obj = db.Entity(_key)
19 db_obj["count"] = 0
20 db.Put(db_obj)
21 break
22 except db.CollisionError: # recall the function
23 time.sleep(i + 1)
24 else:
25 raise ValueError("Can't set the Uid")
26 return db_obj["count"]
28 if db.IsInTransaction():
29 return transact(db_key)
30 else:
31 return db.RunInTransaction(transact, db_key)
34def generate_uid(skel, bone):
35 db_key = db.Key("viur-uids", f"{skel.kindName}-{bone.name}-uid")
36 count_value = generate_number(db_key)
37 if bone.fillchar:
38 length_to_fill = bone.length - len(bone.pattern)
39 res = str(count_value).rjust(length_to_fill, bone.fillchar)
40 return bone.pattern.replace("*", res)
41 else:
42 return bone.pattern.replace("*", str(count_value))
45class UidBone(BaseBone):
46 """
47 The "UidBone" represents a data field that contains text values.
48 """
49 type = "uid"
51 def __init__(
52 self,
53 *,
54 generate_fn: t.Callable = generate_uid,
55 fillchar: str = "*",
56 length: int = 13,
57 pattern: str | t.Callable | None = "*",
58 **kwargs
59 ):
60 """
61 Initializes a new UidBone.
63 :param generate_fn: The compute function to calculate the unique value,
64 :param fillchar The char that are filed in when the uid has not the length.
65 :param length: The length allowed for values of this bone.
66 :param pattern: The pattern for this Bone. "*" will be replaced with the uid value.
67 :param kwargs: Inherited arguments from the BaseBone.
68 """
70 super().__init__(
71 compute=Compute(fn=generate_fn, interval=ComputeInterval(ComputeMethod.Once)),
72 unique=UniqueValue(UniqueLockMethod.SameValue, False, "Unique Value already in use"),
73 **kwargs
74 )
75 if self.multiple or self.languages:
76 raise ValueError("UidBone cannot be multiple or translated")
78 if not self.readOnly:
79 raise ValueError("UidBone must be read-only")
81 self.fillchar = str(fillchar)
82 self.length = length
83 if isinstance(pattern, t.Callable):
84 pattern = pattern()
85 self.pattern = str(pattern)
86 if self.pattern.count("*") != 1:
87 raise ValueError("Only one wildcard (*) is allowed and required in the pattern")
88 if len(self.fillchar) != 1:
89 raise ValueError("Only one char is allowed as fillchar")
91 def structure(self) -> dict:
92 ret = super().structure() | {
93 "fillchar": self.fillchar,
94 "length": self.length,
95 "pattern": self.pattern
96 }
97 return ret