Coverage for /opt/conda/envs/apienv/lib/python3.10/site-packages/daiquiri/core/schema/validators.py: 77%

47 statements  

« prev     ^ index     » next       coverage.py v7.6.5, created at 2024-11-15 02:12 +0000

1#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3from marshmallow import validate, fields, ValidationError 

4import bleach 

5 

6"""Custom Schema Validators 

7 

8 https://json-schema.org/understanding-json-schema/reference/string.html 

9""" 

10 

11 

12def ValidatedRegexp(pattern, **kwargs): 

13 """A regexp validated fields.Str 

14 

15 Args: 

16 pattern: the pattern to use from the above dictionary 

17 Kwargs: 

18 passed to the `field` 

19 """ 

20 

21 expressions = { 

22 "word": "^[A-z0-9]+$", 

23 "word-dash": "^[A-z0-9-]+$", 

24 "word-dash-space": r"^[\w\s-]+$", 

25 "word-special": r"^(\w|\s|-|%|\(|\)|,|\.)+$", 

26 "saving": "^({(sample|component|subsampletype|subsampleid)}|[A-z0-9/-])+$", 

27 "smiles": r"^([^J][A-Za-z0-9@+\-\[\]\(\)\\\/%=#$]+)$", 

28 "path": "^/[A-z0-9-/]+$", 

29 } 

30 

31 if pattern in expressions: 

32 validates = kwargs.pop("validate", None) 

33 if validates and not isinstance(validates, list): 

34 validates = [validates] 

35 

36 metadata = kwargs.pop("metadata", {}) 

37 metadata["pattern"] = expressions[pattern] 

38 

39 return fields.Str( 

40 validate=[ 

41 validate.Regexp( 

42 expressions[pattern], 

43 flags=0, 

44 error="Input {input} does not match {regex}", 

45 ) 

46 ] 

47 + (validates if validates else []), 

48 metadata=metadata, 

49 **kwargs 

50 ) 

51 else: 

52 raise Exception("Cant find regular expression for {pat}".format(pat=pattern)) 

53 

54 

55class Any(fields.Field): 

56 """Mixed field""" 

57 

58 def _jsonschema_type_mapping(self): 

59 return {"type": "any", "title": self.name} 

60 

61 

62class NoneValidator(validate.Validator): 

63 """Validator that requires the field is None""" 

64 

65 def __call__(self, val): 

66 if val is not None: 

67 raise ValidationError("Field must be empty") 

68 

69 return val 

70 

71 

72class EmptyField(fields.Field): 

73 """Custom empty field type""" 

74 

75 def _jsonschema_type_mapping(self): 

76 return {"type": "empty", "title": self.name} 

77 

78 

79def RequireEmpty(**kwargs): 

80 """A RequireEmpty field 

81 

82 Requires that this particular field is empty by combining NoneValidator and EmptyField 

83 """ 

84 return EmptyField(validate=NoneValidator(), allow_none=True, **kwargs) 

85 

86 

87def OneOf(choices, **kwargs): 

88 """Single enum type validator 

89 

90 Args: 

91 choices (list): List of choices 

92 """ 

93 metadata = kwargs.pop("metadata", {}) 

94 metadata["enum"] = choices 

95 

96 return fields.Str(metadata=metadata, validate=validate.OneOf(choices), **kwargs) 

97 

98 

99class SanitizedHTML(fields.Str): 

100 """A Sanitised HTML string 

101 

102 bleach.cleaned HTML string field 

103 """ 

104 

105 def __init__(self, *args, **kwargs): 

106 self._strip = kwargs.pop("strip", None) 

107 self._load = kwargs.pop("load", None) 

108 self._linkify = kwargs.pop("linkify", None) 

109 

110 super().__init__(*args, **kwargs) 

111 

112 def _serialize(self, value, attr, obj, **kwargs): 

113 if value is None: 

114 return None 

115 

116 if self._load: 

117 value = bleach.clean(value, strip=self._strip) 

118 

119 if self._linkify: 

120 value = bleach.linkify(value) 

121 

122 return super()._serialize(value, attr, obj, **kwargs) 

123 

124 def _deserialize(self, value, attr, data, **kwargs): 

125 value = bleach.clean(value, strip=self._strip) 

126 return super()._deserialize(value, attr, data, **kwargs)