clear-skies 1.19.22__py3-none-any.whl → 2.0.23__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.
Files changed (362) hide show
  1. clear_skies-2.0.23.dist-info/METADATA +76 -0
  2. clear_skies-2.0.23.dist-info/RECORD +265 -0
  3. {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info}/WHEEL +1 -1
  4. clearskies/__init__.py +37 -21
  5. clearskies/action.py +7 -0
  6. clearskies/authentication/__init__.py +9 -38
  7. clearskies/authentication/authentication.py +44 -0
  8. clearskies/authentication/authorization.py +14 -8
  9. clearskies/authentication/authorization_pass_through.py +22 -0
  10. clearskies/authentication/jwks.py +135 -58
  11. clearskies/authentication/public.py +3 -26
  12. clearskies/authentication/secret_bearer.py +515 -44
  13. clearskies/autodoc/formats/oai3_json/__init__.py +2 -2
  14. clearskies/autodoc/formats/oai3_json/oai3_json.py +11 -9
  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 +10 -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 +16 -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 +56 -17
  41. clearskies/backends/api_backend.py +1128 -166
  42. clearskies/backends/backend.py +54 -85
  43. clearskies/backends/cursor_backend.py +246 -191
  44. clearskies/backends/memory_backend.py +514 -208
  45. clearskies/backends/secrets_backend.py +68 -31
  46. clearskies/column.py +1221 -0
  47. clearskies/columns/__init__.py +71 -0
  48. clearskies/columns/audit.py +306 -0
  49. clearskies/columns/belongs_to_id.py +478 -0
  50. clearskies/columns/belongs_to_model.py +129 -0
  51. clearskies/columns/belongs_to_self.py +109 -0
  52. clearskies/columns/boolean.py +110 -0
  53. clearskies/columns/category_tree.py +273 -0
  54. clearskies/columns/category_tree_ancestors.py +51 -0
  55. clearskies/columns/category_tree_children.py +126 -0
  56. clearskies/columns/category_tree_descendants.py +48 -0
  57. clearskies/columns/created.py +92 -0
  58. clearskies/columns/created_by_authorization_data.py +114 -0
  59. clearskies/columns/created_by_header.py +103 -0
  60. clearskies/columns/created_by_ip.py +90 -0
  61. clearskies/columns/created_by_routing_data.py +102 -0
  62. clearskies/columns/created_by_user_agent.py +89 -0
  63. clearskies/columns/date.py +232 -0
  64. clearskies/columns/datetime.py +284 -0
  65. clearskies/columns/email.py +78 -0
  66. clearskies/columns/float.py +149 -0
  67. clearskies/columns/has_many.py +529 -0
  68. clearskies/columns/has_many_self.py +62 -0
  69. clearskies/columns/has_one.py +21 -0
  70. clearskies/columns/integer.py +158 -0
  71. clearskies/columns/json.py +126 -0
  72. clearskies/columns/many_to_many_ids.py +335 -0
  73. clearskies/columns/many_to_many_ids_with_data.py +274 -0
  74. clearskies/columns/many_to_many_models.py +156 -0
  75. clearskies/columns/many_to_many_pivots.py +132 -0
  76. clearskies/columns/phone.py +162 -0
  77. clearskies/columns/select.py +95 -0
  78. clearskies/columns/string.py +102 -0
  79. clearskies/columns/timestamp.py +164 -0
  80. clearskies/columns/updated.py +107 -0
  81. clearskies/columns/uuid.py +83 -0
  82. clearskies/configs/README.md +105 -0
  83. clearskies/configs/__init__.py +170 -0
  84. clearskies/configs/actions.py +43 -0
  85. clearskies/configs/any.py +15 -0
  86. clearskies/configs/any_dict.py +24 -0
  87. clearskies/configs/any_dict_or_callable.py +25 -0
  88. clearskies/configs/authentication.py +23 -0
  89. clearskies/configs/authorization.py +23 -0
  90. clearskies/configs/boolean.py +18 -0
  91. clearskies/configs/boolean_or_callable.py +20 -0
  92. clearskies/configs/callable_config.py +20 -0
  93. clearskies/configs/columns.py +34 -0
  94. clearskies/configs/conditions.py +30 -0
  95. clearskies/configs/config.py +26 -0
  96. clearskies/configs/datetime.py +20 -0
  97. clearskies/configs/datetime_or_callable.py +21 -0
  98. clearskies/configs/email.py +10 -0
  99. clearskies/configs/email_list.py +17 -0
  100. clearskies/configs/email_list_or_callable.py +17 -0
  101. clearskies/configs/email_or_email_list_or_callable.py +59 -0
  102. clearskies/configs/endpoint.py +23 -0
  103. clearskies/configs/endpoint_list.py +29 -0
  104. clearskies/configs/float.py +18 -0
  105. clearskies/configs/float_or_callable.py +20 -0
  106. clearskies/configs/headers.py +28 -0
  107. clearskies/configs/integer.py +18 -0
  108. clearskies/configs/integer_or_callable.py +20 -0
  109. clearskies/configs/joins.py +30 -0
  110. clearskies/configs/list_any_dict.py +32 -0
  111. clearskies/configs/list_any_dict_or_callable.py +33 -0
  112. clearskies/configs/model_class.py +35 -0
  113. clearskies/configs/model_column.py +67 -0
  114. clearskies/configs/model_columns.py +58 -0
  115. clearskies/configs/model_destination_name.py +26 -0
  116. clearskies/configs/model_to_id_column.py +45 -0
  117. clearskies/configs/readable_model_column.py +11 -0
  118. clearskies/configs/readable_model_columns.py +11 -0
  119. clearskies/configs/schema.py +23 -0
  120. clearskies/configs/searchable_model_columns.py +11 -0
  121. clearskies/configs/security_headers.py +39 -0
  122. clearskies/configs/select.py +28 -0
  123. clearskies/configs/select_list.py +49 -0
  124. clearskies/configs/string.py +31 -0
  125. clearskies/configs/string_dict.py +34 -0
  126. clearskies/configs/string_list.py +47 -0
  127. clearskies/configs/string_list_or_callable.py +48 -0
  128. clearskies/configs/string_or_callable.py +18 -0
  129. clearskies/configs/timedelta.py +20 -0
  130. clearskies/configs/timezone.py +20 -0
  131. clearskies/configs/url.py +25 -0
  132. clearskies/configs/validators.py +45 -0
  133. clearskies/configs/writeable_model_column.py +11 -0
  134. clearskies/configs/writeable_model_columns.py +11 -0
  135. clearskies/configurable.py +78 -0
  136. clearskies/contexts/__init__.py +8 -8
  137. clearskies/contexts/cli.py +129 -43
  138. clearskies/contexts/context.py +93 -56
  139. clearskies/contexts/wsgi.py +79 -33
  140. clearskies/contexts/wsgi_ref.py +87 -0
  141. clearskies/cursors/__init__.py +7 -0
  142. clearskies/cursors/cursor.py +166 -0
  143. clearskies/cursors/from_environment/__init__.py +5 -0
  144. clearskies/cursors/from_environment/mysql.py +51 -0
  145. clearskies/cursors/from_environment/postgresql.py +49 -0
  146. clearskies/cursors/from_environment/sqlite.py +35 -0
  147. clearskies/cursors/mysql.py +61 -0
  148. clearskies/cursors/postgresql.py +61 -0
  149. clearskies/cursors/sqlite.py +62 -0
  150. clearskies/decorators.py +33 -0
  151. clearskies/decorators.pyi +10 -0
  152. clearskies/di/__init__.py +11 -7
  153. clearskies/di/additional_config.py +117 -3
  154. clearskies/di/additional_config_auto_import.py +12 -0
  155. clearskies/di/di.py +717 -126
  156. clearskies/di/inject/__init__.py +23 -0
  157. clearskies/di/inject/akeyless_sdk.py +16 -0
  158. clearskies/di/inject/by_class.py +24 -0
  159. clearskies/di/inject/by_name.py +22 -0
  160. clearskies/di/inject/di.py +16 -0
  161. clearskies/di/inject/environment.py +15 -0
  162. clearskies/di/inject/input_output.py +19 -0
  163. clearskies/di/inject/now.py +16 -0
  164. clearskies/di/inject/requests.py +16 -0
  165. clearskies/di/inject/secrets.py +15 -0
  166. clearskies/di/inject/utcnow.py +16 -0
  167. clearskies/di/inject/uuid.py +16 -0
  168. clearskies/di/injectable.py +32 -0
  169. clearskies/di/injectable_properties.py +131 -0
  170. clearskies/end.py +219 -0
  171. clearskies/endpoint.py +1303 -0
  172. clearskies/endpoint_group.py +333 -0
  173. clearskies/endpoints/__init__.py +25 -0
  174. clearskies/endpoints/advanced_search.py +519 -0
  175. clearskies/endpoints/callable.py +382 -0
  176. clearskies/endpoints/create.py +201 -0
  177. clearskies/endpoints/delete.py +133 -0
  178. clearskies/endpoints/get.py +267 -0
  179. clearskies/endpoints/health_check.py +181 -0
  180. clearskies/endpoints/list.py +567 -0
  181. clearskies/endpoints/restful_api.py +417 -0
  182. clearskies/endpoints/schema.py +185 -0
  183. clearskies/endpoints/simple_search.py +279 -0
  184. clearskies/endpoints/update.py +188 -0
  185. clearskies/environment.py +7 -3
  186. clearskies/exceptions/__init__.py +19 -0
  187. clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
  188. clearskies/exceptions/missing_dependency.py +2 -0
  189. clearskies/exceptions/moved_permanently.py +3 -0
  190. clearskies/exceptions/moved_temporarily.py +3 -0
  191. clearskies/functional/__init__.py +2 -2
  192. clearskies/functional/json.py +47 -0
  193. clearskies/functional/routing.py +92 -0
  194. clearskies/functional/string.py +19 -11
  195. clearskies/functional/validations.py +61 -9
  196. clearskies/input_outputs/__init__.py +9 -7
  197. clearskies/input_outputs/cli.py +135 -152
  198. clearskies/input_outputs/exceptions/__init__.py +6 -1
  199. clearskies/input_outputs/headers.py +54 -0
  200. clearskies/input_outputs/input_output.py +77 -123
  201. clearskies/input_outputs/programmatic.py +62 -0
  202. clearskies/input_outputs/wsgi.py +36 -48
  203. clearskies/model.py +1894 -199
  204. clearskies/query/__init__.py +12 -0
  205. clearskies/query/condition.py +228 -0
  206. clearskies/query/join.py +136 -0
  207. clearskies/query/query.py +193 -0
  208. clearskies/query/sort.py +27 -0
  209. clearskies/schema.py +82 -0
  210. clearskies/secrets/__init__.py +4 -31
  211. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
  212. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
  213. clearskies/secrets/akeyless.py +421 -155
  214. clearskies/secrets/exceptions/__init__.py +7 -1
  215. clearskies/secrets/exceptions/not_found_error.py +2 -0
  216. clearskies/secrets/exceptions/permissions_error.py +2 -0
  217. clearskies/secrets/secrets.py +12 -11
  218. clearskies/security_header.py +17 -0
  219. clearskies/security_headers/__init__.py +8 -8
  220. clearskies/security_headers/cache_control.py +47 -109
  221. clearskies/security_headers/cors.py +38 -92
  222. clearskies/security_headers/csp.py +76 -150
  223. clearskies/security_headers/hsts.py +14 -15
  224. clearskies/typing.py +11 -0
  225. clearskies/validator.py +36 -0
  226. clearskies/validators/__init__.py +33 -0
  227. clearskies/validators/after_column.py +61 -0
  228. clearskies/validators/before_column.py +15 -0
  229. clearskies/validators/in_the_future.py +29 -0
  230. clearskies/validators/in_the_future_at_least.py +13 -0
  231. clearskies/validators/in_the_future_at_most.py +12 -0
  232. clearskies/validators/in_the_past.py +29 -0
  233. clearskies/validators/in_the_past_at_least.py +12 -0
  234. clearskies/validators/in_the_past_at_most.py +12 -0
  235. clearskies/validators/maximum_length.py +25 -0
  236. clearskies/validators/maximum_value.py +28 -0
  237. clearskies/validators/minimum_length.py +25 -0
  238. clearskies/validators/minimum_value.py +28 -0
  239. clearskies/{input_requirements → validators}/required.py +18 -9
  240. clearskies/validators/timedelta.py +58 -0
  241. clearskies/validators/unique.py +28 -0
  242. clear_skies-1.19.22.dist-info/METADATA +0 -46
  243. clear_skies-1.19.22.dist-info/RECORD +0 -206
  244. clearskies/application.py +0 -29
  245. clearskies/authentication/auth0_jwks.py +0 -118
  246. clearskies/authentication/auth_exception.py +0 -2
  247. clearskies/authentication/jwks_jwcrypto.py +0 -39
  248. clearskies/backends/example_backend.py +0 -43
  249. clearskies/backends/file_backend.py +0 -48
  250. clearskies/backends/json_backend.py +0 -7
  251. clearskies/backends/restful_api_advanced_search_backend.py +0 -138
  252. clearskies/binding_config.py +0 -16
  253. clearskies/column_types/__init__.py +0 -184
  254. clearskies/column_types/audit.py +0 -235
  255. clearskies/column_types/belongs_to.py +0 -250
  256. clearskies/column_types/boolean.py +0 -60
  257. clearskies/column_types/category_tree.py +0 -226
  258. clearskies/column_types/column.py +0 -373
  259. clearskies/column_types/created.py +0 -26
  260. clearskies/column_types/created_by_authorization_data.py +0 -26
  261. clearskies/column_types/created_by_header.py +0 -24
  262. clearskies/column_types/created_by_ip.py +0 -17
  263. clearskies/column_types/created_by_routing_data.py +0 -25
  264. clearskies/column_types/created_by_user_agent.py +0 -17
  265. clearskies/column_types/created_micro.py +0 -26
  266. clearskies/column_types/datetime.py +0 -108
  267. clearskies/column_types/datetime_micro.py +0 -12
  268. clearskies/column_types/email.py +0 -18
  269. clearskies/column_types/float.py +0 -43
  270. clearskies/column_types/has_many.py +0 -139
  271. clearskies/column_types/integer.py +0 -41
  272. clearskies/column_types/json.py +0 -25
  273. clearskies/column_types/many_to_many.py +0 -278
  274. clearskies/column_types/many_to_many_with_data.py +0 -162
  275. clearskies/column_types/select.py +0 -11
  276. clearskies/column_types/string.py +0 -24
  277. clearskies/column_types/updated.py +0 -24
  278. clearskies/column_types/updated_micro.py +0 -24
  279. clearskies/column_types/uuid.py +0 -25
  280. clearskies/columns.py +0 -123
  281. clearskies/condition_parser.py +0 -172
  282. clearskies/contexts/build_context.py +0 -54
  283. clearskies/contexts/convert_to_application.py +0 -190
  284. clearskies/contexts/extract_handler.py +0 -37
  285. clearskies/contexts/test.py +0 -94
  286. clearskies/decorators/__init__.py +0 -39
  287. clearskies/decorators/auth0_jwks.py +0 -22
  288. clearskies/decorators/authorization.py +0 -10
  289. clearskies/decorators/binding_classes.py +0 -9
  290. clearskies/decorators/binding_modules.py +0 -9
  291. clearskies/decorators/bindings.py +0 -9
  292. clearskies/decorators/create.py +0 -10
  293. clearskies/decorators/delete.py +0 -10
  294. clearskies/decorators/docs.py +0 -14
  295. clearskies/decorators/get.py +0 -10
  296. clearskies/decorators/jwks.py +0 -26
  297. clearskies/decorators/merge.py +0 -124
  298. clearskies/decorators/patch.py +0 -10
  299. clearskies/decorators/post.py +0 -10
  300. clearskies/decorators/public.py +0 -11
  301. clearskies/decorators/response_headers.py +0 -10
  302. clearskies/decorators/return_raw_response.py +0 -9
  303. clearskies/decorators/schema.py +0 -10
  304. clearskies/decorators/secret_bearer.py +0 -24
  305. clearskies/decorators/security_headers.py +0 -10
  306. clearskies/di/standard_dependencies.py +0 -140
  307. clearskies/di/test_module/__init__.py +0 -6
  308. clearskies/di/test_module/another_module/__init__.py +0 -2
  309. clearskies/di/test_module/module_class.py +0 -5
  310. clearskies/handlers/__init__.py +0 -41
  311. clearskies/handlers/advanced_search.py +0 -271
  312. clearskies/handlers/base.py +0 -473
  313. clearskies/handlers/callable.py +0 -189
  314. clearskies/handlers/create.py +0 -35
  315. clearskies/handlers/crud_by_method.py +0 -18
  316. clearskies/handlers/database_connector.py +0 -32
  317. clearskies/handlers/delete.py +0 -61
  318. clearskies/handlers/exceptions/__init__.py +0 -5
  319. clearskies/handlers/exceptions/not_found.py +0 -3
  320. clearskies/handlers/get.py +0 -156
  321. clearskies/handlers/health_check.py +0 -59
  322. clearskies/handlers/input_processing.py +0 -79
  323. clearskies/handlers/list.py +0 -530
  324. clearskies/handlers/mygrations.py +0 -82
  325. clearskies/handlers/request_method_routing.py +0 -47
  326. clearskies/handlers/restful_api.py +0 -218
  327. clearskies/handlers/routing.py +0 -62
  328. clearskies/handlers/schema_helper.py +0 -128
  329. clearskies/handlers/simple_routing.py +0 -204
  330. clearskies/handlers/simple_routing_route.py +0 -192
  331. clearskies/handlers/simple_search.py +0 -136
  332. clearskies/handlers/update.py +0 -96
  333. clearskies/handlers/write.py +0 -193
  334. clearskies/input_requirements/__init__.py +0 -68
  335. clearskies/input_requirements/after.py +0 -36
  336. clearskies/input_requirements/before.py +0 -36
  337. clearskies/input_requirements/in_the_future_at_least.py +0 -19
  338. clearskies/input_requirements/in_the_future_at_most.py +0 -19
  339. clearskies/input_requirements/in_the_past_at_least.py +0 -19
  340. clearskies/input_requirements/in_the_past_at_most.py +0 -19
  341. clearskies/input_requirements/maximum_length.py +0 -19
  342. clearskies/input_requirements/minimum_length.py +0 -22
  343. clearskies/input_requirements/requirement.py +0 -25
  344. clearskies/input_requirements/time_delta.py +0 -38
  345. clearskies/input_requirements/unique.py +0 -18
  346. clearskies/mocks/__init__.py +0 -7
  347. clearskies/mocks/input_output.py +0 -124
  348. clearskies/mocks/models.py +0 -142
  349. clearskies/models.py +0 -345
  350. clearskies/security_headers/base.py +0 -12
  351. clearskies/tests/simple_api/models/__init__.py +0 -2
  352. clearskies/tests/simple_api/models/status.py +0 -23
  353. clearskies/tests/simple_api/models/user.py +0 -21
  354. clearskies/tests/simple_api/users_api.py +0 -64
  355. {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info/licenses}/LICENSE +0 -0
  356. /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
  357. /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
  358. /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
  359. /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
  360. /clearskies/{secrets/exceptions → exceptions}/not_found.py +0 -0
  361. /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
  362. /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
