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,184 +0,0 @@
1
- from .audit import Audit
2
- from .belongs_to import BelongsTo
3
- from .boolean import Boolean
4
- from .category_tree import CategoryTree
5
- from .column import Column
6
- from .created import Created
7
- from .created_by_authorization_data import CreatedByAuthorizationData
8
- from .created_by_header import CreatedByHeader
9
- from .created_by_ip import CreatedByIp
10
- from .created_by_routing_data import CreatedByRoutingData
11
- from .created_by_user_agent import CreatedByUserAgent
12
- from .created_micro import CreatedMicro
13
- from .datetime import DateTime
14
- from .datetime_micro import DateTimeMicro
15
- from .email import Email
16
- from .float import Float
17
- from .has_many import HasMany
18
- from .integer import Integer
19
- from .json import JSON
20
- from .many_to_many import ManyToMany
21
- from .many_to_many_with_data import ManyToManyWithData
22
- from .select import Select
23
- from .string import String
24
- from .updated import Updated
25
- from .updated_micro import UpdatedMicro
26
- from .uuid import UUID
27
-
28
-
29
- def build_column_config(name, column_class, **kwargs):
30
- return (name, {**{"class": column_class}, **kwargs})
31
-
32
-
33
- def audit(name, **kwargs):
34
- return build_column_config(name, Audit, **kwargs)
35
-
36
-
37
- def belongs_to(name, **kwargs):
38
- return build_column_config(name, BelongsTo, **kwargs)
39
-
40
-
41
- def boolean(name, **kwargs):
42
- return build_column_config(name, Boolean, **kwargs)
43
-
44
-
45
- def category_tree(name, **kwargs):
46
- return build_column_config(name, CategoryTree, **kwargs)
47
-
48
-
49
- def created(name, **kwargs):
50
- return build_column_config(name, Created, **kwargs)
51
-
52
-
53
- def created_by_authorization_data(name, **kwargs):
54
- return build_column_config(name, CreatedByAuthorizationData, **kwargs)
55
-
56
-
57
- def created_by_header(name, **kwargs):
58
- return build_column_config(name, CreatedByHeader, **kwargs)
59
-
60
-
61
- def created_by_ip(name, **kwargs):
62
- return build_column_config(name, CreatedByIp, **kwargs)
63
-
64
-
65
- def created_by_routing_data(name, **kwargs):
66
- return build_column_config(name, CreatedByRoutingData, **kwargs)
67
-
68
-
69
- def created_by_user_agent(name, **kwargs):
70
- return build_column_config(name, CreatedByUserAgent, **kwargs)
71
-
72
-
73
- def created_micro(name, **kwargs):
74
- return build_column_config(name, CreatedMicro, **kwargs)
75
-
76
-
77
- def datetime(name, **kwargs):
78
- return build_column_config(name, DateTime, **kwargs)
79
-
80
-
81
- def datetime_micro(name, **kwargs):
82
- return build_column_config(name, DateTimeMicro, **kwargs)
83
-
84
-
85
- def email(name, **kwargs):
86
- return build_column_config(name, Email, **kwargs)
87
-
88
-
89
- def float(name, **kwargs):
90
- return build_column_config(name, Float, **kwargs)
91
-
92
-
93
- def has_many(name, **kwargs):
94
- return build_column_config(name, HasMany, **kwargs)
95
-
96
-
97
- def integer(name, **kwargs):
98
- return build_column_config(name, Integer, **kwargs)
99
-
100
-
101
- def json(name, **kwargs):
102
- return build_column_config(name, JSON, **kwargs)
103
-
104
-
105
- def many_to_many(name, **kwargs):
106
- return build_column_config(name, ManyToMany, **kwargs)
107
-
108
-
109
- def many_to_many_with_data(name, **kwargs):
110
- return build_column_config(name, ManyToManyWithData, **kwargs)
111
-
112
-
113
- def select(name, **kwargs):
114
- return build_column_config(name, Select, **kwargs)
115
-
116
-
117
- def string(name, **kwargs):
118
- return build_column_config(name, String, **kwargs)
119
-
120
-
121
- def updated(name, **kwargs):
122
- return build_column_config(name, Updated, **kwargs)
123
-
124
-
125
- def updated_micro(name, **kwargs):
126
- return build_column_config(name, UpdatedMicro, **kwargs)
127
-
128
-
129
- def uuid(name, **kwargs):
130
- return build_column_config(name, UUID, **kwargs)
131
-
132
-
133
- __all__ = [
134
- "build_column_config",
135
- "audit",
136
- "Audit",
137
- "belongs_to",
138
- "BelongsTo",
139
- "boolean",
140
- "Boolean",
141
- "category_tree",
142
- "CategoryTree",
143
- "Column",
144
- "created",
145
- "created_micro",
146
- "Created",
147
- "CreatdMicro",
148
- "created_by_authorization_data",
149
- "CreatedByAuthorizationData",
150
- "created_by_ip",
151
- "CreatedByIp",
152
- "created_by_user_agent",
153
- "CreatedByUserAgent",
154
- "CreatedMicro",
155
- "created_micro",
156
- "datetime",
157
- "datetime_micro",
158
- "DateTime",
159
- "DateTimeMicro",
160
- "email",
161
- "Email",
162
- "float",
163
- "Float",
164
- "has_many",
165
- "HasMany",
166
- "integer",
167
- "Integer",
168
- "json",
169
- "JSON",
170
- "many_to_many",
171
- "ManyToMany",
172
- "many_to_many_with_data",
173
- "ManyToManyWithData",
174
- "select",
175
- "Select",
176
- "string",
177
- "String",
178
- "updated",
179
- "updated_micro",
180
- "Updated",
181
- "UpdatedMicro",
182
- "uuid",
183
- "UUID",
184
- ]
@@ -1,235 +0,0 @@
1
- from . import string, has_many
2
- from clearskies.functional.string import title_case_to_snake_case
3
-
4
-
5
- class Audit(has_many.HasMany):
6
- """
7
- Enables auditing for a model.
8
-
9
- Specif the audit class to use and attach this to your model. Everytime the model is created/updated/deleted,
10
- the audit class will record the action and the changes. Your audit model must have the following columns:
11
-
12
- | Name | type |
13
- |-------------|----------|
14
- | class | str |
15
- | resource_id | str |
16
- | action | str |
17
- | data | json |
18
- | created_at | created |
19
-
20
- The names are not currently adjustable.
21
-
22
- 1. Class is a string that records the name of the class that the action happened for. This allows you to use
23
- the same audit class for multiple, different, resources.
24
- 2. resource_id is the id of the record which the audit entry is for.
25
- 3. Action is the actual action taken (create/update/delete)
26
- 4. Data is a serialized record of what columns in the record were changed (both their previous and new values)
27
- 5. The time the audit record was created
28
-
29
- With `exclude_columns` you can specify some names of columns to ignore. If an update happens and only columns
30
- in `exclude_columns` are being set, then a history entry will not be created. Also, these columns will
31
- not be included in the audit record.
32
-
33
- With `mask_columns` you can specify the names of columns which should be noted as updated in the audit record,
34
- but the actual values (before and after) should not be recorded.
35
- """
36
-
37
- _parent_columns = None
38
-
39
- required_configs = [
40
- "audit_models_class",
41
- ]
42
-
43
- my_configs = [
44
- "child_models_class",
45
- "exclude_columns",
46
- "mask_columns",
47
- "foreign_column_name",
48
- "is_readable",
49
- "readable_child_columns",
50
- "parent_class_name",
51
- "parent_id_column_name",
52
- ]
53
-
54
- def __init__(self, di):
55
- super().__init__(di)
56
-
57
- def configure(self, name, configuration, model_class):
58
- if "audit_models_class" not in configuration:
59
- raise KeyError(
60
- "Missing required configuration value 'audit_models_class' for column '{name}' in model class "
61
- + f"'{model_class.__name__}'"
62
- )
63
- self.validate_models_class(configuration["audit_models_class"])
64
- has_many_configuration = {
65
- **configuration,
66
- "child_models_class": configuration.get("audit_models_class"),
67
- "foreign_column_name": "resource_id",
68
- "is_readable": True,
69
- "readable_child_columns": ["resource_id", "action", "data", "created_at"],
70
- "parent_class_name": model_class.__name__,
71
- "exclude_columns": configuration.get("exclude_columns", []),
72
- "mask_columns": configuration.get("mask_columns", []),
73
- }
74
- super().configure(name, has_many_configuration, model_class)
75
-
76
- def _check_configuration(self, configuration):
77
- super()._check_configuration(configuration)
78
- error_prefix = f"Configuration error for '{self.name}' in '{self.model_class.__name__}':"
79
- audit_columns = self.di.build(configuration["audit_models_class"], cache=True).raw_columns_configuration()
80
- parent_columns = self.di.build(self.model_class, cache=True).raw_columns_configuration()
81
- required_audit_columns = {
82
- "class": string.String,
83
- "resource_id": True,
84
- "action": string.String,
85
- "data": True,
86
- "created_at": True,
87
- }
88
- for column_name, column_type in required_audit_columns.items():
89
- if column_name not in audit_columns:
90
- raise ValueError(f"{error_prefix} audit models class does not have the required column '{column_name}'")
91
- if column_type == True:
92
- continue
93
- if audit_columns[column_name]["class"] != column_type:
94
- raise ValueError(
95
- f"{error_prefix} the '{column_name}' column in the audit models class should have a type of "
96
- + column_type.__name__
97
- + " but it has something else"
98
- )
99
-
100
- for config_name in ["exclude_columns", "mask_columns"]:
101
- if config_name not in configuration:
102
- continue
103
-
104
- config_columns = configuration[config_name]
105
- if not hasattr(config_columns, "__iter__"):
106
- raise ValueError(f"{error_prefix} '{config_name}' should be an iterable with the list of column names.")
107
- if isinstance(config_columns, str):
108
- raise ValueError(
109
- f"{error_prefix} '{config_name}' should be an iterable " + "with a list of column names."
110
- )
111
- for column_name in config_columns:
112
- if column_name not in parent_columns:
113
- raise ValueError(
114
- f"{error_prefix} '{config_name}' references column named '{column_name}' but this"
115
- + " column does not exist in the original model class."
116
- )
117
-
118
- def provide(self, data, column_name):
119
- return super().provide(data, column_name).where("class=" + self.config("parent_class_name"))
120
-
121
- def save_finished(self, model):
122
- super().save_finished(model)
123
- old_data = model._previous_data
124
- new_data = model._data
125
- exclude_columns = self.config("exclude_columns")
126
- mask_columns = self.config("mask_columns")
127
- parent_columns = self.parent_columns
128
-
129
- if not old_data:
130
- create_data = {}
131
- for key in new_data.keys():
132
- if key in exclude_columns:
133
- continue
134
- create_data = {
135
- **create_data,
136
- **parent_columns[key].to_json(model),
137
- }
138
- if key in mask_columns and key in create_data:
139
- create_data[key] = "****"
140
- self.record(model, "create", data=create_data)
141
- return
142
-
143
- # note that this is fairly simple logic to get started. It's not going to detect changes that happen
144
- # in other "tables". For instance, disconnecting a record by deleting an entry in a many-to-many relationship
145
- # won't be picked up by this.
146
- old_model = model.empty_model()
147
- old_model.data = old_data
148
- from_data = {}
149
- to_data = {}
150
- for column, new_value in new_data.items():
151
- if column in exclude_columns or column not in old_data:
152
- continue
153
- if old_data[column] == new_value:
154
- continue
155
- from_data = {
156
- **from_data,
157
- **parent_columns[column].to_json(old_model),
158
- }
159
- to_data = {
160
- **to_data,
161
- **parent_columns[column].to_json(model),
162
- }
163
- if column in mask_columns and column in to_data:
164
- to_data[column] = "****"
165
- from_data[column] = "****"
166
- if not from_data and not to_data:
167
- return
168
-
169
- self.record(
170
- model,
171
- "update",
172
- data={
173
- "from": from_data,
174
- "to": to_data,
175
- },
176
- )
177
-
178
- def post_delete(self, model):
179
- super().post_delete(model)
180
- exclude_columns = self.config("exclude_columns")
181
- parent_columns = self.parent_columns
182
- mask_columns = self.config("mask_columns")
183
-
184
- final_data = {}
185
- for key in model._data.keys():
186
- if key in exclude_columns:
187
- continue
188
- final_data = {
189
- **final_data,
190
- **parent_columns[key].to_json(model),
191
- }
192
-
193
- for key in mask_columns:
194
- if key not in final_data:
195
- continue
196
- final_data[key] = "****"
197
-
198
- self.child_models.create(
199
- {
200
- "class": self.config("parent_class_name"),
201
- "resource_id": model.get(self.config("parent_id_column_name")),
202
- "action": "delete",
203
- "data": final_data,
204
- }
205
- )
206
-
207
- @property
208
- def parent_columns(self):
209
- if self._parent_columns == None:
210
- self._parent_columns = self.di.build(self.model_class, cache=True).columns()
211
- return self._parent_columns
212
-
213
- def record(self, model, action, data=None, record_data=None):
214
- audit_data = {
215
- "class": self.config("parent_class_name"),
216
- "resource_id": model.get(self.config("parent_id_column_name")),
217
- "action": action,
218
- }
219
- if data is not None:
220
- audit_data["data"] = data
221
- if record_data is not None:
222
- audit_data = {
223
- **audit_data,
224
- **record_data,
225
- }
226
-
227
- self.child_models.create(audit_data)
228
-
229
-
230
- def build_column_config(name, column_class, **kwargs):
231
- return (name, {**{"class": column_class}, **kwargs})
232
-
233
-
234
- def audit(name, **kwargs):
235
- return build_column_config(name, Audit, **kwargs)
@@ -1,250 +0,0 @@
1
- import re
2
- from .string import String
3
- from ..autodoc.schema import Array as AutoDocArray
4
- from ..autodoc.schema import Object as AutoDocObject
5
- from ..autodoc.schema import String as AutoDocString
6
- from collections import OrderedDict
7
-
8
-
9
- class BelongsTo(String):
10
- """
11
- Controls a belongs to relationship.
12
-
13
- This column should be named something like 'parent_id', e.g. user_id, column_id, etc... It expects the actual
14
- database column to be an integer. It also provides an additional property on the model which returns the
15
- related model, instead of the id, with a name given by dropping `_id` from the column name. In other words,
16
- if you have a column called user_id and a particular model has a user_id of 5, then:
17
-
18
- ```
19
- print(model.user_id)
20
- # prints 5
21
- print(model.user.id)
22
- # prints 5
23
- print(model.user.name)
24
- # prints the name of the user with an id of 5.
25
- ```
26
- """
27
-
28
- wants_n_plus_one = True
29
- required_configs = [
30
- "parent_models_class",
31
- ]
32
-
33
- my_configs = [
34
- "model_column_name",
35
- "readable_parent_columns",
36
- "join_type",
37
- ]
38
-
39
- def __init__(self, di):
40
- super().__init__(di)
41
-
42
- def _check_configuration(self, configuration):
43
- super()._check_configuration(configuration)
44
- self.validate_models_class(configuration["parent_models_class"])
45
-
46
- if not configuration.get("model_column_name") and self.name[-3:] != "_id":
47
- raise ValueError(
48
- f"Invalid name for column '{self.name}' in '{self.model_class.__name__}' - "
49
- + "BelongsTo column names must end in '_id', or you must set 'model_column_name' to specify the name of the column "
50
- + "that the parent model can be fetched from."
51
- )
52
- if configuration.get("model_column_name") and type(configuration.get("model_column_name")) != str:
53
- raise ValueError(
54
- f"Configuration error for '{self.name}' in '{self.model_class.__name__}': 'model_column_name' must be a string."
55
- )
56
-
57
- join_type = configuration.get("join_type")
58
- if join_type and join_type.upper() not in ["LEFT", "INNER"]:
59
- raise ValueError(
60
- f"Configuration error for '{self.name}' in '{self.model_class.__name__}': join_type must be INNER or LEFT"
61
- )
62
-
63
- if configuration.get("readable_parent_columns"):
64
- parent_columns = self.di.build(configuration["parent_models_class"], cache=True).raw_columns_configuration()
65
- error_prefix = f"Configuration error for '{self.name}' in '{self.model_class.__name__}':"
66
- readable_parent_columns = configuration["readable_parent_columns"]
67
- if not hasattr(readable_parent_columns, "__iter__"):
68
- raise ValueError(
69
- f"{error_prefix} 'readable_parent_columns' should be an iterable "
70
- + "with the list of child columns to output."
71
- )
72
- if isinstance(readable_parent_columns, str):
73
- raise ValueError(
74
- f"{error_prefix} 'readable_parent_columns' should be an iterable "
75
- + "with the list of child columns to output."
76
- )
77
- for column_name in readable_parent_columns:
78
- if column_name not in parent_columns:
79
- raise ValueError(
80
- f"{error_prefix} 'readable_parent_columns' references column named '{column_name}' but this"
81
- + "column does not exist in the model class."
82
- )
83
-
84
- def _finalize_configuration(self, configuration):
85
- return {
86
- **super()._finalize_configuration(configuration),
87
- **{
88
- "model_column_name": configuration.get("model_column_name")
89
- if configuration.get("model_column_name")
90
- else self.name[:-3],
91
- "join_type": configuration.get("join_type", "INNER").upper(),
92
- },
93
- }
94
-
95
- def input_error_for_value(self, value, operator=None):
96
- integer_check = super().input_error_for_value(value)
97
- if integer_check:
98
- return integer_check
99
- parent_models = self.parent_models
100
- id_column_name = parent_models.get_id_column_name()
101
- matching_parents = parent_models.where(f"{id_column_name}={value}")
102
- input_output = self.di.build("input_output", cache=True)
103
- matching_parents = matching_parents.where_for_request(
104
- matching_parents,
105
- input_output.routing_data(),
106
- input_output.get_authorization_data(),
107
- input_output,
108
- )
109
- if not len(matching_parents):
110
- return f"Invalid selection for {self.name}: record does not exist"
111
- return ""
112
-
113
- def can_provide(self, column_name):
114
- return column_name == self.config("model_column_name")
115
-
116
- def provide(self, data, column_name):
117
- # did we have data parent data loaded up with a query?
118
- alias = self.join_table_alias()
119
- parent_id_column_name = self.parent_models.get_id_column_name()
120
- if f"{alias}_{parent_id_column_name}" in data:
121
- parent_data = {parent_id_column_name: data[f"{alias}_{parent_id_column_name}"]}
122
- for column_name in self.parent_columns.keys():
123
- select_alias = f"{alias}_{column_name}"
124
- parent_data[column_name] = data[select_alias] if select_alias in data else None
125
- return self.parent_models.model(parent_data)
126
-
127
- # if not, just look it up from the id
128
- parent_id = data.get(self.name)
129
- if parent_id:
130
- parent_id_column_name = self.parent_models.get_id_column_name()
131
- return self.parent_models.where(f"{parent_id_column_name}={parent_id}").first()
132
- return self.parent_models.empty_model()
133
-
134
- def join_table_alias(self):
135
- return self.parent_models.table_name() + "_" + self.name
136
-
137
- def configure_n_plus_one(self, models, columns=None):
138
- if columns is None:
139
- columns = self.config("readable_parent_columns", silent=True)
140
- if not columns:
141
- return models
142
-
143
- models = self.add_join(models)
144
- alias = self.join_table_alias()
145
- parent_id_column_name = self.parent_models.get_id_column_name()
146
- select_parts = [f"{alias}.{column_name} AS {alias}_{column_name}" for column_name in columns]
147
- select_parts.append(f"{alias}.{parent_id_column_name} AS {alias}_{parent_id_column_name}")
148
- return models.select(", ".join(select_parts))
149
-
150
- @property
151
- def parent_models(self):
152
- return self.di.build(self.config("parent_models_class"), cache=True)
153
-
154
- @property
155
- def parent_columns(self):
156
- return self.parent_models.model_columns
157
-
158
- def to_json(self, model):
159
- # if we don't have readable parent columns specified, then just return the id
160
- if not self.config("readable_parent_columns", silent=True):
161
- return super().to_json(model)
162
-
163
- # otherwise return an object with the readable parent columns
164
- columns = self.parent_columns
165
- parent = model.__getattr__(self.config("model_column_name"))
166
- json = OrderedDict()
167
- if parent.id_column_name not in self.config("readable_parent_columns"):
168
- json[parent.id_column_name] = list(columns[parent.id_column_name].to_json(parent).values())[0]
169
- for column_name in self.config("readable_parent_columns"):
170
- json = {**json, **columns[column_name].to_json(parent)}
171
- id_less_name = self.config("model_column_name")
172
- return {
173
- **super().to_json(model),
174
- id_less_name: json,
175
- }
176
-
177
- def documentation(self, name=None, example=None, value=None):
178
- columns = self.parent_columns
179
- parent_id_column_name = self.parent_models.get_id_column_name()
180
- parent_properties = [columns[parent_id_column_name].documentation()]
181
-
182
- parent_columns = self.config("readable_parent_columns", silent=True)
183
- parent_id_doc = AutoDocString(name if name is not None else self.name)
184
- if not parent_columns:
185
- return parent_id_doc
186
-
187
- for column_name in self.config("readable_parent_columns"):
188
- if column_name == parent_id_column_name:
189
- continue
190
- parent_properties.append(columns[column_name].documentation())
191
-
192
- return [
193
- parent_id_doc,
194
- AutoDocObject(
195
- self.config("model_column_name"),
196
- parent_properties,
197
- ),
198
- ]
199
-
200
- def is_allowed_operator(self, operator, relationship_reference=None):
201
- """
202
- This is called when processing user data to decide if the end-user is specifying an allowed operator
203
- """
204
- if not relationship_reference:
205
- return "="
206
- parent_columns = self.parent_columns
207
- if relationship_reference not in self.parent_columns:
208
- raise ValueError(
209
- "I was asked to search on a related column that doens't exist. This shouldn't have happened :("
210
- )
211
- return self.parent_columns[relationship_reference].is_allowed_operator(operator)
212
-
213
- def check_search_value(self, value, operator=None, relationship_reference=None):
214
- if not relationship_reference:
215
- return self.input_error_for_value(value, operator=operator)
216
- parent_columns = self.parent_columns
217
- if relationship_reference not in self.parent_columns:
218
- raise ValueError(
219
- "I was asked to search on a related column that doens't exist. This shouldn't have happened :("
220
- )
221
- return self.parent_columns[relationship_reference].check_search_value(value, operator=operator)
222
-
223
- def add_join(self, models):
224
- parent_table = self.parent_models.table_name()
225
- alias = self.join_table_alias()
226
-
227
- if models.is_joined(parent_table, alias=alias):
228
- return models
229
-
230
- join_type = "LEFT " if self.config("join_type") == "LEFT" else ""
231
- own_table_name = models.table_name()
232
- parent_id_column_name = self.parent_models.get_id_column_name()
233
- return models.join(
234
- f"{join_type}JOIN {parent_table} as {alias} on {alias}.{parent_id_column_name}={own_table_name}.{self.name}"
235
- )
236
-
237
- def add_search(self, models, value, operator=None, relationship_reference=None):
238
- if not relationship_reference:
239
- return super().add_search(models, value, operator=operator)
240
-
241
- parent_columns = self.parent_columns
242
- if relationship_reference not in self.parent_columns:
243
- raise ValueError(
244
- "I was asked to search on a related column that doens't exist. This shouldn't have happened :("
245
- )
246
-
247
- models = self.add_join(models)
248
- related_column = self.parent_columns[relationship_reference]
249
- alias = self.join_table_alias()
250
- return models.where(related_column.build_condition(value, operator=operator, column_prefix=f"{alias}."))