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
@@ -1,271 +0,0 @@
1
- from .simple_search import SimpleSearch
2
- from .. import autodoc
3
- from .. import condition_parser
4
-
5
-
6
- class AdvancedSearch(SimpleSearch):
7
- expected_request_methods = "POST"
8
-
9
- @property
10
- def allowed_request_keys(self):
11
- return ["sort", "where", "limit"]
12
-
13
- def configure_models_from_request_data(self, models, request_data, query_parameters, pagination_data):
14
- limit = int(
15
- self._from_either(request_data, query_parameters, "limit", default=self.configuration("default_limit"))
16
- )
17
- models = models.limit(limit)
18
- if pagination_data:
19
- models = models.pagination(**pagination_data)
20
- if "sort" in request_data:
21
- primary_column = request_data["sort"][0]["column"]
22
- primary_direction = request_data["sort"][0]["direction"]
23
- secondary_column = None
24
- secondary_direction = None
25
- secondary_table = None
26
- if len(request_data["sort"]) == 2:
27
- secondary_column = request_data["sort"][1]["column"]
28
- secondary_direction = request_data["sort"][1]["direction"]
29
- # call _add_join first because it's expecting something like table.column_name, and
30
- # _resolve_references_for_query will break that up.
31
- models = self._add_join(secondary_column, models)
32
- [secondary_column, secondary_table] = self._resolve_references_for_query(secondary_column)
33
-
34
- # call _add_join first because it's expecting column_name to be something like 'table.column_name', and
35
- # _resolve_references_for_query will break up the table and column name reference.
36
- models = self._add_join(primary_column, models)
37
- [primary_column, primary_table] = self._resolve_references_for_query(primary_column)
38
- models = models.sort_by(
39
- primary_column,
40
- primary_direction,
41
- primary_table=primary_table,
42
- secondary_column=secondary_column,
43
- secondary_direction=secondary_direction,
44
- secondary_table=secondary_table,
45
- )
46
- if "where" in request_data:
47
- for where in request_data["where"]:
48
- column_name = where["column"]
49
- if column_name == "id":
50
- column_name = self.id_column_name
51
- models = self._add_join(column_name, models)
52
- [column_name, relationship_reference] = self._unpack_column_name_with_reference(column_name)
53
- column = self._columns[column_name]
54
- models = column.add_search(
55
- models,
56
- where["value"],
57
- operator=where["operator"].lower() if "operator" in where else None,
58
- relationship_reference=relationship_reference,
59
- )
60
-
61
- return [models, limit]
62
-
63
- def check_request_data(self, request_data, query_parameters, pagination_data):
64
- # first, check the pagination data
65
- if pagination_data:
66
- error = self._model.validate_pagination_kwargs(pagination_data, self.auto_case_internal_column_name)
67
- if error:
68
- return error
69
- # next, check that they didn't provide something unexpected in the rest of the request
70
- allowed_request_keys = self.allowed_request_keys
71
- for key in request_data.keys():
72
- if key not in allowed_request_keys:
73
- return f"Invalid request parameter found in request body: '{key}'"
74
- # and ensure that the data we expect is not in the query parameters. This is not as strict
75
- # of a check as ensuring that *nothing* is in the query parameters, but query parameters get
76
- # used for a lot of things, so that could backfire
77
- for key in allowed_request_keys:
78
- if key in query_parameters:
79
- return f"Invalid request: key '{key}' was found in a URL parameter but should only be in the JSON body"
80
- limit = request_data.get("limit", None)
81
- if limit is not None and type(limit) != int and type(limit) != float and type(limit) != str:
82
- return "Invalid request: 'limit' should be an integer"
83
- if limit:
84
- try:
85
- limit = int(limit)
86
- except ValueError:
87
- return "Invalid request: 'limit' should be an integer"
88
- if limit and limit > self.configuration("max_limit"):
89
- return f"Invalid request: 'limit' must be at most {self.configuration('max_limit')}"
90
- allowed_sort_columns = self.configuration("sortable_columns")
91
- if not allowed_sort_columns:
92
- allowed_sort_columns = self._columns
93
- if "sort" in request_data:
94
- if type(request_data["sort"]) != list:
95
- return (
96
- "Invalid request: if provided, 'sort' must be a list of "
97
- + "objects with 'column' and 'direction' keys"
98
- )
99
- if len(request_data["sort"]) > 2:
100
- return "Invalid request: at most 2 sort directives may be specified"
101
- for index, sort in enumerate(request_data["sort"]):
102
- error_prefix = (
103
- "Invalid request: 'sort' must be a list of objects with 'column' and 'direction'"
104
- + f" keys, but entry #{index+1}"
105
- )
106
- if type(sort) != dict:
107
- return f"{error_prefix} was not an object"
108
- if "column" not in sort or "direction" not in sort or not sort["column"] or not sort["direction"]:
109
- return f"{error_prefix} did not declare both 'column' and 'direction'"
110
- if len(sort) != 2:
111
- return f"{error_prefix} had extra keys present"
112
- if sort["direction"].lower() not in ["asc", "desc"]:
113
- return (
114
- "Invalid request: sort direction must be 'asc' or 'desc'"
115
- + f" but found something else in sort entry #{index+1}"
116
- )
117
- if sort["column"] not in allowed_sort_columns:
118
- return f"Invalid request: invalid sort column specified in sort entry #{index+1}"
119
- return self.check_search_in_request_data(request_data, query_parameters)
120
-
121
- def check_search_in_request_data(self, request_data, query_parameters):
122
- if "where" in request_data:
123
- if type(request_data["where"]) != list:
124
- return "Invalid request: if provided, 'where' must be a list of objects"
125
- for index, where in enumerate(request_data["where"]):
126
- if type(where) != dict:
127
- return f"Invalid request: 'where' must be a list of objects, entry #{index+1} was not an object"
128
- if "column" not in where or not where["column"]:
129
- return f"Invalid request: 'column' missing in 'where' entry #{index+1}"
130
- column_name = where["column"]
131
- if column_name not in self.configuration("searchable_columns"):
132
- return f"Invalid request: invalid search column specified in where entry #{index+1}"
133
- [column_name, relationship_reference] = self._unpack_column_name_with_reference(column_name)
134
- if column_name == "id":
135
- column_name = self.id_column_name
136
- if "value" not in where:
137
- return f"Invalid request: 'value' missing in 'where' entry #{index+1}"
138
- operator = None
139
- if "operator" in where:
140
- if type(where["operator"]) != str:
141
- return f"Invalid request: operator must be a string in 'where' entry #{index+1}"
142
- if not self._columns[column_name].is_allowed_operator(
143
- where["operator"], relationship_reference=relationship_reference
144
- ):
145
- return f"Invalid request: given operator is not allowed for column in 'where' entry #{index+1}"
146
- operator = where["operator"].lower()
147
- value_error = self._columns[column_name].check_search_value(
148
- where["value"], operator, relationship_reference=relationship_reference
149
- )
150
- if value_error:
151
- return f"Invalid request: {value_error} for 'where' entry #{index+1}"
152
- # similarly, query parameters mean search conditions
153
- for column_name, value in query_parameters.items():
154
- if column_name not in self.configuration("searchable_columns"):
155
- return f"Invalid request: invalid search column: '{column_name}'"
156
- value_error = self._columns[column_name].check_search_value(value)
157
- if value_error:
158
- return f"Invalid request: {value_error} for search column '{column_name}'"
159
-
160
- return None
161
-
162
- def map_input_to_internal_names(self, input):
163
- input = super().map_input_to_internal_names(input)
164
-
165
- # the base will take care of most of this, but it won't handle the data inside of
166
- # input['where']. Therefore, we need to handle that
167
- if "where" not in input or type(input["where"]) != list:
168
- return input
169
-
170
- mapped_wheres = []
171
-
172
- # a map between the internal column name and the external column name: easier to have before hand
173
- search_column_map = {}
174
- for internal_name in self.configuration("searchable_columns"):
175
- external_name = self.auto_case_column_name(internal_name, True)
176
- search_column_map[external_name] = internal_name
177
-
178
- # it's important that as we do this we only change things that we know we can change.
179
- # we don't want to remove things that don't belong or things that seem wrong, otherwise
180
- # the input checking won't be able to return meaningful error messages.
181
- for where in input["where"]:
182
- if type(where) != dict:
183
- mapped_wheres.append(where)
184
- continue
185
- mapped_where = {**where}
186
- for internal_name in ["column", "operator", "value"]:
187
- external_name = self.auto_case_internal_column_name(internal_name)
188
- if external_name != internal_name and external_name in mapped_where:
189
- mapped_where[internal_name] = mapped_where[external_name]
190
- del mapped_where[external_name]
191
- if "column" in mapped_where and type(mapped_where["column"]) == str:
192
- if mapped_where["column"] in search_column_map:
193
- mapped_where["column"] = search_column_map[mapped_where["column"]]
194
-
195
- mapped_wheres.append(mapped_where)
196
-
197
- input["where"] = mapped_wheres
198
- return input
199
-
200
- def documentation(self):
201
- return [
202
- self._documentation_request(
203
- "POST",
204
- [
205
- *self.documentation_request_parameters(),
206
- ],
207
- ),
208
- ]
209
-
210
- def documentation_request_parameters(self):
211
- return [
212
- *self.documentation_json_parameters(),
213
- ]
214
-
215
- def documentation_json_parameters(self):
216
- # named 'where' in the request
217
- where_condition = autodoc.schema.Object(
218
- self.auto_case_internal_column_name("condition"),
219
- [
220
- autodoc.schema.Enum(
221
- self.auto_case_internal_column_name("column"),
222
- [
223
- self.auto_case_column_name(column.name, True)
224
- for column in self._get_searchable_columns().values()
225
- ],
226
- autodoc.schema.String(self.auto_case_column_name("column_name", True)),
227
- example="name",
228
- ),
229
- autodoc.schema.Enum(
230
- self.auto_case_internal_column_name("operator"),
231
- condition_parser.ConditionParser.operators,
232
- autodoc.schema.String(self.auto_case_internal_column_name("operator")),
233
- example="=",
234
- ),
235
- autodoc.schema.String(self.auto_case_internal_column_name("value"), example="Jane"),
236
- ],
237
- )
238
-
239
- allowed_sort_columns = self.configuration("sortable_columns")
240
- if not allowed_sort_columns:
241
- allowed_sort_columns = [self.auto_case_column_name(key, True) for key in self._columns.keys()]
242
-
243
- sort_item = autodoc.schema.Object(
244
- self.auto_case_internal_column_name("sort"),
245
- [
246
- autodoc.schema.Enum(
247
- self.auto_case_internal_column_name("column"),
248
- allowed_sort_columns,
249
- autodoc.schema.String(self.auto_case_internal_column_name("column")),
250
- example=self.auto_case_internal_column_name("name"),
251
- ),
252
- autodoc.schema.Enum(
253
- self.auto_case_internal_column_name("direction"),
254
- ["asc", "desc"],
255
- autodoc.schema.String(self.auto_case_internal_column_name("direction")),
256
- example="asc",
257
- ),
258
- ],
259
- )
260
-
261
- return [
262
- autodoc.request.JSONBody(
263
- autodoc.schema.Array(self.auto_case_internal_column_name("where"), where_condition),
264
- description="List of search conditions",
265
- ),
266
- autodoc.request.JSONBody(
267
- autodoc.schema.Array(self.auto_case_internal_column_name("sort"), sort_item),
268
- description="List of sort directives (max 2)",
269
- ),
270
- *self.documentation_json_pagination_parameters(),
271
- ]