@@ -1,68 +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 .maximum_length import MaximumLength
8
- from .required import Required
9
- from .requirement import Requirement
10
- from .unique import Unique
11
- from .in_the_future_at_least import InTheFutureAtLeast
12
- from .in_the_future_at_most import InTheFutureAtMost
13
- from .in_the_past_at_least import InThePastAtLeast
14
- from .in_the_past_at_most import InThePastAtMost
15
- from .time_delta import TimeDelta
16
-
17
-
18
- def after(other_column_name: str, allow_equal: bool = False):
19
- return BindingConfig(After, other_column_name=other_column_name, allow_equal=allow_equal)
20
-
21
-
22
- def before(other_column_name: str, allow_equal: bool = False):
23
- return BindingConfig(Before, other_column_name=other_column_name, allow_equal=allow_equal)
24
-
25
-
26
- def minimum_length(minimum_length: int):
27
- return BindingConfig(MinimumLength, minimum_length)
28
-
29
-
30
- def maximum_length(maximum_length: int):
31
- return BindingConfig(MaximumLength, maximum_length)
32
-
33
-
34
- def required():
35
- return BindingConfig(Required)
36
-
37
-
38
- def unique():
39
- return BindingConfig(Unique)
40
-
41
-
42
- def in_the_future_at_least(time_delta: datetime.timedelta):
43
- return BindingConfig(InTheFutureAtLeast, time_delta)
44
-
45
-
46
- def in_the_future_at_most(time_delta: datetime.timedelta):
47
- return BindingConfig(InTheFutureAtMost, time_delta)
48
-
49
-
50
- def in_the_past_at_least(time_delta: datetime.timedelta):
51
- return BindingConfig(InThePastAtLeast, time_delta)
52
-
53
-
54
- def in_the_past_at_most(time_delta: datetime.timedelta):
55
- return BindingConfig(InThePastAtMost, time_delta)
56
-
57
-
58
- __all__ = [
59
- "in_the_future_at_least",
60
- "in_the_future_at_most",
61
- "in_the_past_at_least",
62
- "in_the_past_at_most",
63
- "minimum_length",
64
- "maximum_length",
65
- "required",
66
- "TimeDelta",
67
- "unique",
68
- ]
@@ -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,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,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
@@ -1,142 +0,0 @@
1
- from ..models import Models as ModelsBase
2
- from ..model import Model as ModelBase
3
- from ..columns import Columns
4
- from ..di import StandardDependencies
5
-
6
-
7
- class Model(ModelBase):
8
- _columns_configuration = None
9
-
10
- def set_columns_configuration(self, columns_configuration):
11
- self._columns_configuration = columns_configuration
12
-
13
- def columns_configuration(self):
14
- return self._columns_configuration
15
-
16
-
17
- class Models(ModelsBase):
18
- _model_configuration = None
19
- updated = None
20
- created = None
21
- deleted = None
22
- create_responses = None
23
- update_responses = None
24
- search_responses = None
25
- iterating = None
26
- iterator_index = None
27
- iterated = None
28
- counted = None
29
-
30
- @classmethod
31
- def reset(cls):
32
- cls.updated = None
33
- cls.created = None
34
- cls.iterated = None
35
- cls.counted = None
36
- cls.deleted = None
37
-
38
- def __init__(self, model_configuration):
39
- self._model_configuration = model_configuration
40
- super().__init__(self, Columns(StandardDependencies()))
41
-
42
- def model(self, data):
43
- model_class = self.model_class()
44
- model = model_class(self._backend, self._columns)
45
- model.set_columns_configuration(self._model_configuration)
46
- model.data = data
47
- return model
48
-
49
- def model_class(self):
50
- return Model
51
-
52
- def table_name(self):
53
- return "mock_model"
54
-
55
- def blank(self):
56
- blank = self.__class__(self._model_configuration)
57
- blank.create_responses = self.create_responses
58
- blank.update_responses = self.update_responses
59
- blank.search_responses = self.search_responses
60
- return blank
61
-
62
- def add_update_response(self, data):
63
- if self.update_responses is None:
64
- self.update_responses = []
65
- self.update_responses.append(data)
66
-
67
- def add_create_response(self, data):
68
- if self.create_responses is None:
69
- self.create_responses = []
70
- self.create_responses.append(data)
71
-
72
- def add_search_response(self, data):
73
- # We're expecting a list because a search implicitly returns multiple records. Technically, we're also
74
- # okay with tuples, but this is the most straight-forward way to get what we want and should avoid weird
75
- # errors without causing serious issues later.
76
- if type(data) != list:
77
- raise ValueError("A list should be passed into to 'add_search_response'")
78
- if self.search_responses is None:
79
- self.search_responses = []
80
- self.search_responses.append(data)
81
-
82
- def clear_search_responses(self):
83
- self.search_responses = None
84
-
85
- # our mock models also acts as the backend for the mock model
86
- def update(self, id, data, model):
87
- if self.update_responses is None:
88
- raise ValueError("Must set update data through 'models.add_update_response' before attempting to update")
89
- if not len(self.update_responses):
90
- raise ValueError("Ran out of responses while processing an update!")
91
- if Models.updated is None:
92
- Models.updated = []
93
- Models.updated.append({"id": id, "data": data, "model": model})
94
- return self.update_responses.pop(0)
95
-
96
- def create(self, data, model):
97
- if self.create_responses is None:
98
- raise ValueError("Must set create data through 'models.add_create_response' before attempting to create")
99
- if not len(self.create_responses):
100
- raise ValueError("Ran out of responses while processing an create!")
101
- if Models.created is None:
102
- Models.created = []
103
- Models.created.append({"data": data, "model": model})
104
- return self.create_responses.pop(0)
105
-
106
- def delete(self, id, model):
107
- if Models.deleted is None:
108
- Models.deleted = []
109
- Models.deleted.append({"id": id, "model": model})
110
- return True
111
-
112
- def count(self, configuration, model):
113
- if self.search_responses is None:
114
- raise ValueError("Must set search data through 'models.add_search_response' before counting")
115
- if Models.counted == None:
116
- Models.counted = []
117
- del configuration["model_columns"]
118
- Models.counted.append(configuration)
119
- counted = self.search_responses.pop(0)
120
- return len(counted)
121
-
122
- def records(self, configuration, model, next_page_data=None):
123
- if self.search_responses is None:
124
- raise ValueError("Must set search data through 'models.add_search_response' before counting")
125
- if Models.iterated == None:
126
- Models.iterated = []
127
- del configuration["model_columns"]
128
- Models.iterated.append(configuration)
129
- records = self.search_responses.pop(0)
130
- return records
131
-
132
- def next(self):
133
- self.iterator_index += 1
134
- if self.iterator_index >= len(self.iterating):
135
- raise StopIteration()
136
- return self.iterating[self.iterator_index]
137
-
138
- def column_from_backend(self, column, value):
139
- return column.from_backend(value)
140
-
141
- def column_to_backend(self, column, backend_data):
142
- return column.to_backend(backend_data)