clear-skies 1.22.10__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 (368) 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.22.10.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 +8 -39
  7. clearskies/authentication/authentication.py +44 -0
  8. clearskies/authentication/authorization.py +14 -8
  9. clearskies/authentication/authorization_pass_through.py +14 -10
  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 +55 -20
  41. clearskies/backends/api_backend.py +1118 -280
  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 +115 -4
  154. clearskies/di/additional_config_auto_import.py +12 -0
  155. clearskies/di/di.py +714 -125
  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 -160
  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 +1874 -193
  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.22.10.dist-info/METADATA +0 -47
  243. clear_skies-1.22.10.dist-info/RECORD +0 -213
  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 -51
  248. clearskies/backends/api_get_only_backend.py +0 -48
  249. clearskies/backends/example_backend.py +0 -43
  250. clearskies/backends/file_backend.py +0 -48
  251. clearskies/backends/json_backend.py +0 -7
  252. clearskies/backends/restful_api_advanced_search_backend.py +0 -103
  253. clearskies/binding_config.py +0 -16
  254. clearskies/column_types/__init__.py +0 -203
  255. clearskies/column_types/audit.py +0 -249
  256. clearskies/column_types/belongs_to.py +0 -271
  257. clearskies/column_types/boolean.py +0 -60
  258. clearskies/column_types/category_tree.py +0 -304
  259. clearskies/column_types/column.py +0 -373
  260. clearskies/column_types/created.py +0 -26
  261. clearskies/column_types/created_by_authorization_data.py +0 -26
  262. clearskies/column_types/created_by_header.py +0 -24
  263. clearskies/column_types/created_by_ip.py +0 -17
  264. clearskies/column_types/created_by_routing_data.py +0 -25
  265. clearskies/column_types/created_by_user_agent.py +0 -17
  266. clearskies/column_types/created_micro.py +0 -26
  267. clearskies/column_types/datetime.py +0 -109
  268. clearskies/column_types/datetime_micro.py +0 -13
  269. clearskies/column_types/email.py +0 -18
  270. clearskies/column_types/float.py +0 -43
  271. clearskies/column_types/has_many.py +0 -179
  272. clearskies/column_types/has_one.py +0 -58
  273. clearskies/column_types/integer.py +0 -41
  274. clearskies/column_types/json.py +0 -25
  275. clearskies/column_types/many_to_many.py +0 -278
  276. clearskies/column_types/many_to_many_with_data.py +0 -162
  277. clearskies/column_types/phone.py +0 -48
  278. clearskies/column_types/select.py +0 -11
  279. clearskies/column_types/string.py +0 -24
  280. clearskies/column_types/timestamp.py +0 -73
  281. clearskies/column_types/updated.py +0 -26
  282. clearskies/column_types/updated_micro.py +0 -26
  283. clearskies/column_types/uuid.py +0 -25
  284. clearskies/columns.py +0 -123
  285. clearskies/condition_parser.py +0 -172
  286. clearskies/contexts/build_context.py +0 -54
  287. clearskies/contexts/convert_to_application.py +0 -190
  288. clearskies/contexts/extract_handler.py +0 -37
  289. clearskies/contexts/test.py +0 -94
  290. clearskies/decorators/__init__.py +0 -39
  291. clearskies/decorators/auth0_jwks.py +0 -22
  292. clearskies/decorators/authorization.py +0 -10
  293. clearskies/decorators/binding_classes.py +0 -9
  294. clearskies/decorators/binding_modules.py +0 -9
  295. clearskies/decorators/bindings.py +0 -9
  296. clearskies/decorators/create.py +0 -10
  297. clearskies/decorators/delete.py +0 -10
  298. clearskies/decorators/docs.py +0 -14
  299. clearskies/decorators/get.py +0 -10
  300. clearskies/decorators/jwks.py +0 -26
  301. clearskies/decorators/merge.py +0 -124
  302. clearskies/decorators/patch.py +0 -10
  303. clearskies/decorators/post.py +0 -10
  304. clearskies/decorators/public.py +0 -11
  305. clearskies/decorators/response_headers.py +0 -10
  306. clearskies/decorators/return_raw_response.py +0 -9
  307. clearskies/decorators/schema.py +0 -10
  308. clearskies/decorators/secret_bearer.py +0 -24
  309. clearskies/decorators/security_headers.py +0 -10
  310. clearskies/di/standard_dependencies.py +0 -151
  311. clearskies/di/test_module/__init__.py +0 -6
  312. clearskies/di/test_module/another_module/__init__.py +0 -2
  313. clearskies/di/test_module/module_class.py +0 -5
  314. clearskies/handlers/__init__.py +0 -41
  315. clearskies/handlers/advanced_search.py +0 -271
  316. clearskies/handlers/base.py +0 -479
  317. clearskies/handlers/callable.py +0 -191
  318. clearskies/handlers/create.py +0 -35
  319. clearskies/handlers/crud_by_method.py +0 -18
  320. clearskies/handlers/database_connector.py +0 -32
  321. clearskies/handlers/delete.py +0 -61
  322. clearskies/handlers/exceptions/__init__.py +0 -5
  323. clearskies/handlers/exceptions/not_found.py +0 -3
  324. clearskies/handlers/get.py +0 -156
  325. clearskies/handlers/health_check.py +0 -59
  326. clearskies/handlers/input_processing.py +0 -79
  327. clearskies/handlers/list.py +0 -530
  328. clearskies/handlers/mygrations.py +0 -82
  329. clearskies/handlers/request_method_routing.py +0 -47
  330. clearskies/handlers/restful_api.py +0 -218
  331. clearskies/handlers/routing.py +0 -62
  332. clearskies/handlers/schema_helper.py +0 -128
  333. clearskies/handlers/simple_routing.py +0 -206
  334. clearskies/handlers/simple_routing_route.py +0 -192
  335. clearskies/handlers/simple_search.py +0 -136
  336. clearskies/handlers/update.py +0 -96
  337. clearskies/handlers/write.py +0 -193
  338. clearskies/input_requirements/__init__.py +0 -78
  339. clearskies/input_requirements/after.py +0 -36
  340. clearskies/input_requirements/before.py +0 -36
  341. clearskies/input_requirements/in_the_future_at_least.py +0 -19
  342. clearskies/input_requirements/in_the_future_at_most.py +0 -19
  343. clearskies/input_requirements/in_the_past_at_least.py +0 -19
  344. clearskies/input_requirements/in_the_past_at_most.py +0 -19
  345. clearskies/input_requirements/maximum_length.py +0 -19
  346. clearskies/input_requirements/maximum_value.py +0 -19
  347. clearskies/input_requirements/minimum_length.py +0 -22
  348. clearskies/input_requirements/minimum_value.py +0 -19
  349. clearskies/input_requirements/requirement.py +0 -25
  350. clearskies/input_requirements/time_delta.py +0 -38
  351. clearskies/input_requirements/unique.py +0 -18
  352. clearskies/mocks/__init__.py +0 -7
  353. clearskies/mocks/input_output.py +0 -124
  354. clearskies/mocks/models.py +0 -142
  355. clearskies/models.py +0 -350
  356. clearskies/security_headers/base.py +0 -12
  357. clearskies/tests/simple_api/models/__init__.py +0 -2
  358. clearskies/tests/simple_api/models/status.py +0 -23
  359. clearskies/tests/simple_api/models/user.py +0 -21
  360. clearskies/tests/simple_api/users_api.py +0 -64
  361. {clear_skies-1.22.10.dist-info → clear_skies-2.0.23.dist-info/licenses}/LICENSE +0 -0
  362. /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
  363. /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
  364. /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
  365. /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
  366. /clearskies/{secrets/exceptions → exceptions}/not_found.py +0 -0
  367. /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
  368. /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from clearskies import decorators
