clear-skies 1.22.30__py3-none-any.whl → 2.0.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (344) hide show
  1. {clear_skies-1.22.30.dist-info → clear_skies-2.0.0.dist-info}/METADATA +5 -7
  2. clear_skies-2.0.0.dist-info/RECORD +248 -0
  3. {clear_skies-1.22.30.dist-info → clear_skies-2.0.0.dist-info}/WHEEL +1 -1
  4. clearskies/__init__.py +42 -25
  5. clearskies/action.py +7 -0
  6. clearskies/authentication/__init__.py +8 -41
  7. clearskies/authentication/authentication.py +42 -0
  8. clearskies/authentication/authorization.py +4 -9
  9. clearskies/authentication/authorization_pass_through.py +11 -9
  10. clearskies/authentication/jwks.py +128 -58
  11. clearskies/authentication/public.py +3 -38
  12. clearskies/authentication/secret_bearer.py +516 -54
  13. clearskies/autodoc/formats/oai3_json/__init__.py +1 -1
  14. clearskies/autodoc/formats/oai3_json/oai3_json.py +9 -7
  15. clearskies/autodoc/formats/oai3_json/parameter.py +6 -3
  16. clearskies/autodoc/formats/oai3_json/request.py +7 -5
  17. clearskies/autodoc/formats/oai3_json/response.py +7 -4
  18. clearskies/autodoc/formats/oai3_json/schema/object.py +4 -1
  19. clearskies/autodoc/request/__init__.py +2 -0
  20. clearskies/autodoc/request/header.py +4 -6
  21. clearskies/autodoc/request/json_body.py +4 -6
  22. clearskies/autodoc/request/parameter.py +8 -0
  23. clearskies/autodoc/request/request.py +7 -4
  24. clearskies/autodoc/request/url_parameter.py +4 -6
  25. clearskies/autodoc/request/url_path.py +4 -6
  26. clearskies/autodoc/schema/__init__.py +4 -2
  27. clearskies/autodoc/schema/array.py +5 -6
  28. clearskies/autodoc/schema/boolean.py +4 -10
  29. clearskies/autodoc/schema/date.py +0 -3
  30. clearskies/autodoc/schema/datetime.py +1 -4
  31. clearskies/autodoc/schema/double.py +0 -3
  32. clearskies/autodoc/schema/enum.py +4 -2
  33. clearskies/autodoc/schema/integer.py +4 -9
  34. clearskies/autodoc/schema/long.py +0 -3
  35. clearskies/autodoc/schema/number.py +4 -9
  36. clearskies/autodoc/schema/object.py +5 -7
  37. clearskies/autodoc/schema/password.py +0 -3
  38. clearskies/autodoc/schema/schema.py +11 -0
  39. clearskies/autodoc/schema/string.py +4 -10
  40. clearskies/backends/__init__.py +55 -20
  41. clearskies/backends/api_backend.py +1100 -284
  42. clearskies/backends/backend.py +40 -84
  43. clearskies/backends/cursor_backend.py +236 -186
  44. clearskies/backends/memory_backend.py +519 -226
  45. clearskies/backends/secrets_backend.py +75 -31
  46. clearskies/column.py +1232 -0
  47. clearskies/columns/__init__.py +71 -0
  48. clearskies/columns/audit.py +205 -0
  49. clearskies/columns/belongs_to_id.py +483 -0
  50. clearskies/columns/belongs_to_model.py +128 -0
  51. clearskies/columns/belongs_to_self.py +105 -0
  52. clearskies/columns/boolean.py +109 -0
  53. clearskies/columns/category_tree.py +275 -0
  54. clearskies/columns/category_tree_ancestors.py +51 -0
  55. clearskies/columns/category_tree_children.py +127 -0
  56. clearskies/columns/category_tree_descendants.py +48 -0
  57. clearskies/columns/created.py +94 -0
  58. clearskies/columns/created_by_authorization_data.py +116 -0
  59. clearskies/columns/created_by_header.py +99 -0
  60. clearskies/columns/created_by_ip.py +92 -0
  61. clearskies/columns/created_by_routing_data.py +96 -0
  62. clearskies/columns/created_by_user_agent.py +92 -0
  63. clearskies/columns/date.py +230 -0
  64. clearskies/columns/datetime.py +278 -0
  65. clearskies/columns/email.py +76 -0
  66. clearskies/columns/float.py +149 -0
  67. clearskies/columns/has_many.py +505 -0
  68. clearskies/columns/has_many_self.py +56 -0
  69. clearskies/columns/has_one.py +14 -0
  70. clearskies/columns/integer.py +156 -0
  71. clearskies/columns/json.py +122 -0
  72. clearskies/columns/many_to_many_ids.py +333 -0
  73. clearskies/columns/many_to_many_ids_with_data.py +270 -0
  74. clearskies/columns/many_to_many_models.py +154 -0
  75. clearskies/columns/many_to_many_pivots.py +133 -0
  76. clearskies/columns/phone.py +158 -0
  77. clearskies/columns/select.py +91 -0
  78. clearskies/columns/string.py +98 -0
  79. clearskies/columns/timestamp.py +160 -0
  80. clearskies/columns/updated.py +110 -0
  81. clearskies/columns/uuid.py +86 -0
  82. clearskies/configs/README.md +105 -0
  83. clearskies/configs/__init__.py +159 -0
  84. clearskies/configs/actions.py +43 -0
  85. clearskies/configs/any.py +13 -0
  86. clearskies/configs/any_dict.py +22 -0
  87. clearskies/configs/any_dict_or_callable.py +23 -0
  88. clearskies/configs/authentication.py +23 -0
  89. clearskies/configs/authorization.py +23 -0
  90. clearskies/configs/boolean.py +16 -0
  91. clearskies/configs/boolean_or_callable.py +18 -0
  92. clearskies/configs/callable_config.py +18 -0
  93. clearskies/configs/columns.py +34 -0
  94. clearskies/configs/conditions.py +30 -0
  95. clearskies/configs/config.py +21 -0
  96. clearskies/configs/datetime.py +18 -0
  97. clearskies/configs/datetime_or_callable.py +19 -0
  98. clearskies/configs/endpoint.py +23 -0
  99. clearskies/configs/float.py +16 -0
  100. clearskies/configs/float_or_callable.py +18 -0
  101. clearskies/configs/integer.py +16 -0
  102. clearskies/configs/integer_or_callable.py +18 -0
  103. clearskies/configs/joins.py +30 -0
  104. clearskies/configs/list_any_dict.py +30 -0
  105. clearskies/configs/list_any_dict_or_callable.py +31 -0
  106. clearskies/configs/model_class.py +35 -0
  107. clearskies/configs/model_column.py +65 -0
  108. clearskies/configs/model_columns.py +56 -0
  109. clearskies/configs/model_destination_name.py +25 -0
  110. clearskies/configs/model_to_id_column.py +43 -0
  111. clearskies/configs/readable_model_column.py +9 -0
  112. clearskies/configs/readable_model_columns.py +9 -0
  113. clearskies/configs/schema.py +23 -0
  114. clearskies/configs/searchable_model_columns.py +9 -0
  115. clearskies/configs/security_headers.py +39 -0
  116. clearskies/configs/select.py +26 -0
  117. clearskies/configs/select_list.py +47 -0
  118. clearskies/configs/string.py +29 -0
  119. clearskies/configs/string_dict.py +32 -0
  120. clearskies/configs/string_list.py +32 -0
  121. clearskies/configs/string_list_or_callable.py +35 -0
  122. clearskies/configs/string_or_callable.py +18 -0
  123. clearskies/configs/timedelta.py +18 -0
  124. clearskies/configs/timezone.py +18 -0
  125. clearskies/configs/url.py +23 -0
  126. clearskies/configs/validators.py +45 -0
  127. clearskies/configs/writeable_model_column.py +9 -0
  128. clearskies/configs/writeable_model_columns.py +9 -0
  129. clearskies/configurable.py +76 -0
  130. clearskies/contexts/__init__.py +8 -8
  131. clearskies/contexts/cli.py +5 -42
  132. clearskies/contexts/context.py +78 -56
  133. clearskies/contexts/wsgi.py +13 -30
  134. clearskies/contexts/wsgi_ref.py +49 -0
  135. clearskies/di/__init__.py +10 -7
  136. clearskies/di/additional_config.py +115 -4
  137. clearskies/di/additional_config_auto_import.py +12 -0
  138. clearskies/di/di.py +742 -121
  139. clearskies/di/inject/__init__.py +23 -0
  140. clearskies/di/inject/by_class.py +21 -0
  141. clearskies/di/inject/by_name.py +18 -0
  142. clearskies/di/inject/di.py +13 -0
  143. clearskies/di/inject/environment.py +14 -0
  144. clearskies/di/inject/input_output.py +20 -0
  145. clearskies/di/inject/now.py +13 -0
  146. clearskies/di/inject/requests.py +13 -0
  147. clearskies/di/inject/secrets.py +14 -0
  148. clearskies/di/inject/utcnow.py +13 -0
  149. clearskies/di/inject/uuid.py +15 -0
  150. clearskies/di/injectable.py +29 -0
  151. clearskies/di/injectable_properties.py +131 -0
  152. clearskies/end.py +183 -0
  153. clearskies/endpoint.py +1309 -0
  154. clearskies/endpoint_group.py +297 -0
  155. clearskies/endpoints/__init__.py +23 -0
  156. clearskies/endpoints/advanced_search.py +526 -0
  157. clearskies/endpoints/callable.py +387 -0
  158. clearskies/endpoints/create.py +202 -0
  159. clearskies/endpoints/delete.py +139 -0
  160. clearskies/endpoints/get.py +275 -0
  161. clearskies/endpoints/health_check.py +181 -0
  162. clearskies/endpoints/list.py +573 -0
  163. clearskies/endpoints/restful_api.py +427 -0
  164. clearskies/endpoints/simple_search.py +286 -0
  165. clearskies/endpoints/update.py +190 -0
  166. clearskies/environment.py +5 -3
  167. clearskies/exceptions/__init__.py +17 -0
  168. clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
  169. clearskies/exceptions/moved_permanently.py +3 -0
  170. clearskies/exceptions/moved_temporarily.py +3 -0
  171. clearskies/exceptions/not_found.py +2 -0
  172. clearskies/functional/__init__.py +2 -2
  173. clearskies/functional/routing.py +92 -0
  174. clearskies/functional/string.py +19 -11
  175. clearskies/functional/validations.py +61 -9
  176. clearskies/input_outputs/__init__.py +9 -7
  177. clearskies/input_outputs/cli.py +130 -142
  178. clearskies/input_outputs/exceptions/__init__.py +1 -1
  179. clearskies/input_outputs/headers.py +45 -0
  180. clearskies/input_outputs/input_output.py +91 -122
  181. clearskies/input_outputs/programmatic.py +69 -0
  182. clearskies/input_outputs/wsgi.py +23 -38
  183. clearskies/model.py +489 -184
  184. clearskies/parameters_to_properties.py +31 -0
  185. clearskies/query/__init__.py +12 -0
  186. clearskies/query/condition.py +223 -0
  187. clearskies/query/join.py +136 -0
  188. clearskies/query/query.py +196 -0
  189. clearskies/query/sort.py +27 -0
  190. clearskies/schema.py +82 -0
  191. clearskies/secrets/__init__.py +3 -31
  192. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
  193. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
  194. clearskies/secrets/akeyless.py +88 -147
  195. clearskies/secrets/secrets.py +8 -8
  196. clearskies/security_header.py +8 -0
  197. clearskies/security_headers/__init__.py +8 -8
  198. clearskies/security_headers/cache_control.py +47 -110
  199. clearskies/security_headers/cors.py +40 -95
  200. clearskies/security_headers/csp.py +76 -151
  201. clearskies/security_headers/hsts.py +14 -16
  202. clearskies/test_base.py +8 -0
  203. clearskies/typing.py +11 -0
  204. clearskies/validator.py +25 -0
  205. clearskies/validators/__init__.py +33 -0
  206. clearskies/validators/after_column.py +62 -0
  207. clearskies/validators/before_column.py +13 -0
  208. clearskies/validators/in_the_future.py +32 -0
  209. clearskies/validators/in_the_future_at_least.py +11 -0
  210. clearskies/validators/in_the_future_at_most.py +10 -0
  211. clearskies/validators/in_the_past.py +32 -0
  212. clearskies/validators/in_the_past_at_least.py +10 -0
  213. clearskies/validators/in_the_past_at_most.py +10 -0
  214. clearskies/validators/maximum_length.py +26 -0
  215. clearskies/validators/maximum_value.py +29 -0
  216. clearskies/validators/minimum_length.py +26 -0
  217. clearskies/validators/minimum_value.py +29 -0
  218. clearskies/validators/required.py +35 -0
  219. clearskies/validators/timedelta.py +59 -0
  220. clearskies/validators/unique.py +31 -0
  221. clear_skies-1.22.30.dist-info/RECORD +0 -214
  222. clearskies/application.py +0 -29
  223. clearskies/authentication/auth0_jwks.py +0 -118
  224. clearskies/authentication/auth_exception.py +0 -2
  225. clearskies/authentication/jwks_jwcrypto.py +0 -51
  226. clearskies/backends/api_get_only_backend.py +0 -48
  227. clearskies/backends/example_backend.py +0 -43
  228. clearskies/backends/file_backend.py +0 -48
  229. clearskies/backends/json_backend.py +0 -7
  230. clearskies/backends/restful_api_advanced_search_backend.py +0 -103
  231. clearskies/binding_config.py +0 -16
  232. clearskies/column_types/__init__.py +0 -203
  233. clearskies/column_types/audit.py +0 -249
  234. clearskies/column_types/belongs_to.py +0 -271
  235. clearskies/column_types/boolean.py +0 -60
  236. clearskies/column_types/category_tree.py +0 -304
  237. clearskies/column_types/column.py +0 -373
  238. clearskies/column_types/created.py +0 -26
  239. clearskies/column_types/created_by_authorization_data.py +0 -26
  240. clearskies/column_types/created_by_header.py +0 -24
  241. clearskies/column_types/created_by_ip.py +0 -17
  242. clearskies/column_types/created_by_routing_data.py +0 -25
  243. clearskies/column_types/created_by_user_agent.py +0 -17
  244. clearskies/column_types/created_micro.py +0 -26
  245. clearskies/column_types/datetime.py +0 -109
  246. clearskies/column_types/datetime_micro.py +0 -12
  247. clearskies/column_types/email.py +0 -18
  248. clearskies/column_types/float.py +0 -43
  249. clearskies/column_types/has_many.py +0 -179
  250. clearskies/column_types/has_one.py +0 -60
  251. clearskies/column_types/integer.py +0 -41
  252. clearskies/column_types/json.py +0 -25
  253. clearskies/column_types/many_to_many.py +0 -278
  254. clearskies/column_types/many_to_many_with_data.py +0 -162
  255. clearskies/column_types/phone.py +0 -48
  256. clearskies/column_types/select.py +0 -11
  257. clearskies/column_types/string.py +0 -24
  258. clearskies/column_types/timestamp.py +0 -73
  259. clearskies/column_types/updated.py +0 -26
  260. clearskies/column_types/updated_micro.py +0 -26
  261. clearskies/column_types/uuid.py +0 -25
  262. clearskies/columns.py +0 -123
  263. clearskies/condition_parser.py +0 -172
  264. clearskies/contexts/build_context.py +0 -54
  265. clearskies/contexts/convert_to_application.py +0 -190
  266. clearskies/contexts/extract_handler.py +0 -37
  267. clearskies/contexts/test.py +0 -94
  268. clearskies/decorators/__init__.py +0 -41
  269. clearskies/decorators/allow_non_json_bodies.py +0 -9
  270. clearskies/decorators/auth0_jwks.py +0 -22
  271. clearskies/decorators/authorization.py +0 -10
  272. clearskies/decorators/binding_classes.py +0 -9
  273. clearskies/decorators/binding_modules.py +0 -9
  274. clearskies/decorators/bindings.py +0 -9
  275. clearskies/decorators/create.py +0 -10
  276. clearskies/decorators/delete.py +0 -10
  277. clearskies/decorators/docs.py +0 -14
  278. clearskies/decorators/get.py +0 -10
  279. clearskies/decorators/jwks.py +0 -26
  280. clearskies/decorators/merge.py +0 -124
  281. clearskies/decorators/patch.py +0 -10
  282. clearskies/decorators/post.py +0 -10
  283. clearskies/decorators/public.py +0 -11
  284. clearskies/decorators/response_headers.py +0 -10
  285. clearskies/decorators/return_raw_response.py +0 -9
  286. clearskies/decorators/schema.py +0 -10
  287. clearskies/decorators/secret_bearer.py +0 -24
  288. clearskies/decorators/security_headers.py +0 -10
  289. clearskies/di/standard_dependencies.py +0 -151
  290. clearskies/handlers/__init__.py +0 -41
  291. clearskies/handlers/advanced_search.py +0 -271
  292. clearskies/handlers/base.py +0 -479
  293. clearskies/handlers/callable.py +0 -192
  294. clearskies/handlers/create.py +0 -35
  295. clearskies/handlers/crud_by_method.py +0 -18
  296. clearskies/handlers/database_connector.py +0 -32
  297. clearskies/handlers/delete.py +0 -61
  298. clearskies/handlers/exceptions/__init__.py +0 -5
  299. clearskies/handlers/exceptions/not_found.py +0 -3
  300. clearskies/handlers/get.py +0 -156
  301. clearskies/handlers/health_check.py +0 -59
  302. clearskies/handlers/input_processing.py +0 -79
  303. clearskies/handlers/list.py +0 -530
  304. clearskies/handlers/mygrations.py +0 -82
  305. clearskies/handlers/request_method_routing.py +0 -47
  306. clearskies/handlers/restful_api.py +0 -218
  307. clearskies/handlers/routing.py +0 -62
  308. clearskies/handlers/schema_helper.py +0 -128
  309. clearskies/handlers/simple_routing.py +0 -206
  310. clearskies/handlers/simple_routing_route.py +0 -197
  311. clearskies/handlers/simple_search.py +0 -136
  312. clearskies/handlers/update.py +0 -102
  313. clearskies/handlers/write.py +0 -193
  314. clearskies/input_requirements/__init__.py +0 -78
  315. clearskies/input_requirements/after.py +0 -36
  316. clearskies/input_requirements/before.py +0 -36
  317. clearskies/input_requirements/in_the_future_at_least.py +0 -19
  318. clearskies/input_requirements/in_the_future_at_most.py +0 -19
  319. clearskies/input_requirements/in_the_past_at_least.py +0 -19
  320. clearskies/input_requirements/in_the_past_at_most.py +0 -19
  321. clearskies/input_requirements/maximum_length.py +0 -19
  322. clearskies/input_requirements/maximum_value.py +0 -19
  323. clearskies/input_requirements/minimum_length.py +0 -22
  324. clearskies/input_requirements/minimum_value.py +0 -19
  325. clearskies/input_requirements/required.py +0 -23
  326. clearskies/input_requirements/requirement.py +0 -25
  327. clearskies/input_requirements/time_delta.py +0 -38
  328. clearskies/input_requirements/unique.py +0 -18
  329. clearskies/mocks/__init__.py +0 -7
  330. clearskies/mocks/input_output.py +0 -124
  331. clearskies/mocks/models.py +0 -142
  332. clearskies/models.py +0 -350
  333. clearskies/security_headers/base.py +0 -12
  334. clearskies/tests/simple_api/models/__init__.py +0 -2
  335. clearskies/tests/simple_api/models/status.py +0 -23
  336. clearskies/tests/simple_api/models/user.py +0 -21
  337. clearskies/tests/simple_api/users_api.py +0 -64
  338. {clear_skies-1.22.30.dist-info → clear_skies-2.0.0.dist-info}/LICENSE +0 -0
  339. /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
  340. /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
  341. /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
  342. /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
  343. /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
  344. /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
