clear-skies 1.22.31__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of clear-skies might be problematic. Click here for more details.

Files changed (345) hide show
  1. {clear_skies-1.22.31.dist-info → clear_skies-2.0.1.dist-info}/METADATA +12 -14
  2. clear_skies-2.0.1.dist-info/RECORD +249 -0
  3. {clear_skies-1.22.31.dist-info → clear_skies-2.0.1.dist-info}/WHEEL +1 -1
  4. clearskies/__init__.py +42 -25
  5. clearskies/action.py +7 -0
  6. clearskies/authentication/__init__.py +8 -41
  7. clearskies/authentication/authentication.py +46 -0
  8. clearskies/authentication/authorization.py +8 -9
  9. clearskies/authentication/authorization_pass_through.py +11 -9
  10. clearskies/authentication/jwks.py +133 -58
  11. clearskies/authentication/public.py +3 -38
  12. clearskies/authentication/secret_bearer.py +516 -54
  13. clearskies/autodoc/formats/oai3_json/__init__.py +1 -1
  14. clearskies/autodoc/formats/oai3_json/oai3_json.py +9 -7
  15. clearskies/autodoc/formats/oai3_json/parameter.py +6 -3
  16. clearskies/autodoc/formats/oai3_json/request.py +7 -5
  17. clearskies/autodoc/formats/oai3_json/response.py +7 -4
  18. clearskies/autodoc/formats/oai3_json/schema/object.py +4 -1
  19. clearskies/autodoc/request/__init__.py +2 -0
  20. clearskies/autodoc/request/header.py +4 -6
  21. clearskies/autodoc/request/json_body.py +4 -6
  22. clearskies/autodoc/request/parameter.py +8 -0
  23. clearskies/autodoc/request/request.py +7 -4
  24. clearskies/autodoc/request/url_parameter.py +4 -6
  25. clearskies/autodoc/request/url_path.py +4 -6
  26. clearskies/autodoc/schema/__init__.py +4 -2
  27. clearskies/autodoc/schema/array.py +5 -6
  28. clearskies/autodoc/schema/boolean.py +4 -10
  29. clearskies/autodoc/schema/date.py +0 -3
  30. clearskies/autodoc/schema/datetime.py +1 -4
  31. clearskies/autodoc/schema/double.py +0 -3
  32. clearskies/autodoc/schema/enum.py +4 -2
  33. clearskies/autodoc/schema/integer.py +4 -9
  34. clearskies/autodoc/schema/long.py +0 -3
  35. clearskies/autodoc/schema/number.py +4 -9
  36. clearskies/autodoc/schema/object.py +5 -7
  37. clearskies/autodoc/schema/password.py +0 -3
  38. clearskies/autodoc/schema/schema.py +11 -0
  39. clearskies/autodoc/schema/string.py +4 -10
  40. clearskies/backends/__init__.py +55 -20
  41. clearskies/backends/api_backend.py +1100 -284
  42. clearskies/backends/backend.py +53 -84
  43. clearskies/backends/cursor_backend.py +236 -186
  44. clearskies/backends/memory_backend.py +519 -226
  45. clearskies/backends/secrets_backend.py +75 -31
  46. clearskies/column.py +1229 -0
  47. clearskies/columns/__init__.py +71 -0
  48. clearskies/columns/audit.py +205 -0
  49. clearskies/columns/belongs_to_id.py +483 -0
  50. clearskies/columns/belongs_to_model.py +128 -0
  51. clearskies/columns/belongs_to_self.py +105 -0
  52. clearskies/columns/boolean.py +109 -0
  53. clearskies/columns/category_tree.py +275 -0
  54. clearskies/columns/category_tree_ancestors.py +51 -0
  55. clearskies/columns/category_tree_children.py +127 -0
  56. clearskies/columns/category_tree_descendants.py +48 -0
  57. clearskies/columns/created.py +94 -0
  58. clearskies/columns/created_by_authorization_data.py +116 -0
  59. clearskies/columns/created_by_header.py +99 -0
  60. clearskies/columns/created_by_ip.py +92 -0
  61. clearskies/columns/created_by_routing_data.py +96 -0
  62. clearskies/columns/created_by_user_agent.py +92 -0
  63. clearskies/columns/date.py +230 -0
  64. clearskies/columns/datetime.py +278 -0
  65. clearskies/columns/email.py +76 -0
  66. clearskies/columns/float.py +149 -0
  67. clearskies/columns/has_many.py +505 -0
  68. clearskies/columns/has_many_self.py +56 -0
  69. clearskies/columns/has_one.py +14 -0
  70. clearskies/columns/integer.py +156 -0
  71. clearskies/columns/json.py +122 -0
  72. clearskies/columns/many_to_many_ids.py +333 -0
  73. clearskies/columns/many_to_many_ids_with_data.py +270 -0
  74. clearskies/columns/many_to_many_models.py +154 -0
  75. clearskies/columns/many_to_many_pivots.py +133 -0
  76. clearskies/columns/phone.py +158 -0
  77. clearskies/columns/select.py +91 -0
  78. clearskies/columns/string.py +98 -0
  79. clearskies/columns/timestamp.py +160 -0
  80. clearskies/columns/updated.py +110 -0
  81. clearskies/columns/uuid.py +86 -0
  82. clearskies/configs/README.md +105 -0
  83. clearskies/configs/__init__.py +162 -0
  84. clearskies/configs/actions.py +43 -0
  85. clearskies/configs/any.py +13 -0
  86. clearskies/configs/any_dict.py +22 -0
  87. clearskies/configs/any_dict_or_callable.py +23 -0
  88. clearskies/configs/authentication.py +23 -0
  89. clearskies/configs/authorization.py +23 -0
  90. clearskies/configs/boolean.py +16 -0
  91. clearskies/configs/boolean_or_callable.py +18 -0
  92. clearskies/configs/callable_config.py +18 -0
  93. clearskies/configs/columns.py +34 -0
  94. clearskies/configs/conditions.py +30 -0
  95. clearskies/configs/config.py +24 -0
  96. clearskies/configs/datetime.py +18 -0
  97. clearskies/configs/datetime_or_callable.py +19 -0
  98. clearskies/configs/endpoint.py +23 -0
  99. clearskies/configs/endpoint_list.py +28 -0
  100. clearskies/configs/float.py +16 -0
  101. clearskies/configs/float_or_callable.py +18 -0
  102. clearskies/configs/integer.py +16 -0
  103. clearskies/configs/integer_or_callable.py +18 -0
  104. clearskies/configs/joins.py +30 -0
  105. clearskies/configs/list_any_dict.py +30 -0
  106. clearskies/configs/list_any_dict_or_callable.py +31 -0
  107. clearskies/configs/model_class.py +35 -0
  108. clearskies/configs/model_column.py +65 -0
  109. clearskies/configs/model_columns.py +56 -0
  110. clearskies/configs/model_destination_name.py +25 -0
  111. clearskies/configs/model_to_id_column.py +43 -0
  112. clearskies/configs/readable_model_column.py +9 -0
  113. clearskies/configs/readable_model_columns.py +9 -0
  114. clearskies/configs/schema.py +23 -0
  115. clearskies/configs/searchable_model_columns.py +9 -0
  116. clearskies/configs/security_headers.py +39 -0
  117. clearskies/configs/select.py +26 -0
  118. clearskies/configs/select_list.py +47 -0
  119. clearskies/configs/string.py +29 -0
  120. clearskies/configs/string_dict.py +32 -0
  121. clearskies/configs/string_list.py +32 -0
  122. clearskies/configs/string_list_or_callable.py +35 -0
  123. clearskies/configs/string_or_callable.py +18 -0
  124. clearskies/configs/timedelta.py +18 -0
  125. clearskies/configs/timezone.py +18 -0
  126. clearskies/configs/url.py +23 -0
  127. clearskies/configs/validators.py +45 -0
  128. clearskies/configs/writeable_model_column.py +9 -0
  129. clearskies/configs/writeable_model_columns.py +9 -0
  130. clearskies/configurable.py +76 -0
  131. clearskies/contexts/__init__.py +8 -8
  132. clearskies/contexts/cli.py +8 -41
  133. clearskies/contexts/context.py +91 -56
  134. clearskies/contexts/wsgi.py +16 -29
  135. clearskies/contexts/wsgi_ref.py +53 -0
  136. clearskies/di/__init__.py +10 -7
  137. clearskies/di/additional_config.py +115 -4
  138. clearskies/di/additional_config_auto_import.py +12 -0
  139. clearskies/di/di.py +742 -121
  140. clearskies/di/inject/__init__.py +23 -0
  141. clearskies/di/inject/by_class.py +21 -0
  142. clearskies/di/inject/by_name.py +18 -0
  143. clearskies/di/inject/di.py +13 -0
  144. clearskies/di/inject/environment.py +14 -0
  145. clearskies/di/inject/input_output.py +20 -0
  146. clearskies/di/inject/now.py +13 -0
  147. clearskies/di/inject/requests.py +13 -0
  148. clearskies/di/inject/secrets.py +14 -0
  149. clearskies/di/inject/utcnow.py +13 -0
  150. clearskies/di/inject/uuid.py +15 -0
  151. clearskies/di/injectable.py +29 -0
  152. clearskies/di/injectable_properties.py +131 -0
  153. clearskies/end.py +183 -0
  154. clearskies/endpoint.py +1310 -0
  155. clearskies/endpoint_group.py +310 -0
  156. clearskies/endpoints/__init__.py +23 -0
  157. clearskies/endpoints/advanced_search.py +526 -0
  158. clearskies/endpoints/callable.py +388 -0
  159. clearskies/endpoints/create.py +202 -0
  160. clearskies/endpoints/delete.py +139 -0
  161. clearskies/endpoints/get.py +275 -0
  162. clearskies/endpoints/health_check.py +181 -0
  163. clearskies/endpoints/list.py +573 -0
  164. clearskies/endpoints/restful_api.py +427 -0
  165. clearskies/endpoints/simple_search.py +286 -0
  166. clearskies/endpoints/update.py +190 -0
  167. clearskies/environment.py +5 -3
  168. clearskies/exceptions/__init__.py +17 -0
  169. clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
  170. clearskies/exceptions/moved_permanently.py +3 -0
  171. clearskies/exceptions/moved_temporarily.py +3 -0
  172. clearskies/exceptions/not_found.py +2 -0
  173. clearskies/functional/__init__.py +2 -2
  174. clearskies/functional/routing.py +92 -0
  175. clearskies/functional/string.py +19 -11
  176. clearskies/functional/validations.py +61 -9
  177. clearskies/input_outputs/__init__.py +9 -7
  178. clearskies/input_outputs/cli.py +130 -142
  179. clearskies/input_outputs/exceptions/__init__.py +1 -1
  180. clearskies/input_outputs/headers.py +45 -0
  181. clearskies/input_outputs/input_output.py +91 -122
  182. clearskies/input_outputs/programmatic.py +69 -0
  183. clearskies/input_outputs/wsgi.py +23 -38
  184. clearskies/model.py +984 -183
  185. clearskies/parameters_to_properties.py +31 -0
  186. clearskies/query/__init__.py +12 -0
  187. clearskies/query/condition.py +223 -0
  188. clearskies/query/join.py +136 -0
  189. clearskies/query/query.py +196 -0
  190. clearskies/query/sort.py +27 -0
  191. clearskies/schema.py +82 -0
  192. clearskies/secrets/__init__.py +3 -31
  193. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
  194. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
  195. clearskies/secrets/akeyless.py +88 -147
  196. clearskies/secrets/secrets.py +8 -8
  197. clearskies/security_header.py +15 -0
  198. clearskies/security_headers/__init__.py +8 -8
  199. clearskies/security_headers/cache_control.py +47 -110
  200. clearskies/security_headers/cors.py +40 -95
  201. clearskies/security_headers/csp.py +76 -151
  202. clearskies/security_headers/hsts.py +14 -16
  203. clearskies/test_base.py +8 -0
  204. clearskies/typing.py +11 -0
  205. clearskies/validator.py +37 -0
  206. clearskies/validators/__init__.py +33 -0
  207. clearskies/validators/after_column.py +62 -0
  208. clearskies/validators/before_column.py +13 -0
  209. clearskies/validators/in_the_future.py +32 -0
  210. clearskies/validators/in_the_future_at_least.py +11 -0
  211. clearskies/validators/in_the_future_at_most.py +10 -0
  212. clearskies/validators/in_the_past.py +32 -0
  213. clearskies/validators/in_the_past_at_least.py +10 -0
  214. clearskies/validators/in_the_past_at_most.py +10 -0
  215. clearskies/validators/maximum_length.py +26 -0
  216. clearskies/validators/maximum_value.py +29 -0
  217. clearskies/validators/minimum_length.py +26 -0
  218. clearskies/validators/minimum_value.py +29 -0
  219. clearskies/validators/required.py +35 -0
  220. clearskies/validators/timedelta.py +59 -0
  221. clearskies/validators/unique.py +31 -0
  222. clear_skies-1.22.31.dist-info/RECORD +0 -214
  223. clearskies/application.py +0 -29
  224. clearskies/authentication/auth0_jwks.py +0 -118
  225. clearskies/authentication/auth_exception.py +0 -2
  226. clearskies/authentication/jwks_jwcrypto.py +0 -51
  227. clearskies/backends/api_get_only_backend.py +0 -48
  228. clearskies/backends/example_backend.py +0 -43
  229. clearskies/backends/file_backend.py +0 -48
  230. clearskies/backends/json_backend.py +0 -7
  231. clearskies/backends/restful_api_advanced_search_backend.py +0 -103
  232. clearskies/binding_config.py +0 -16
  233. clearskies/column_types/__init__.py +0 -203
  234. clearskies/column_types/audit.py +0 -249
  235. clearskies/column_types/belongs_to.py +0 -271
  236. clearskies/column_types/boolean.py +0 -60
  237. clearskies/column_types/category_tree.py +0 -304
  238. clearskies/column_types/column.py +0 -373
  239. clearskies/column_types/created.py +0 -26
  240. clearskies/column_types/created_by_authorization_data.py +0 -26
  241. clearskies/column_types/created_by_header.py +0 -24
  242. clearskies/column_types/created_by_ip.py +0 -17
  243. clearskies/column_types/created_by_routing_data.py +0 -25
  244. clearskies/column_types/created_by_user_agent.py +0 -17
  245. clearskies/column_types/created_micro.py +0 -26
  246. clearskies/column_types/datetime.py +0 -109
  247. clearskies/column_types/datetime_micro.py +0 -12
  248. clearskies/column_types/email.py +0 -18
  249. clearskies/column_types/float.py +0 -43
  250. clearskies/column_types/has_many.py +0 -179
  251. clearskies/column_types/has_one.py +0 -60
  252. clearskies/column_types/integer.py +0 -41
  253. clearskies/column_types/json.py +0 -25
  254. clearskies/column_types/many_to_many.py +0 -278
  255. clearskies/column_types/many_to_many_with_data.py +0 -162
  256. clearskies/column_types/phone.py +0 -48
  257. clearskies/column_types/select.py +0 -11
  258. clearskies/column_types/string.py +0 -24
  259. clearskies/column_types/timestamp.py +0 -73
  260. clearskies/column_types/updated.py +0 -26
  261. clearskies/column_types/updated_micro.py +0 -26
  262. clearskies/column_types/uuid.py +0 -25
  263. clearskies/columns.py +0 -123
  264. clearskies/condition_parser.py +0 -172
  265. clearskies/contexts/build_context.py +0 -54
  266. clearskies/contexts/convert_to_application.py +0 -190
  267. clearskies/contexts/extract_handler.py +0 -37
  268. clearskies/contexts/test.py +0 -94
  269. clearskies/decorators/__init__.py +0 -41
  270. clearskies/decorators/allow_non_json_bodies.py +0 -9
  271. clearskies/decorators/auth0_jwks.py +0 -22
  272. clearskies/decorators/authorization.py +0 -10
  273. clearskies/decorators/binding_classes.py +0 -9
  274. clearskies/decorators/binding_modules.py +0 -9
  275. clearskies/decorators/bindings.py +0 -9
  276. clearskies/decorators/create.py +0 -10
  277. clearskies/decorators/delete.py +0 -10
  278. clearskies/decorators/docs.py +0 -14
  279. clearskies/decorators/get.py +0 -10
  280. clearskies/decorators/jwks.py +0 -26
  281. clearskies/decorators/merge.py +0 -124
  282. clearskies/decorators/patch.py +0 -10
  283. clearskies/decorators/post.py +0 -10
  284. clearskies/decorators/public.py +0 -11
  285. clearskies/decorators/response_headers.py +0 -10
  286. clearskies/decorators/return_raw_response.py +0 -9
  287. clearskies/decorators/schema.py +0 -10
  288. clearskies/decorators/secret_bearer.py +0 -24
  289. clearskies/decorators/security_headers.py +0 -10
  290. clearskies/di/standard_dependencies.py +0 -151
  291. clearskies/handlers/__init__.py +0 -41
  292. clearskies/handlers/advanced_search.py +0 -271
  293. clearskies/handlers/base.py +0 -479
  294. clearskies/handlers/callable.py +0 -192
  295. clearskies/handlers/create.py +0 -35
  296. clearskies/handlers/crud_by_method.py +0 -18
  297. clearskies/handlers/database_connector.py +0 -32
  298. clearskies/handlers/delete.py +0 -61
  299. clearskies/handlers/exceptions/__init__.py +0 -5
  300. clearskies/handlers/exceptions/not_found.py +0 -3
  301. clearskies/handlers/get.py +0 -156
  302. clearskies/handlers/health_check.py +0 -59
  303. clearskies/handlers/input_processing.py +0 -79
  304. clearskies/handlers/list.py +0 -530
  305. clearskies/handlers/mygrations.py +0 -82
  306. clearskies/handlers/request_method_routing.py +0 -47
  307. clearskies/handlers/restful_api.py +0 -218
  308. clearskies/handlers/routing.py +0 -62
  309. clearskies/handlers/schema_helper.py +0 -128
  310. clearskies/handlers/simple_routing.py +0 -206
  311. clearskies/handlers/simple_routing_route.py +0 -197
  312. clearskies/handlers/simple_search.py +0 -136
  313. clearskies/handlers/update.py +0 -102
  314. clearskies/handlers/write.py +0 -193
  315. clearskies/input_requirements/__init__.py +0 -78
  316. clearskies/input_requirements/after.py +0 -36
  317. clearskies/input_requirements/before.py +0 -36
  318. clearskies/input_requirements/in_the_future_at_least.py +0 -19
  319. clearskies/input_requirements/in_the_future_at_most.py +0 -19
  320. clearskies/input_requirements/in_the_past_at_least.py +0 -19
  321. clearskies/input_requirements/in_the_past_at_most.py +0 -19
  322. clearskies/input_requirements/maximum_length.py +0 -19
  323. clearskies/input_requirements/maximum_value.py +0 -19
  324. clearskies/input_requirements/minimum_length.py +0 -22
  325. clearskies/input_requirements/minimum_value.py +0 -19
  326. clearskies/input_requirements/required.py +0 -23
  327. clearskies/input_requirements/requirement.py +0 -25
  328. clearskies/input_requirements/time_delta.py +0 -38
  329. clearskies/input_requirements/unique.py +0 -18
  330. clearskies/mocks/__init__.py +0 -7
  331. clearskies/mocks/input_output.py +0 -124
  332. clearskies/mocks/models.py +0 -142
  333. clearskies/models.py +0 -350
  334. clearskies/security_headers/base.py +0 -12
  335. clearskies/tests/simple_api/models/__init__.py +0 -2
  336. clearskies/tests/simple_api/models/status.py +0 -23
  337. clearskies/tests/simple_api/models/user.py +0 -21
  338. clearskies/tests/simple_api/users_api.py +0 -64
  339. {clear_skies-1.22.31.dist-info → clear_skies-2.0.1.dist-info}/LICENSE +0 -0
  340. /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
  341. /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
  342. /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
  343. /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
  344. /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
  345. /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