6
+ from clearskies.columns.has_many import HasMany
7
+
8
+ if TYPE_CHECKING:
9
+ from clearskies import typing
10
+
11
+
12
+ class HasManySelf(HasMany):
13
+ """
14
+ This is just like the HasMany column, but is used when the model references itself.
15
+
16
+ This exists because a model can't refer to itself inside it's own class definition. There are
17
+ workarounds, but having this class is usually quicker for the developer.
18
+
19
+ The main difference between this and HasMany is that you don't have to provide the child class.
20
+ Also, the name of the column that contains the id of the parent becomes `parent_id` by default,
21
+ rather than basing it on the name of the model. This is done because, since the model is also
22
+ the child, using the name of the model in the name of the column id is often ambiguous.
23
+
24
+ See also BelongsToSelf.
25
+ """
26
+
27
+ _descriptor_config_map = None
28
+
29
+ @decorators.parameters_to_properties
30
+ def __init__(
31
+ self,
32
+ foreign_column_name: str | None = None,
33
+ readable_child_columns: list[str] = [],
34
+ where: typing.condition | list[typing.condition] = [],
35
+ is_readable: bool = True,
36
+ on_change_pre_save: typing.action | list[typing.action] = [],
37
+ on_change_post_save: typing.action | list[typing.action] = [],
38
+ on_change_save_finished: typing.action | list[typing.action] = [],
39
+ ):
40
+ pass
41
+
42
+ def finalize_configuration(self, model_class, name) -> None:
43
+ """
44
+ Finalize and check the configuration.
45
+
46
+ This is an external trigger called by the model class when the model class is ready.
47
+ The reason it exists here instead of in the constructor is because some columns are tightly
48
+ connected to the model class, and can't validate configuration until they know what the model is.
49
+ Therefore, we need the model involved, and the only way for a property to know what class it is
50
+ in is if the parent class checks in (which is what happens here).
51
+ """
52
+ self.child_model_class = model_class
53
+ has_value = False
54
+ try:
55
+ has_value = bool(self.foreign_column_name)
56
+ except KeyError:
57
+ pass
58
+
59
+ if not has_value:
60
+ self.foreign_column_name = "parent_id"
61
+
62
+ super().finalize_configuration(model_class, name)
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.columns.has_many import HasMany
4
+
5
+
6
+ class HasOne(HasMany):
7
+ """
8
+ This operates exactly like the HasMany relationship, except it assumes there is only ever one child.
9
+
10
+ The only real difference between this and HasMany is that the HasMany column type will return a list
11
+ of models, while this returns the first model.
12
+ """
13
+
14
+ _descriptor_config_map = None
15
+
16
+ def __get__(self, model, cls):
17
+ if model is None:
18
+ self.model_class = cls
19
+ return self # type: ignore
20
+
21
+ return super().__get__(model, cls).first()
@@ -0,0 +1,158 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Callable, Self, overload
4
+
5
+ from clearskies import configs, decorators
6
+ from clearskies.autodoc.schema import Integer as AutoDocInteger
7
+ from clearskies.column import Column
8
+
9
+ if TYPE_CHECKING:
10
+ from clearskies import Model, typing
11
+ from clearskies.autodoc.schema import Schema as AutoDocSchema
12
+ from clearskies.query import Condition
13
+
14
+
15
+ class Integer(Column):
16
+ """
17
+ A column that stores integer data.
18
+
19
+ ```python
20
+ import clearskies
21
+
22
+
23
+ class MyModel(clearskies.Model):
24
+ backend = clearskies.backends.MemoryBackend()
25
+ id_column_name = "id"
26
+
27
+ id = clearskies.columns.Uuid()
28
+ age = clearskies.columns.Integer()
29
+
30
+
31
+ wsgi = clearskies.contexts.WsgiRef(
32
+ clearskies.endpoints.Create(
33
+ MyModel,
34
+ writeable_column_names=["age"],
35
+ readable_column_names=["id", "age"],
36
+ ),
37
+ classes=[MyModel],
38
+ )
39
+ wsgi()
40
+ ```
41
+
42
+ And when invoked:
43
+
44
+ ```bash
45
+ $ curl 'http://localhost:8080' -d '{"age":20}' | jq
46
+ {
47
+ "status": "success",
48
+ "error": "",
49
+ "data": {
50
+ "id": "6ea74719-a65f-45ae-b6a3-641ce682ed25",
51
+ "age": 20
52
+ },
53
+ "pagination": {},
54
+ "input_errors": {}
55
+ }
56
+
57
+ $ curl 'http://localhost:8080' -d '{"age":"asdf"}' | jq
58
+ {
59
+ "status": "input_errors",
60
+ "error": "",
61
+ "data": [],
62
+ "pagination": {},
63
+ "input_errors": {
64
+ "age": "value should be an integer"
65
+ }
66
+ }
67
+ ```
68
+ """
69
+
70
+ default = configs.Integer(default=None) # type: ignore
71
+ setable = configs.IntegerOrCallable(default=None) # type: ignore
72
+ _allowed_search_operators = ["<=>", "!=", "<=", ">=", ">", "<", "=", "in", "is not null", "is null"]
73
+
74
+ auto_doc_class: type[AutoDocSchema] = AutoDocInteger
75
+
76
+ _descriptor_config_map = None
77
+
78
+ @decorators.parameters_to_properties
79
+ def __init__(
80
+ self,
81
+ default: int | None = None,
82
+ setable: int | Callable[..., int] | None = None,
83
+ is_readable: bool = True,
84
+ is_writeable: bool = True,
85
+ is_searchable: bool = True,
86
+ is_temporary: bool = False,
87
+ validators: typing.validator | list[typing.validator] = [],
88
+ on_change_pre_save: typing.action | list[typing.action] = [],
89
+ on_change_post_save: typing.action | list[typing.action] = [],
90
+ on_change_save_finished: typing.action | list[typing.action] = [],
91
+ created_by_source_type: str = "",
92
+ created_by_source_key: str = "",
93
+ created_by_source_strict: bool = True,
94
+ ):
95
+ pass
96
+
97
+ @overload
98
+ def __get__(self, instance: None, cls: type[Model]) -> Self:
99
+ pass
100
+
101
+ @overload
102
+ def __get__(self, instance: Model, cls: type[Model]) -> int:
103
+ pass
104
+
105
+ def __get__(self, instance, cls):
106
+ if instance is None:
107
+ self.model_class = cls
108
+ return self
109
+
110
+ value = super().__get__(instance, cls)
111
+ return None if value is None else int(value)
112
+
113
+ def __set__(self, instance, value: int) -> None:
114
+ # this makes sure we're initialized
115
+ if "name" not in self._config: # type: ignore
116
+ instance.get_columns()
117
+
118
+ instance._next_data[self.name] = value
119
+
120
+ def from_backend(self, value) -> int | None:
121
+ return None if value is None else int(value)
122
+
123
+ def to_backend(self, data):
124
+ if self.name not in data or data[self.name] is None:
125
+ return data
126
+
127
+ return {**data, self.name: int(data[self.name])}
128
+
129
+ def input_error_for_value(self, value, operator=None):
130
+ try:
131
+ int(value)
132
+ except ValueError:
133
+ return "value should be an integer"
134
+ return ""
135
+
136
+ def equals(self, value: int) -> Condition:
137
+ return super().equals(value)
138
+
139
+ def spaceship(self, value: int) -> Condition:
140
+ return super().spaceship(value)
141
+
142
+ def not_equals(self, value: int) -> Condition:
143
+ return super().not_equals(value)
144
+
145
+ def less_than_equals(self, value: int) -> Condition:
146
+ return super().less_than_equals(value)
147
+
148
+ def greater_than_equals(self, value: int) -> Condition:
149
+ return super().greater_than_equals(value)
150
+
151
+ def less_than(self, value: int) -> Condition:
152
+ return super().less_than(value)
153
+
154
+ def greater_than(self, value: int) -> Condition:
155
+ return super().greater_than(value)
156
+
157
+ def is_in(self, values: list[int]) -> Condition:
158
+ return super().is_in(values)
@@ -0,0 +1,126 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import TYPE_CHECKING, Any, Callable, Self, overload
5
+
6
+ from clearskies import configs, decorators
7
+ from clearskies.column import Column
8
+
9
+ if TYPE_CHECKING:
10
+ from clearskies import Model, typing
11
+
12
+
13
+ class Json(Column):
14
+ """
15
+ A column to store generic data.
16
+
17
+ ```python
18
+ import clearskies
19
+
20
+
21
+ class MyModel(clearskies.Model):
22
+ backend = clearskies.backends.MemoryBackend()
23
+ id_column_name = "id"
24
+
25
+ id = clearskies.columns.Uuid()
26
+ my_data = clearskies.columns.Json()
27
+
28
+
29
+ wsgi = clearskies.contexts.WsgiRef(
30
+ clearskies.endpoints.Create(
31
+ MyModel,
32
+ writeable_column_names=["my_data"],
33
+ readable_column_names=["id", "my_data"],
34
+ ),
35
+ classes=[MyModel],
36
+ )
37
+ wsgi()
38
+ ```
39
+
40
+ And when invoked:
41
+
42
+ ```bash
43
+ $ curl 'http://localhost:8080' -d '{"my_data":{"count":[1,2,3,4,{"thing":true}]}}' | jq
44
+ {
45
+ "status": "success",
46
+ "error": "",
47
+ "data": {
48
+ "id": "63cbd5e7-a198-4424-bd35-3890075a2a5e",
49
+ "my_data": {
50
+ "count": [
51
+ 1,
52
+ 2,
53
+ 3,
54
+ 4,
55
+ {
56
+ "thing": true
57
+ }
58
+ ]
59
+ }
60
+ },
61
+ "pagination": {},
62
+ "input_errors": {}
63
+ }
64
+ ```
65
+
66
+ Note that there is no attempt to check the shape of the input passed into a JSON column.
67
+
68
+ """
69
+
70
+ setable = configs.Any(default=None) # type: ignore
71
+ default = configs.Any(default=None) # type: ignore
72
+ is_searchable = configs.Boolean(default=False)
73
+ _descriptor_config_map = None
74
+
75
+ @decorators.parameters_to_properties
76
+ def __init__(
77
+ self,
78
+ default: dict[str, Any] | list[Any] | None = None,
79
+ setable: dict[str, Any] | list[Any] | Callable[..., dict[str, Any]] | None = None,
80
+ is_readable: bool = True,
81
+ is_writeable: bool = True,
82
+ is_temporary: bool = False,
83
+ validators: typing.validator | list[typing.validator] = [],
84
+ on_change_pre_save: typing.action | list[typing.action] = [],
85
+ on_change_post_save: typing.action | list[typing.action] = [],
86
+ on_change_save_finished: typing.action | list[typing.action] = [],
87
+ created_by_source_type: str = "",
88
+ created_by_source_key: str = "",
89
+ created_by_source_strict: bool = True,
90
+ ):
91
+ pass
92
+
93
+ @overload
94
+ def __get__(self, instance: None, cls: type[Model]) -> Self:
95
+ pass
96
+
97
+ @overload
98
+ def __get__(self, instance: Model, cls: type[Model]) -> dict[str, Any]:
99
+ pass
100
+
101
+ def __get__(self, instance, cls):
102
+ return super().__get__(instance, cls)
103
+
104
+ def __set__(self, instance, value: dict[str, Any]) -> None:
105
+ # this makes sure we're initialized
106
+ if "name" not in self._config: # type: ignore
107
+ instance.get_columns()
108
+
109
+ instance._next_data[self.name] = value
110
+
111
+ def from_backend(self, value) -> dict[str, Any] | list[Any] | None:
112
+ if isinstance(value, (list, dict)):
113
+ return value
114
+ if not value:
115
+ return None
116
+ try:
117
+ return json.loads(value)
118
+ except json.JSONDecodeError:
119
+ return None
120
+
121
+ def to_backend(self, data):
122
+ if self.name not in data or data[self.name] is None:
123
+ return data
124
+
125
+ value = data[self.name]
126
+ return {**data, self.name: value if isinstance(value, str) else json.dumps(value)}