@@ -1,142 +0,0 @@
1
- from ..models import Models as ModelsBase
2
- from ..model import Model as ModelBase
3
- from ..columns import Columns
4
- from ..di import StandardDependencies
5
-
6
-
7
- class Model(ModelBase):
8
- _columns_configuration = None
9
-
10
- def set_columns_configuration(self, columns_configuration):
11
- self._columns_configuration = columns_configuration
12
-
13
- def columns_configuration(self):
14
- return self._columns_configuration
15
-
16
-
17
- class Models(ModelsBase):
18
- _model_configuration = None
19
- updated = None
20
- created = None
21
- deleted = None
22
- create_responses = None
23
- update_responses = None
24
- search_responses = None
25
- iterating = None
26
- iterator_index = None
27
- iterated = None
28
- counted = None
29
-
30
- @classmethod
31
- def reset(cls):
32
- cls.updated = None
33
- cls.created = None
34
- cls.iterated = None
35
- cls.counted = None
36
- cls.deleted = None
37
-
38
- def __init__(self, model_configuration):
39
- self._model_configuration = model_configuration
40
- super().__init__(self, Columns(StandardDependencies()))
41
-
42
- def model(self, data):
43
- model_class = self.model_class()
44
- model = model_class(self._backend, self._columns)
45
- model.set_columns_configuration(self._model_configuration)
46
- model.data = data
47
- return model
48
-
49
- def model_class(self):
50
- return Model
51
-
52
- def table_name(self):
53
- return "mock_model"
54
-
55
- def blank(self):
56
- blank = self.__class__(self._model_configuration)
57
- blank.create_responses = self.create_responses
58
- blank.update_responses = self.update_responses
59
- blank.search_responses = self.search_responses
60
- return blank
61
-
62
- def add_update_response(self, data):
63
- if self.update_responses is None:
64
- self.update_responses = []
65
- self.update_responses.append(data)
66
-
67
- def add_create_response(self, data):
68
- if self.create_responses is None:
69
- self.create_responses = []
70
- self.create_responses.append(data)
71
-
72
- def add_search_response(self, data):
73
- # We're expecting a list because a search implicitly returns multiple records. Technically, we're also
74
- # okay with tuples, but this is the most straight-forward way to get what we want and should avoid weird
75
- # errors without causing serious issues later.
76
- if type(data) != list:
77
- raise ValueError("A list should be passed into to 'add_search_response'")
78
- if self.search_responses is None:
79
- self.search_responses = []
80
- self.search_responses.append(data)
81
-
82
- def clear_search_responses(self):
83
- self.search_responses = None
84
-
85
- # our mock models also acts as the backend for the mock model
86
- def update(self, id, data, model):
87
- if self.update_responses is None:
88
- raise ValueError("Must set update data through 'models.add_update_response' before attempting to update")
89
- if not len(self.update_responses):
90
- raise ValueError("Ran out of responses while processing an update!")
91
- if Models.updated is None:
92
- Models.updated = []
93
- Models.updated.append({"id": id, "data": data, "model": model})
94
- return self.update_responses.pop(0)
95
-
96
- def create(self, data, model):
97
- if self.create_responses is None:
98
- raise ValueError("Must set create data through 'models.add_create_response' before attempting to create")
99
- if not len(self.create_responses):
100
- raise ValueError("Ran out of responses while processing an create!")
101
- if Models.created is None:
102
- Models.created = []
103
- Models.created.append({"data": data, "model": model})
104
- return self.create_responses.pop(0)
105
-
106
- def delete(self, id, model):
107
- if Models.deleted is None:
108
- Models.deleted = []
109
- Models.deleted.append({"id": id, "model": model})
110
- return True
111
-
112
- def count(self, configuration, model):
113
- if self.search_responses is None:
114
- raise ValueError("Must set search data through 'models.add_search_response' before counting")
115
- if Models.counted == None:
116
- Models.counted = []
117
- del configuration["model_columns"]
118
- Models.counted.append(configuration)
119
- counted = self.search_responses.pop(0)
120
- return len(counted)
121
-
122
- def records(self, configuration, model, next_page_data=None):
123
- if self.search_responses is None:
124
- raise ValueError("Must set search data through 'models.add_search_response' before counting")
125
- if Models.iterated == None:
126
- Models.iterated = []
127
- del configuration["model_columns"]
128
- Models.iterated.append(configuration)
129
- records = self.search_responses.pop(0)
130
- return records
131
-
132
- def next(self):
133
- self.iterator_index += 1
134
- if self.iterator_index >= len(self.iterating):
135
- raise StopIteration()
136
- return self.iterating[self.iterator_index]
137
-
138
- def column_from_backend(self, column, value):
139
- return column.from_backend(value)
140
-
141
- def column_to_backend(self, column, backend_data):
142
- return column.to_backend(backend_data)
clearskies/models.py DELETED
@@ -1,350 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from .condition_parser import ConditionParser
3
- from typing import Any, Callable, Dict, List, Tuple, Iterator
4
-
5
- try:
6
- from typing_extensions import Self
7
- except ModuleNotFoundError:
8
- from typing import Self
9
-
10
-
11
- class Models(ABC, ConditionParser):
12
- # The database connection
13
- _backend = None
14
- _columns = None
15
- _model_columns = None
16
- _next_page_data = None
17
-
18
- query_wheres = None
19
- query_sorts = None
20
- query_group_by_column = None
21
- query_limit = None
22
- query_pagination = None
23
- query_selects = None
24
- query_select_all = None
25
- must_rexecute = True
26
- must_recount = True
27
- count = None
28
- _table_name = None
29
- _id_column_name = None
30
- _query_configuration = None
31
-
32
- def __init__(self, backend, columns):
33
- self._model_columns = None
34
- self._backend = backend
35
- self._columns = columns
36
- self.must_rexecute = True
37
- self._next_page_data = None
38
- self.must_recount = True
39
-
40
- self.query_wheres = []
41
- self.query_sorts = []
42
- self.query_group_by_column = None
43
- self.query_joins = []
44
- self.query_limit = None
45
- self.query_pagination = {}
46
- self.query_selects = []
47
- self.query_select_all = True
48
-
49
- @abstractmethod
50
- def model_class(self: Self) -> type[Self]:
51
- """Return the model class that this models object will find/return instances of"""
52
- pass
53
-
54
- def clone(self: Self) -> Self:
55
- clone = self.blank()
56
- clone.query_configuration = self.query_configuration
57
- return clone
58
-
59
- def blank(self: Self) -> Self:
60
- return self._build_model()
61
-
62
- def get_table_name(self: Self) -> str:
63
- if self._table_name is None:
64
- self._table_name = self.model_class().table_name()
65
- return self._table_name
66
-
67
- def get_id_column_name(self: Self) -> str:
68
- if self._id_column_name is None:
69
- self._id_column_name = self.empty_model().id_column_name
70
- return self._id_column_name
71
-
72
- @property
73
- def query_configuration(self: Self) -> Dict[str, Any]:
74
- return {
75
- "wheres": [*self.query_wheres],
76
- "sorts": [*self.query_sorts],
77
- "group_by_column": self.query_group_by_column,
78
- "joins": [*self.query_joins],
79
- "limit": self.query_limit,
80
- "pagination": self.query_pagination,
81
- "selects": self.query_selects,
82
- "select_all": self.query_select_all,
83
- "table_name": self.get_table_name(),
84
- "model_columns": self.model_columns,
85
- }
86
-
87
- @query_configuration.setter
88
- def query_configuration(self: Self, configuration: Dict[str, Any]):
89
- self.query_wheres = configuration["wheres"]
90
- self.query_sorts = configuration["sorts"]
91
- self.query_group_by_column = configuration["group_by_column"]
92
- self.query_joins = configuration["joins"]
93
- self.query_limit = configuration["limit"]
94
- self.query_pagination = configuration["pagination"]
95
- self.query_selects = configuration["selects"]
96
- self.query_select_all = configuration["select_all"]
97
- self._model_columns = configuration["model_columns"]
98
-
99
- @property
100
- def model_columns(self: Self):
101
- if self._model_columns is None:
102
- self._model_columns = self.empty_model().columns()
103
- return self._model_columns
104
-
105
- def select(self: Self, selects) -> Self:
106
- return self.clone().select_in_place(selects)
107
-
108
- def select_in_place(self: Self, selects) -> Self:
109
- self.query_selects.append(selects)
110
- self.must_rexecute = True
111
- self._next_page_data = None
112
- return self
113
-
114
- def select_all(self: Self, select_all=True) -> Self:
115
- return self.clone().select_all_in_place(select_all=select_all)
116
-
117
- def select_all_in_place(self: Self, select_all=True) -> Self:
118
- self.query_select_all = select_all
119
- self.must_rexecute = True
120
- self._next_page_data = None
121
- return self
122
-
123
- def where(self: Self, where: str) -> Self:
124
- """Adds the given condition to the query and returns a new Models object"""
125
- return self.clone().where_in_place(where)
126
-
127
- def where_in_place(self: Self, where: str) -> Self:
128
- """Adds the given condition to the query for the current Models object"""
129
- condition = self.parse_condition(where)
130
- self._validate_column(condition["column"], "filter", table=condition["table"])
131
- self.query_wheres.append(self.parse_condition(where))
132
- self.must_rexecute = True
133
- self._next_page_data = None
134
- self.must_recount = True
135
- return self
136
-
137
- def join(self: Self, join: str) -> Self:
138
- return self.clone().join_in_place(join)
139
-
140
- def join_in_place(self: Self, join: str) -> Self:
141
- self.query_joins.append(self.parse_join(join))
142
- self.must_rexecute = True
143
- self._next_page_data = None
144
- self.must_recount = True
145
- return self
146
-
147
- def is_joined(self: Self, table_name, alias=None):
148
- for join in self.query_joins:
149
- if join["table"] != table_name:
150
- continue
151
-
152
- if alias and join["alias"] != alias:
153
- continue
154
-
155
- return join["alias"] if join["alias"] else join["table"]
156
- return False
157
-
158
- def group_by(self: Self, group_column: str) -> Self:
159
- return self.clone().group_by_in_place(group_column)
160
-
161
- def group_by_in_place(self: Self, group_column: str) -> Self:
162
- self._validate_column(group_column, "group")
163
- self.query_group_by_column = group_column
164
- self.must_rexecute = True
165
- self._next_page_data = None
166
- self.must_recount = True
167
- return self
168
-
169
- def sort_by(
170
- self: Self,
171
- primary_column,
172
- primary_direction,
173
- primary_table=None,
174
- secondary_column=None,
175
- secondary_direction=None,
176
- secondary_table=None,
177
- ) -> Self:
178
- return self.clone().sort_by_in_place(
179
- primary_column,
180
- primary_direction,
181
- primary_table=primary_table,
182
- secondary_column=secondary_column,
183
- secondary_direction=secondary_direction,
184
- secondary_table=secondary_table,
185
- )
186
-
187
- def sort_by_in_place(
188
- self: Self,
189
- primary_column,
190
- primary_direction,
191
- primary_table=None,
192
- secondary_column=None,
193
- secondary_direction=None,
194
- secondary_table=None,
195
- ) -> Self:
196
- sorts = [
197
- {"table": primary_table, "column": primary_column, "direction": primary_direction},
198
- {"table": secondary_table, "column": secondary_column, "direction": secondary_direction},
199
- ]
200
- sorts = filter(lambda sort: sort["column"] is not None and sort["direction"] is not None, sorts)
201
- self.query_sorts = list(map(lambda sort: self._normalize_and_validate_sort(sort), sorts))
202
- if len(self.query_sorts) == 0:
203
- raise ValueError("Missing primary column or direction in call to sort_by")
204
- self.must_rexecute = True
205
- self._next_page_data = None
206
- return self
207
-
208
- def _normalize_and_validate_sort(self: Self, sort):
209
- if "column" not in sort or not sort["column"]:
210
- raise ValueError("Missing 'column' for sort")
211
- if "direction" not in sort or not sort["direction"]:
212
- raise ValueError("Missing 'direction' for sort: should be ASC or DESC")
213
- direction = sort["direction"].upper().strip()
214
- if direction != "ASC" and direction != "DESC":
215
- raise ValueError(f"Invalid sort direction: should be ASC or DESC, not '{direction}'")
216
- self._validate_column(sort["column"], "sort", table=sort.get("table"))
217
-
218
- # down the line we may ask the model class what columns we can sort on, but we're good for now
219
- return {"column": sort["column"], "direction": sort["direction"], "table": sort.get("table")}
220
-
221
- def _validate_column(self: Self, column_name, action, table=None):
222
- """
223
- Down the line we may use the model configuration to check what columns are valid sort/group/search targets
224
- """
225
- # for now, only validate columns that belong to *our* table.
226
- # in some cases we are explicitly told the column name
227
- if table is not None:
228
- # note that table may be '', in which case it is implicitly "our" table
229
- if table != "" and table != self.get_table_name():
230
- return
231
-
232
- # but in some cases we should check and see if it is included with the column name
233
- column_name = column_name.replace("`", "")
234
- if "." in column_name:
235
- parts = column_name.split(".")
236
- if parts[0] != self.get_table_name():
237
- return
238
- column_name = column_name.split(".")[1]
239
-
240
- model_columns = self.model_columns
241
- if column_name not in model_columns:
242
- model_class = self.model_class()
243
- raise KeyError(
244
- f"Cannot {action} by column '{column_name}' for model class {model_class.__name__} because this "
245
- + "column does not exist for the model. You can suppress this error by adding a matching column "
246
- + "to your model definition"
247
- )
248
-
249
- def limit(self: Self, limit) -> Self:
250
- return self.clone().limit_in_place(limit)
251
-
252
- def limit_in_place(self: Self, limit) -> Self:
253
- self.query_limit = limit
254
- self.must_rexecute = True
255
- self._next_page_data = None
256
- return self
257
-
258
- def pagination(self: Self, **kwargs) -> Self:
259
- return self.clone().pagination_in_place(**kwargs)
260
-
261
- def pagination_in_place(self: Self, **kwargs) -> Self:
262
- error = self._backend.validate_pagination_kwargs(kwargs, str)
263
- if error:
264
- raise ValueError(
265
- f"Invalid pagination data for model {self.__class__.__name__} with backend "
266
- + f"{self._backend.__class__.__name__}. {error}"
267
- )
268
- self.query_pagination = kwargs
269
- self.must_rexecute = True
270
- self._next_page_data = None
271
- return self
272
-
273
- def find(self: Self, where: str) -> Self:
274
- """Returns the first model where condition"""
275
- return self.clone().where(where).first()
276
-
277
- def __len__(self: Self):
278
- if self.must_recount:
279
- self.count = self._backend.count(self.query_configuration, self.empty_model())
280
- self.must_recount = False
281
- return self.count
282
-
283
- def __iter__(self: Self) -> Iterator[Self]:
284
- self._next_page_data = {}
285
- raw_rows = self._backend.records(
286
- self.query_configuration,
287
- self.empty_model(),
288
- next_page_data=self._next_page_data,
289
- )
290
- models = iter([self.model(row) for row in raw_rows])
291
- return models
292
-
293
- def paginate_all(self: Self) -> List[Self]:
294
- next_models = self.clone()
295
- results = list(next_models.__iter__())
296
- next_page_data = next_models.next_page_data()
297
- while next_page_data:
298
- next_models = next_models.clone().pagination(**next_page_data)
299
- results.extend(next_models.__iter__())
300
- next_page_data = next_models.next_page_data()
301
- return results
302
-
303
- def model(self: Self, data) -> Self:
304
- model = self._build_model()
305
- model.data = data
306
- return model
307
-
308
- def _build_model(self: Self) -> Self:
309
- model_class = self.model_class()
310
- return model_class(self._backend, self._columns)
311
-
312
- def empty_model(self: Self) -> Self:
313
- return self.model({})
314
-
315
- def create(self: Self, data: Dict[str, Any]) -> Self:
316
- empty = self.empty_model()
317
- empty.save(data)
318
- return empty
319
-
320
- def first(self: Self) -> Self:
321
- iter = self.__iter__()
322
- try:
323
- return iter.__next__()
324
- except StopIteration:
325
- return self.empty_model()
326
-
327
- def columns(self: Self, overrides=None):
328
- model = self.model({})
329
- return model.columns(overrides=None)
330
-
331
- def raw_columns_configuration(self: Self):
332
- return self.model({}).all_columns()
333
-
334
- def allowed_pagination_keys(self: Self) -> List[str]:
335
- return self._backend.allowed_pagination_keys()
336
-
337
- def validate_pagination_kwargs(self, kwargs: Dict[str, Any], case_mapping: Callable) -> str:
338
- return self._backend.validate_pagination_kwargs(kwargs, case_mapping)
339
-
340
- def next_page_data(self: Self):
341
- return self._next_page_data
342
-
343
- def documentation_pagination_next_page_response(self: Self, case_mapping: Callable) -> List[Any]:
344
- return self._backend.documentation_pagination_next_page_response(case_mapping)
345
-
346
- def documentation_pagination_next_page_example(self: Self, case_mapping: Callable) -> Dict[str, Any]:
347
- return self._backend.documentation_pagination_next_page_example(case_mapping)
348
-
349
- def documentation_pagination_parameters(self: Self, case_mapping: Callable) -> List[Tuple[Any]]:
350
- return self._backend.documentation_pagination_parameters(case_mapping)
@@ -1,12 +0,0 @@
1
- class Base:
2
- environment = None
3
- is_cors = False
4
-
5
- def __init__(self, environment):
6
- self.environment = environment
7
-
8
- def configure(self):
9
- pass
10
-
11
- def set_headers_for_input_output(self, input_output):
12
- pass
@@ -1,2 +0,0 @@
1
- from .user import User
2
- from .status import Status
@@ -1,23 +0,0 @@
1
- from collections import OrderedDict
2
- from clearskies import Model
3
- from clearskies.column_types import string, has_many
4
- from clearskies.input_requirements import required, maximum_length
5
- from . import user
6
-
7
-
8
- class Status(Model):
9
- def __init__(self, cursor_backend, columns):
10
- super().__init__(cursor_backend, columns)
11
-
12
- def columns_configuration(self):
13
- return OrderedDict(
14
- [
15
- string("name"),
16
- has_many(
17
- "users",
18
- child_models_class=user.User,
19
- is_readable=True,
20
- readable_child_columns=["status_id", "name", "email"],
21
- ),
22
- ]
23
- )
@@ -1,21 +0,0 @@
1
- from collections import OrderedDict
2
- from clearskies import Model
3
- from clearskies.column_types import belongs_to, email, string, integer, created, updated
4
- from clearskies.input_requirements import required, maximum_length
5
- from . import status
6
-
7
-
8
- class User(Model):
9
- def __init__(self, cursor_backend, columns):
10
- super().__init__(cursor_backend, columns)
11
-
12
- def columns_configuration(self):
13
- return OrderedDict(
14
- [
15
- belongs_to("status_id", parent_models_class=status.Status, input_requirements=[required()]),
16
- string("name", input_requirements=[required(), maximum_length(255)]),
17
- email("email", input_requirements=[required(), maximum_length(255)]),
18
- created("created"),
19
- updated("updated"),
20
- ]
21
- )
@@ -1,64 +0,0 @@
1
- import clearskies
2
- from . import models
3
-
4
-
5
- def restart_user(user_id, input_output):
6
- return {"user_id": user_id}
7
-
8
-
9
- users_api = clearskies.Application(
10
- clearskies.handlers.SimpleRouting,
11
- {
12
- "authentication": clearskies.authentication.public(),
13
- "routes": [
14
- {
15
- "path": "users/{user_id}/restart",
16
- "handler_class": clearskies.handlers.Callable,
17
- "handler_config": {
18
- "callable": restart_user,
19
- },
20
- },
21
- {
22
- "path": "users",
23
- "handler_class": clearskies.handlers.RestfulAPI,
24
- "handler_config": {
25
- "model_class": models.User,
26
- "readable_columns": ["id", "status_id", "name", "email", "created", "updated"],
27
- "writeable_columns": ["status_id", "name", "email"],
28
- "searchable_columns": ["status_id", "name", "email"],
29
- "default_sort_column": "name",
30
- },
31
- },
32
- {
33
- "path": "statuses",
34
- "handler_class": clearskies.handlers.RestfulAPI,
35
- "handler_config": {
36
- "model_class": models.Status,
37
- "read_only": True,
38
- "readable_columns": ["id", "name", "users"],
39
- "searchable_columns": ["name", "users"],
40
- "default_sort_column": "name",
41
- },
42
- },
43
- {
44
- "path": "v1",
45
- "handler_class": clearskies.handlers.SimpleRouting,
46
- "handler_config": {
47
- "routes": [
48
- {
49
- "path": "users",
50
- "handler_class": clearskies.handlers.RestfulAPI,
51
- "handler_config": {
52
- "read_only": True,
53
- "model_class": models.User,
54
- "readable_columns": ["id", "status_id", "name"],
55
- "searchable_columns": ["status_id", "name"],
56
- "default_sort_column": "name",
57
- },
58
- },
59
- ]
60
- },
61
- },
62
- ],
63
- },
64
- )
File without changes
File without changes