@@ -1,78 +0,0 @@
1
- import datetime
2
-
3
- from .after import After
4
- from .before import Before
5
- from ..binding_config import BindingConfig
6
- from .minimum_length import MinimumLength
7
- from .minimum_value import MinimumValue
8
- from .maximum_length import MaximumLength
9
- from .maximum_value import MaximumValue
10
- from .required import Required
11
- from .requirement import Requirement
12
- from .unique import Unique
13
- from .in_the_future_at_least import InTheFutureAtLeast
14
- from .in_the_future_at_most import InTheFutureAtMost
15
- from .in_the_past_at_least import InThePastAtLeast
16
- from .in_the_past_at_most import InThePastAtMost
17
- from .time_delta import TimeDelta
18
-
19
-
20
- def after(other_column_name: str, allow_equal: bool = False):
21
- return BindingConfig(After, other_column_name=other_column_name, allow_equal=allow_equal)
22
-
23
-
24
- def before(other_column_name: str, allow_equal: bool = False):
25
- return BindingConfig(Before, other_column_name=other_column_name, allow_equal=allow_equal)
26
-
27
-
28
- def minimum_length(minimum_length: int):
29
- return BindingConfig(MinimumLength, minimum_length)
30
-
31
-
32
- def minimum_value(minimum_value: int):
33
- return BindingConfig(MinimumValue, minimum_value)
34
-
35
-
36
- def maximum_length(maximum_length: int):
37
- return BindingConfig(MaximumLength, maximum_length)
38
-
39
-
40
- def maximum_value(maximum_value: int):
41
- return BindingConfig(MaximumValue, maximum_value)
42
-
43
-
44
- def required():
45
- return BindingConfig(Required)
46
-
47
-
48
- def unique():
49
- return BindingConfig(Unique)
50
-
51
-
52
- def in_the_future_at_least(time_delta: datetime.timedelta):
53
- return BindingConfig(InTheFutureAtLeast, time_delta)
54
-
55
-
56
- def in_the_future_at_most(time_delta: datetime.timedelta):
57
- return BindingConfig(InTheFutureAtMost, time_delta)
58
-
59
-
60
- def in_the_past_at_least(time_delta: datetime.timedelta):
61
- return BindingConfig(InThePastAtLeast, time_delta)
62
-
63
-
64
- def in_the_past_at_most(time_delta: datetime.timedelta):
65
- return BindingConfig(InThePastAtMost, time_delta)
66
-
67
-
68
- __all__ = [
69
- "in_the_future_at_least",
70
- "in_the_future_at_most",
71
- "in_the_past_at_least",
72
- "in_the_past_at_most",
73
- "minimum_length",
74
- "maximum_length",
75
- "required",
76
- "TimeDelta",
77
- "unique",
78
- ]
@@ -1,36 +0,0 @@
1
- from .requirement import Requirement
2
- import datetime
3
- import dateparser
4
-
5
-
6
- class After(Requirement):
7
- def configure(self, other_column_name: str, allow_equal: bool = False):
8
- self.other_column_name = other_column_name
9
- self.allow_equal = allow_equal
10
-
11
- def check(self, model, data):
12
- # we won't check anything for missing values (columns should be required if that is an issue)
13
- if not data.get(self.column_name):
14
- return ""
15
- my_value = data[self.column_name]
16
- other_value = data.get(self.other_column_name, model.__getitem__(self.other_column_name))
17
- # again, no checks for non-values
18
- if not other_value:
19
- return ""
20
-
21
- my_value_as_date = dateparser.parse(data[self.column_name])
22
- if not my_value_as_date:
23
- return f"'{self.column_name}' was not a valid date."
24
-
25
- if type(other_value) != str and type(other_value) != datetime.datetime:
26
- return f"'{other_column_name}' was not a valid date."
27
- other_value_as_date = dateparser.parse(other_value) if type(other_value) == str else other_value
28
- if not other_value_as_date:
29
- return f"'{self.other_column_name}' was not a valid date."
30
-
31
- if my_value_as_date == other_value_as_date:
32
- return "" if self.allow_equal else f"'{self.column_name}' must be after '{self.other_column_name}'"
33
-
34
- if my_value_as_date < other_value_as_date:
35
- return f"'{self.column_name}' must be after '{self.other_column_name}'"
36
- return ""
@@ -1,36 +0,0 @@
1
- from .requirement import Requirement
2
- import datetime
3
- import dateparser
4
-
5
-
6
- class Before(Requirement):
7
- def configure(self, other_column_name: str, allow_equal: bool = False):
8
- self.other_column_name = other_column_name
9
- self.allow_equal = allow_equal
10
-
11
- def check(self, model, data):
12
- # we won't check anything for missing values (columns should be required if that is an issue)
13
- if not data.get(self.column_name):
14
- return ""
15
- my_value = data[self.column_name]
16
- other_value = data.get(self.other_column_name, model.__getitem__(self.other_column_name))
17
- # again, no checks for non-values
18
- if not other_value:
19
- return ""
20
-
21
- my_value_as_date = dateparser.parse(data[self.column_name])
22
- if not my_value_as_date:
23
- return f"'{self.column_name}' was not a valid date."
24
-
25
- if type(other_value) != str and type(other_value) != datetime.datetime:
26
- return f"'{other_column_name}' was not a valid date."
27
- other_value_as_date = dateparser.parse(other_value) if type(other_value) == str else other_value
28
- if not other_value_as_date:
29
- return f"'{self.other_column_name}' was not a valid date."
30
-
31
- if my_value_as_date == other_value_as_date:
32
- return "" if self.allow_equal else f"'{self.column_name}' must be before '{self.other_column_name}'"
33
-
34
- if my_value_as_date > other_value_as_date:
35
- return f"'{self.column_name}' must be before '{self.other_column_name}'"
36
- return ""
@@ -1,19 +0,0 @@
1
- from .time_delta import TimeDelta
2
- import datetime
3
- import dateparser
4
-
5
-
6
- class InTheFutureAtLeast(TimeDelta):
7
- def check(self, model, data):
8
- if self.column_name not in data or not data[self.column_name]:
9
- return ""
10
- as_date = dateparser.parse(data[self.column_name])
11
- if not as_date:
12
- return f"'{self.column_name}' was not a valid date"
13
- now = (
14
- self.datetime.datetime.now() if not as_date.tzinfo else self.datetime.datetime.now(tz=datetime.timezone.utc)
15
- )
16
- if as_date < now + self.time_delta:
17
- human_friendly = self.delta_human_friendly()
18
- return f"'{self.column_name}' must be at least {human_friendly} in the future."
19
- return ""
@@ -1,19 +0,0 @@
1
- from .time_delta import TimeDelta
2
- import datetime
3
- import dateparser
4
-
5
-
6
- class InTheFutureAtMost(TimeDelta):
7
- def check(self, model, data):
8
- if self.column_name not in data or not data[self.column_name]:
9
- return ""
10
- as_date = dateparser.parse(data[self.column_name])
11
- if not as_date:
12
- return f"'{self.column_name}' was not a valid date"
13
- now = (
14
- self.datetime.datetime.now() if not as_date.tzinfo else self.datetime.datetime.now(tz=datetime.timezone.utc)
15
- )
16
- if as_date > now + self.time_delta:
17
- human_friendly = self.delta_human_friendly()
18
- return f"'{self.column_name}' must be at most {human_friendly} in the future."
19
- return ""
@@ -1,19 +0,0 @@
1
- from .time_delta import TimeDelta
2
- import datetime
3
- import dateparser
4
-
5
-
6
- class InThePastAtLeast(TimeDelta):
7
- def check(self, model, data):
8
- if self.column_name not in data or not data[self.column_name]:
9
- return ""
10
- as_date = dateparser.parse(data[self.column_name])
11
- if not as_date:
12
- return f"'{self.column_name}' was not a valid date"
13
- now = (
14
- self.datetime.datetime.now() if not as_date.tzinfo else self.datetime.datetime.now(tz=datetime.timezone.utc)
15
- )
16
- if as_date > now - self.time_delta:
17
- human_friendly = self.delta_human_friendly()
18
- return f"'{self.column_name}' must be at least {human_friendly} in the past."
19
- return ""
@@ -1,19 +0,0 @@
1
- from .time_delta import TimeDelta
2
- import datetime
3
- import dateparser
4
-
5
-
6
- class InThePastAtMost(TimeDelta):
7
- def check(self, model, data):
8
- if self.column_name not in data or not data[self.column_name]:
9
- return ""
10
- as_date = dateparser.parse(data[self.column_name])
11
- if not as_date:
12
- return f"'{self.column_name}' was not a valid date"
13
- now = (
14
- self.datetime.datetime.now() if not as_date.tzinfo else self.datetime.datetime.now(tz=datetime.timezone.utc)
15
- )
16
- if as_date < now - self.time_delta:
17
- human_friendly = self.delta_human_friendly()
18
- return f"'{self.column_name}' must be at most {human_friendly} in the past."
19
- return ""
@@ -1,19 +0,0 @@
1
- from .requirement import Requirement
2
-
3
-
4
- class MaximumLength(Requirement):
5
- maximum_length = None
6
-
7
- def configure(self, maximum_length):
8
- if type(maximum_length) != int:
9
- raise ValueError(
10
- f"Maximum length must be an int to use the MaximumLength class for column '{self.column_name}'"
11
- )
12
- self.maximum_length = maximum_length
13
-
14
- def check(self, model, data):
15
- if self.column_name not in data or not data[self.column_name]:
16
- return ""
17
- if len(data[self.column_name]) <= self.maximum_length:
18
- return ""
19
- return f"'{self.column_name}' must be at most {self.maximum_length} characters long."
@@ -1,19 +0,0 @@
1
- from .requirement import Requirement
2
-
3
-
4
- class MaximumValue(Requirement):
5
- maximum_value = None
6
-
7
- def configure(self, maximum_value):
8
- if type(maximum_value) != int:
9
- raise ValueError(
10
- f"Maximum value must be an int to use the MaximumValue class for column '{self.column_name}'"
11
- )
12
- self.maximum_value = maximum_value
13
-
14
- def check(self, model, data):
15
- if self.column_name not in data or not data[self.column_name]:
16
- return ""
17
- if int(data[self.column_name]) <= self.maximum_value:
18
- return ""
19
- return f"'{self.column_name}' must be at most {self.maximum_value}."
@@ -1,22 +0,0 @@
1
- from .requirement import Requirement
2
-
3
-
4
- class MinimumLength(Requirement):
5
- minimum_length = None
6
-
7
- def configure(self, minimum_length):
8
- if type(minimum_length) != int:
9
- raise ValueError(
10
- f"Minimum length must be an int to use the MinimumLength class for column '{self.column_name}'"
11
- )
12
- self.minimum_length = minimum_length
13
-
14
- def check(self, model, data):
15
- # If the column isn't in the data then skip this check. Otherwise, setting a minimum length would implicitly
16
- # make a column required, and this will likely cause more problems than it solves. In short, the minimum
17
- # length should only apply if data is actually being set
18
- if self.column_name not in data or not data[self.column_name]:
19
- return ""
20
- if len(data[self.column_name]) >= self.minimum_length:
21
- return ""
22
- return f"'{self.column_name}' must be at least {self.minimum_length} characters long."
@@ -1,19 +0,0 @@
1
- from .requirement import Requirement
2
-
3
-
4
- class MinimumValue(Requirement):
5
- minimum_value = None
6
-
7
- def configure(self, minimum_value):
8
- if type(minimum_value) != int:
9
- raise ValueError(
10
- f"Minimum value must be an int to use the MinimumValue class for column '{self.column_name}'"
11
- )
12
- self.minimum_value = minimum_value
13
-
14
- def check(self, model, data):
15
- if self.column_name not in data or not data[self.column_name]:
16
- return ""
17
- if int(data[self.column_name]) >= self.minimum_value:
18
- return ""
19
- return f"'{self.column_name}' must be at least {self.minimum_value}."
@@ -1,23 +0,0 @@
1
- from .requirement import Requirement
2
-
3
-
4
- class Required(Requirement):
5
- def check(self, model, data):
6
- # you'd think that "required" is straight forward and we want an input error if it isn't found.
7
- # this isn't strictly true though. If the model already exists, the column has a value in the model already,
8
- # and the column is completely missing from the input data, then it is actually perfectly fine (because
9
- # there will still be a value in the column after the save). However, if the model doesn't exist, then
10
- # we must require the column in the data with an actual value.
11
- has_value = False
12
- has_some_value = False
13
- if self.column_name in data:
14
- has_some_value = True
15
- if type(data[self.column_name]) == str:
16
- has_value = bool(data[self.column_name].strip())
17
- else:
18
- has_value = bool(data[self.column_name])
19
- if has_value:
20
- return ""
21
- if model.exists and model[self.column_name] and not has_some_value:
22
- return ""
23
- return f"'{self.column_name}' is required."
@@ -1,25 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
-
4
- class Requirement(ABC):
5
- _column_name = None
6
-
7
- @property
8
- def column_name(self):
9
- if self._column_name is None:
10
- raise ValueError("Attempt to get column name on requirement before setting it")
11
- return self._column_name
12
-
13
- @column_name.setter
14
- def column_name(self, column_name):
15
- self._column_name = column_name
16
-
17
- def configure(self, *arguments):
18
- pass
19
-
20
- @abstractmethod
21
- def check(self, data):
22
- pass
23
-
24
- def additional_write_columns(self, is_create=False):
25
- return {}
@@ -1,38 +0,0 @@
1
- from .requirement import Requirement
2
- import datetime
3
- from collections import OrderedDict
4
-
5
-
6
- class TimeDelta(Requirement):
7
- time_delta = None
8
-
9
- def __init__(self, datetime):
10
- self.datetime = datetime
11
-
12
- def configure(self, time_delta: datetime.timedelta):
13
- if type(time_delta) != datetime.timedelta:
14
- raise ValueError(
15
- "The argument for all time-related input requirement classes is a datetime.timedelta object, but I received something else."
16
- )
17
- self.time_delta = time_delta
18
- self.human_friendly = None
19
-
20
- def delta_human_friendly(self):
21
- remainder = int(self.time_delta.total_seconds())
22
- parts = []
23
- conversion = OrderedDict(
24
- [
25
- ("year", 31536000),
26
- ("day", 86400),
27
- ("hour", 3600),
28
- ("minute", 60),
29
- ("second", 1),
30
- ]
31
- )
32
- for name, num_seconds in conversion.items():
33
- if num_seconds > remainder:
34
- continue
35
- amount = int(remainder / num_seconds)
36
- remainder -= amount * num_seconds
37
- parts.append(f"{amount} {name}" + ("s" if amount != 1 else ""))
38
- return ", ".join(parts)
@@ -1,18 +0,0 @@
1
- from .requirement import Requirement
2
-
3
-
4
- class Unique(Requirement):
5
- def check(self, model, data):
6
- # Unique is mildly tricky. We obviously want to search the backend for the new value,
7
- # but we need to first skip this if our column is not being set, or if we're editing
8
- # the model and nothing is changing.
9
- if self.column_name not in data:
10
- return ""
11
- new_value = data[self.column_name]
12
- if model.exists and model.__getattr__(self.column_name) == new_value:
13
- return ""
14
-
15
- matching_model = model.find(f"{self.column_name}={new_value}")
16
- if matching_model.exists:
17
- return f"Invalid value for '{self.column_name}': the given value already exists, and must be unique."
18
- return ""
@@ -1,7 +0,0 @@
1
- from .models import Models
2
- from .input_output import InputOutput
3
-
4
- __all__ = [
5
- "Models",
6
- "InputOutput",
7
- ]
@@ -1,124 +0,0 @@
1
- import json
2
- from .. import input_outputs
3
-
4
-
5
- class InputOutput(input_outputs.InputOutput):
6
- _body = None
7
- _request_method = None
8
- _script_name = ""
9
- _path_info = ""
10
- _query_string = ""
11
- _content_type = ""
12
- _protocol = ""
13
- _query_parameters = None
14
- _context_specifics = None
15
- _client_ip = None
16
- response = None
17
-
18
- def __init__(
19
- self,
20
- request_headers=None,
21
- body=None,
22
- request_method="GET",
23
- request_url="",
24
- script_name="",
25
- path_info="",
26
- query_string="",
27
- content_type="",
28
- protocol="",
29
- query_parameters=None,
30
- authorization_data=None,
31
- context_specifics=None,
32
- client_ip=None,
33
- ):
34
- self.set_request_method(request_method)
35
- self.set_body(body)
36
- self.set_request_headers(request_headers)
37
- self.set_request_url(request_url, script_name=script_name)
38
- self._path_info = path_info
39
- self._query_string = query_string
40
- self._content_type = content_type
41
- self._protocol = protocol
42
- self._query_parameters = query_parameters if query_parameters is not None else {}
43
- self._authorization_data = authorization_data
44
- self._context_specifics = context_specifics if context_specifics is not None else {}
45
- self._client_ip = client_ip if client_ip is not None else "127.0.0.1"
46
-
47
- def respond(self, body, status_code=200):
48
- self.response = {"body": body, "status_code": status_code, "headers": self._response_headers}
49
- return (body, status_code)
50
-
51
- def get_body(self):
52
- return self._body
53
-
54
- def has_body(self):
55
- return bool(self._body)
56
-
57
- def set_body(self, body):
58
- self._body = None
59
- if body:
60
- self._body = body if type(body) == str else json.dumps(body)
61
-
62
- def set_request_url(self, request_url, script_name=""):
63
- if request_url and script_name:
64
- raise ValueError("You cannot specify both request_url and script_name")
65
- self._script_name = request_url if request_url else script_name
66
-
67
- def get_request_method(self):
68
- return self._request_method
69
-
70
- def set_request_method(self, request_method):
71
- self._request_method = request_method.upper()
72
-
73
- def set_request_headers(self, request_headers):
74
- self._request_headers = {}
75
- if request_headers is None:
76
- request_headers = {}
77
- for key, value in request_headers.items():
78
- self._request_headers[key.lower()] = value
79
-
80
- def set_query_parameters(self, query_parameters):
81
- if query_parameters is None:
82
- self._query_parameters = {}
83
- else:
84
- self._query_parameters = query_parameters
85
-
86
- def get_script_name(self):
87
- return self._script_name
88
-
89
- def get_path_info(self):
90
- return self._path_info
91
-
92
- def get_query_string(self):
93
- return self._query_string
94
-
95
- def get_content_type(self):
96
- return self._content_type
97
-
98
- def get_protocol(self):
99
- return self._protocol
100
-
101
- def has_request_header(self, header_name):
102
- return header_name.lower() in self._request_headers
103
-
104
- def get_request_header(self, header_name, silent=True):
105
- if not self.has_request_header(header_name):
106
- if not silent:
107
- raise ValueError(f"Request header '{header_name}' not found in request")
108
- return ""
109
- return self._request_headers[header_name.lower()]
110
-
111
- def get_query_parameter(self, key):
112
- return self._query_parameters[key] if key in self._query_parameters else ""
113
-
114
- def get_query_parameters(self):
115
- return self._query_parameters
116
-
117
- def set_context_specifics(self, context_specifics):
118
- self._context_specifics = context_specifics
119
-
120
- def context_specifics(self):
121
- return self._context_specifics
122
-
123
- def get_client_ip(self):
124
- return self._client_ip