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,26 +0,0 @@
1
- from .string import String
2
-
3
-
4
- class CreatedByAuthorizationData(String):
5
- required_configs = [
6
- "authorization_data_key_name",
7
- ]
8
-
9
- def __init__(self, di):
10
- super().__init__(di)
11
-
12
- @property
13
- def is_writeable(self):
14
- return False
15
-
16
- def pre_save(self, data, model):
17
- if model.exists:
18
- return data
19
-
20
- authorization_data = self.di.build("input_output", cache=True).get_authorization_data()
21
- # data comes last so that it can override the info in the authorization data. This seems counter-intuitive,
22
- # but is important. You would think that you *don't* want the data from the authorization data to be
23
- # overridden (since this is mainly used for logging), but the trouble is that there are a variety of use-cases
24
- # where the application must provide the audit data. Examples include registration and login. In these
25
- # cases, authorization data will be empty, and must be provided by the applicaiton.
26
- return {self.name: authorization_data.get(self.config("authorization_data_key_name"), "N/A"), **data}
@@ -1,24 +0,0 @@
1
- from .string import String
2
-
3
-
4
- class CreatedByHeader(String):
5
- required_configs = [
6
- "header_name",
7
- ]
8
-
9
- def __init__(self, di):
10
- super().__init__(di)
11
-
12
- @property
13
- def is_writeable(self):
14
- return False
15
-
16
- def pre_save(self, data, model):
17
- if model.exists:
18
- return data
19
-
20
- input_output = self.di.build("input_output", cache=True)
21
- return {
22
- **data,
23
- self.name: input_output.get_request_header(self.config("header_name")),
24
- }
@@ -1,17 +0,0 @@
1
- from .string import String
2
-
3
-
4
- class CreatedByIp(String):
5
- def __init__(self, di):
6
- super().__init__(di)
7
-
8
- @property
9
- def is_writeable(self):
10
- return False
11
-
12
- def pre_save(self, data, model):
13
- if model.exists:
14
- return data
15
-
16
- input_output = self.di.build("input_output", cache=True)
17
- return {**data, self.name: input_output.get_client_ip()}
@@ -1,25 +0,0 @@
1
- from .string import String
2
-
3
-
4
- class CreatedByRoutingData(String):
5
- required_configs = [
6
- "routing_data_name",
7
- ]
8
-
9
- def __init__(self, di):
10
- super().__init__(di)
11
-
12
- @property
13
- def is_writeable(self):
14
- return False
15
-
16
- def pre_save(self, data, model):
17
- if model.exists:
18
- return data
19
-
20
- input_output = self.di.build("input_output", cache=True)
21
- routing_data = input_output.routing_data()
22
- return {
23
- **data,
24
- self.name: routing_data[self.config("routing_data_name")],
25
- }
@@ -1,17 +0,0 @@
1
- from .string import String
2
-
3
-
4
- class CreatedByUserAgent(String):
5
- def __init__(self, di):
6
- super().__init__(di)
7
-
8
- @property
9
- def is_writeable(self):
10
- return False
11
-
12
- def pre_save(self, data, model):
13
- if model.exists:
14
- return data
15
-
16
- input_output = self.di.build("input_output", cache=True)
17
- return {**data, self.name: input_output.get_request_header("user-agent")}
@@ -1,26 +0,0 @@
1
- from .datetime_micro import DateTimeMicro
2
-
3
-
4
- class CreatedMicro(DateTimeMicro):
5
- my_configs = [
6
- "date_format",
7
- "default_date",
8
- "utc",
9
- ]
10
-
11
- def __init__(self, di, datetime):
12
- super().__init__(di)
13
- self.datetime = datetime
14
-
15
- @property
16
- def is_writeable(self):
17
- return False
18
-
19
- def pre_save(self, data, model):
20
- if model.exists:
21
- return data
22
- if self.config("utc", silent=True):
23
- now = self.datetime.datetime.now(self.datetime.timezone.utc)
24
- else:
25
- now = self.datetime.datetime.now()
26
- return {**data, self.name: now}
@@ -1,108 +0,0 @@
1
- from .column import Column
2
- from datetime import datetime, timezone
3
- import dateparser
4
- from ..autodoc.schema import DateTime as AutoDocDateTime
5
-
6
-
7
- class DateTime(Column):
8
- _auto_doc_class = AutoDocDateTime
9
- _date_format = "%Y-%m-%d %H:%M:%S"
10
- _default_date = "0000-00-00 00:00:00"
11
-
12
- my_configs = [
13
- "date_format",
14
- "default_date",
15
- ]
16
-
17
- def __init__(self, di):
18
- super().__init__(di)
19
-
20
- def _finalize_configuration(self, configuration):
21
- return {
22
- **{
23
- "date_format": self._date_format,
24
- "default_date": self._default_date,
25
- },
26
- **super()._finalize_configuration(configuration),
27
- }
28
-
29
- def from_backend(self, value):
30
- if not value or value == self.config("default_date"):
31
- date = None
32
- elif type(value) == str:
33
- date = dateparser.parse(value)
34
- else:
35
- date = value
36
- return date.replace(tzinfo=timezone.utc) if date else None
37
-
38
- def to_backend(self, data):
39
- if not self.name in data or type(data[self.name]) == str or data[self.name] == None:
40
- return data
41
-
42
- # hopefully this is a Python datetime object in UTC timezone...
43
- return {**data, **{self.name: data[self.name].strftime(self.config("date_format"))}}
44
-
45
- def to_json(self, model):
46
- datetime = model.get(self.name, silent=True)
47
- return {self.name: datetime.isoformat() if datetime else None}
48
-
49
- def build_condition(self, value, operator=None, column_prefix=""):
50
- date = dateparser.parse(value).astimezone(timezone.utc).strftime(self.config("date_format"))
51
- if not operator:
52
- operator = "="
53
- return f"{column_prefix}{self.name}{operator}{date}"
54
-
55
- def is_allowed_operator(self, operator, relationship_reference=None):
56
- """
57
- This is called when processing user data to decide if the end-user is specifying an allowed operator
58
- """
59
- return operator in ["=", "<", ">", "<=", ">="]
60
-
61
- def input_error_for_value(self, value, operator=None):
62
- value = dateparser.parse(value)
63
- if not value:
64
- return "given value did not appear to be a valid date"
65
- if not value.tzinfo:
66
- return "date is missing timezone information"
67
- return ""
68
-
69
- def values_match(self, value_1, value_2):
70
- """
71
- Compares two values to see if they are the same
72
- """
73
- # in this function we deal with data directly out of the backend, so our date is likely
74
- # to be string-ified and we want to look for default (e.g. null) values in string form.
75
- if type(value_1) == str and "0000-00-00" in value_1:
76
- value_1 = None
77
- if type(value_2) == str and "0000-00-00" in value_2:
78
- value_2 = None
79
- number_values = 0
80
- if value_1:
81
- number_values += 1
82
- if value_2:
83
- number_values += 1
84
- if number_values == 0:
85
- return True
86
- if number_values == 1:
87
- return False
88
-
89
- if type(value_1) == str:
90
- value_1 = dateparser.parse(value_1)
91
- if type(value_2) == str:
92
- value_2 = dateparser.parse(value_2)
93
-
94
- # we need to make sure we're comparing in the same timezones. For our purposes, a difference in timezone
95
- # is fine as long as they represent the same time (e.g. 16:00EST == 20:00UTC). For python, same time in different
96
- # timezones is treated as different datetime objects.
97
- if value_1.tzinfo is not None and value_2.tzinfo is not None:
98
- value_1 = value_1.astimezone(value_2.tzinfo)
99
-
100
- # two times can be the same but if one is datetime-aware and one is not, python will treat them as not equal.
101
- # we want to treat such times as being the same. Therefore, check for equality but ignore the timezone.
102
- for to_check in ["year", "month", "day", "hour", "minute", "second", "microsecond"]:
103
- if getattr(value_1, to_check) != getattr(value_2, to_check):
104
- return False
105
-
106
- # and since we already converted the timezones to match (or one has a timezone and one doesn't), we're good to go.
107
- # if we passed the above loop then the times are the same.
108
- return True
@@ -1,12 +0,0 @@
1
- from .datetime import DateTime
2
- from datetime import datetime, timezone
3
- import dateparser
4
- from ..autodoc.schema import DateTime as AutoDocDateTime
5
-
6
-
7
- class DateTimeMicro(DateTime):
8
- _date_format = "%Y-%m-%d %H:%M:%S.%f"
9
- _default_date = "0000-00-00 00:00:00.000000"
10
-
11
- def __init__(self, di):
12
- super().__init__(di)
@@ -1,18 +0,0 @@
1
- from .string import String
2
- import re
3
-
4
-
5
- class Email(String):
6
- def __init__(self, di):
7
- super().__init__(di)
8
-
9
- def input_error_for_value(self, value, operator=None):
10
- if type(value) != str:
11
- return f"Value must be a string for {self.name}"
12
- if operator and operator.lower() == "like":
13
- # don't check for an email if doing a fuzzy search, since we may be searching
14
- # for a partial email
15
- return ""
16
- if re.search(r"^[^@\s]+@[^@]+\.[^@]+$", value):
17
- return ""
18
- return "Invalid email address"
@@ -1,43 +0,0 @@
1
- from .column import Column
2
- from ..autodoc.schema import Number as AutoDocNumber
3
-
4
-
5
- class Float(Column):
6
- _auto_doc_class = AutoDocNumber
7
-
8
- def __init__(self, di):
9
- super().__init__(di)
10
-
11
- def from_backend(self, value):
12
- return float(value)
13
-
14
- def to_backend(self, data):
15
- if self.name not in data or data[self.name] is None:
16
- return data
17
-
18
- return {
19
- **data,
20
- self.name: float(data[self.name]),
21
- }
22
-
23
- def check_input(self, model, data):
24
- if not self.name in data:
25
- return ""
26
- if isinstance(data[self.name], int) or isinstance(data[self.name], float) or data[self.name] == None:
27
- return ""
28
- return f"Invalid input: {self.name} must be an integer or float"
29
-
30
- def build_condition(self, value, operator=None, column_prefix=""):
31
- if not operator:
32
- operator = "="
33
- return f"{column_prefix}{self.name}{operator}{value}"
34
-
35
- def is_allowed_operator(self, operator, relationship_reference=None):
36
- return operator in ["=", "<", ">", "<=", ">="]
37
-
38
- def input_error_for_value(self, value, operator=None):
39
- return (
40
- "value should be an integer or float"
41
- if (type(value) != int and type(value) != float and value is not None)
42
- else ""
43
- )
@@ -1,139 +0,0 @@
1
- from .column import Column
2
- import re
3
- from collections import OrderedDict
4
- from ..autodoc.schema import Array as AutoDocArray
5
- from ..autodoc.schema import Object as AutoDocObject
6
- from ..autodoc.schema import String as AutoDocString
7
-
8
-
9
- class HasMany(Column):
10
- """
11
- Controls a has-many relationship.
12
-
13
- This is a readonly column. When used in a model context it will return an iterable with the related child records.
14
- When used in an API context, it will convert the child records into a list of objects.
15
-
16
- It assumes that the foreign id in the child table is `[parent_model_class_name]_id` in all lower case.
17
- e.g., if the parent model class is named Status, then it assumes an id in the child class called `status_id`.
18
- """
19
-
20
- required_configs = [
21
- "child_models_class",
22
- ]
23
-
24
- my_configs = [
25
- "foreign_column_name",
26
- "child_columns",
27
- "is_readable",
28
- "readable_child_columns",
29
- "parent_id_column_name",
30
- ]
31
-
32
- def __init__(self, di):
33
- super().__init__(di)
34
-
35
- @property
36
- def is_writeable(self):
37
- return False
38
-
39
- @property
40
- def is_readable(self):
41
- is_readable = self.config("is_readable", True)
42
- # default is_readable to False
43
- return True if (is_readable and is_readable is not None) else False
44
-
45
- def configure(self, name, configuration, model_class):
46
- if "child_models_class" not in configuration:
47
- raise KeyError(
48
- f"Missing required configuration 'child_models_class' for column '{name}' in model class "
49
- + f"'{model_class.__name__}'"
50
- )
51
- self.validate_models_class(configuration["child_models_class"])
52
- configuration["parent_id_column_name"] = model_class.id_column_name
53
-
54
- # if readable_child_columns is set then load up the child models/columns now, because we'll need it in the
55
- # _check_configuration step, but we don't want to load it there because we can't save it back into the config
56
- if "foreign_column_name" not in configuration:
57
- configuration["foreign_column_name"] = (
58
- re.sub(r"(?<!^)(?=[A-Z])", "_", model_class.__name__.replace("_", "")).lower() + "_id"
59
- )
60
-
61
- # continue normally now...
62
- super().configure(name, configuration, model_class)
63
-
64
- def _check_configuration(self, configuration):
65
- super()._check_configuration(configuration)
66
- if configuration.get("is_readable"):
67
- child_columns = self.di.build(configuration["child_models_class"], cache=True).raw_columns_configuration()
68
- error_prefix = f"Configuration error for '{self.name}' in '{self.model_class.__name__}':"
69
- if not "readable_child_columns" in configuration:
70
- raise ValueError(f"{error_prefix} must provide 'readable_child_columns' if is_readable is set")
71
- readable_child_columns = configuration["readable_child_columns"]
72
- if not hasattr(readable_child_columns, "__iter__"):
73
- raise ValueError(
74
- f"{error_prefix} 'readable_child_columns' should be an iterable "
75
- + "with the list of child columns to output."
76
- )
77
- if isinstance(readable_child_columns, str):
78
- raise ValueError(
79
- f"{error_prefix} 'readable_child_columns' should be an iterable "
80
- + "with the list of child columns to output."
81
- )
82
- for column_name in readable_child_columns:
83
- if column_name not in child_columns:
84
- raise ValueError(
85
- f"{error_prefix} 'readable_child_columns' references column named '{column_name}' but this"
86
- + "column does not exist in the model class."
87
- )
88
-
89
- def get_child_columns(self):
90
- if "child_columns" not in self.configuration:
91
- self.configuration["child_columns"] = self.child_models.columns()
92
- return self.configuration["child_columns"]
93
-
94
- def can_provide(self, column_name):
95
- return column_name == self.name
96
-
97
- def provide(self, data, column_name):
98
- foreign_column_name = self.config("foreign_column_name")
99
- id_column_name = self.config("parent_id_column_name")
100
- return self.child_models.where(f"{foreign_column_name}={data[id_column_name]}")
101
-
102
- def to_json(self, model):
103
- children = []
104
- columns = self.get_child_columns()
105
- for child in model.__getattr__(self.name):
106
- json = OrderedDict()
107
- child_id_column_name = child.id_column_name
108
- json = {
109
- **json,
110
- **columns[child_id_column_name].to_json(child),
111
- }
112
- for column_name in self.config("readable_child_columns"):
113
- json = {
114
- **json,
115
- **columns[column_name].to_json(child),
116
- }
117
- children.append(json)
118
- return {self.name: children}
119
-
120
- @property
121
- def child_models(self):
122
- return self.di.build(self.config("child_models_class"), cache=True)
123
-
124
- def documentation(self, name=None, example=None, value=None):
125
- columns = self.get_child_columns()
126
- child_id_column_name = self.child_models.get_id_column_name()
127
- child_properties = [columns[child_id_column_name].documentation()]
128
-
129
- for column_name in self.config("readable_child_columns"):
130
- child_docs = columns[column_name].documentation()
131
- if type(child_docs) != list:
132
- child_docs = [child_docs]
133
- child_properties.extend(child_docs)
134
-
135
- child_object = AutoDocObject(
136
- self.camel_to_nice(self.child_models.model_class().__name__),
137
- child_properties,
138
- )
139
- return AutoDocArray(name if name is not None else self.name, child_object, value=value)
@@ -1,41 +0,0 @@
1
- from .column import Column
2
- from ..autodoc.schema import Integer as AutoDocInteger
3
-
4
-
5
- class Integer(Column):
6
- _auto_doc_class = AutoDocInteger
7
-
8
- def __init__(self, di):
9
- super().__init__(di)
10
-
11
- def to_backend(self, data):
12
- if self.name not in data or data[self.name] is None:
13
- return data
14
-
15
- return {
16
- **data,
17
- self.name: int(data[self.name]),
18
- }
19
-
20
- def from_backend(self, value):
21
- return int(value)
22
-
23
- def input_error_for_value(self, value, operator=None):
24
- if operator == "in":
25
- if type(value) != list:
26
- return f'{self.name} must be an integer when searching with the "IN" operator'
27
- for val in value:
28
- if type(val) != int:
29
- return f"All items in {self.name} must be integers"
30
- return ""
31
- return f"{self.name} must be an integer" if type(value) != int else ""
32
-
33
- def build_condition(self, value, operator=None, column_prefix=""):
34
- if operator == "in":
35
- return f"{column_prefix}{self.name} IN (" + ",".join([str(val) for val in value]) + ")"
36
- if not operator:
37
- operator = "="
38
- return f"{column_prefix}{self.name}{operator}{value}"
39
-
40
- def is_allowed_operator(self, operator, relationship_reference=None):
41
- return operator in ["=", "<", ">", "<=", ">=", "in"]
@@ -1,25 +0,0 @@
1
- import json
2
- from .column import Column
3
-
4
-
5
- class JSON(Column):
6
- def __init__(self, di):
7
- super().__init__(di)
8
-
9
- def from_backend(self, value):
10
- if type(value) == list or type(value) == dict:
11
- return value
12
- if not value:
13
- return None
14
- try:
15
- return json.loads(value)
16
- except json.JSONDecodeError:
17
- return None
18
-
19
- def to_backend(self, data):
20
- if self.name in data:
21
- data[self.name] = json.dumps(data[self.name]) if data[self.name] else ""
22
- return data
23
-
24
- def to_json(self, model):
25
- return {self.name: model.get(self.name, silent=True)}