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

1import time 

2import typing as t 

3from viur.core import db 

4from viur.core.bones.base import BaseBone, Compute, ComputeInterval, ComputeMethod, UniqueValue, UniqueLockMethod 

5 

6 

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 """ 

11 

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"] 

27 

28 if db.IsInTransaction(): 

29 return transact(db_key) 

30 else: 

31 return db.RunInTransaction(transact, db_key) 

32 

33 

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)) 

43 

44 

45class UidBone(BaseBone): 

46 """ 

47 The "UidBone" represents a data field that contains text values. 

48 """ 

49 type = "uid" 

50 

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. 

62 

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 """ 

69 

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") 

77 

78 if not self.readOnly: 

79 raise ValueError("UidBone must be read-only") 

80 

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") 

90 

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