clear-skies 1.22.31__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.31.dist-info → clear_skies-2.0.0.dist-info}/METADATA +11 -13
  2. clear_skies-2.0.0.dist-info/RECORD +248 -0
  3. {clear_skies-1.22.31.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.31.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.31.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,35 +0,0 @@
1
- from .write import Write
2
- from .exceptions import InputError
3
- from collections import OrderedDict
4
- from ..functional import string
5
- import json
6
-
7
-
8
- class Create(Write):
9
- _is_create = True
10
-
11
- def __init__(self, di, logging):
12
- super().__init__(di)
13
- self._logging = logging
14
-
15
- def handle(self, input_output):
16
- model = self._model.empty_model()
17
- input_data = self.request_data(input_output)
18
- self._logging.debug("Incoming request data: " + json.dumps(input_data))
19
- input_errors = {
20
- **self._extra_column_errors(input_data),
21
- **self._find_input_errors(model, input_data, input_output),
22
- }
23
- if input_errors:
24
- self._logging.debug("Request rejected due to input errors: " + json.dumps(input_errors))
25
- raise InputError(input_errors)
26
- model.save(input_data, columns=self._columns)
27
-
28
- return self.success(input_output, self._model_as_json(model, input_output))
29
-
30
- def documentation(self):
31
- nice_model = string.camel_case_to_words(self._model.__class__.__name__)
32
- return self._documentation(
33
- description=f"Create a new {nice_model}",
34
- response_description=f"The new {nice_model}",
35
- )
@@ -1,18 +0,0 @@
1
- from .request_method_routing import RequestMethodRouting
2
- from .create import Create
3
- from .update import Update
4
- from .delete import Delete
5
- from .get import Get
6
-
7
-
8
- class CRUDByMethod(RequestMethodRouting):
9
- def __init__(self, di):
10
- super().__init__(di)
11
-
12
- def method_handler_map(self):
13
- return {
14
- "CREATE": Create,
15
- "GET": Get,
16
- "PATCH": Update,
17
- "DELETE": Delete,
18
- }
@@ -1,32 +0,0 @@
1
- from .base import Base
2
- import os
3
-
4
-
5
- class DatabaseConnector(Base):
6
- _configuration_defaults = {
7
- "tunnel_only": False,
8
- "command": "mysql",
9
- }
10
-
11
- def __init__(self, di):
12
- super().__init__(di)
13
-
14
- def handle(self, input_output):
15
- connection_details = self._di.build("connection_details")
16
- request_body = input_output.json_body(required=False)
17
- if request_body and "tunnel_only" in request_body:
18
- tunnel_only = request_body["tunnel_only"]
19
- else:
20
- tunnel_only = self.configuration("tunnel_only")
21
-
22
- if tunnel_only:
23
- return self.success(input_output, {})
24
-
25
- command = self.configuration("command")
26
- port = connection_details.get("port", 3306)
27
- print("connect!")
28
- print(connection_details["host"])
29
- os.system(
30
- f"{command} -h {connection_details['host']} -u '{connection_details['username']}' -p'{connection_details['password']}' --port={port} -D {connection_details['database']}"
31
- )
32
- print("done!")
@@ -1,61 +0,0 @@
1
- from collections import OrderedDict
2
- from .base import Base
3
- from .. import autodoc
4
- from ..functional import string
5
- from .get import Get
6
-
7
-
8
- class Delete(Get):
9
- _configuration_defaults = {
10
- "model": None,
11
- "model_class": None,
12
- "readable_columns": None,
13
- "where": [],
14
- }
15
-
16
- def __init__(self, di):
17
- super().__init__(di)
18
-
19
- def handle(self, input_output):
20
- model = self.fetch_model(input_output)
21
- if type(model) == str:
22
- return self.error(input_output, model, 404)
23
-
24
- model.delete()
25
- return self.success(input_output, {})
26
-
27
- def documentation(self):
28
- nice_model = string.camel_case_to_words(self._model.__class__.__name__)
29
-
30
- authentication = self.configuration("authentication")
31
- standard_error_responses = []
32
- if not getattr(authentication, "is_public", False):
33
- standard_error_responses.append(self.documentation_access_denied_response())
34
- if getattr(authentication, "can_authorize", False):
35
- standard_error_responses.append(self.documentation_unauthorized_response())
36
-
37
- id_label = "id" if self.configuration("id_column_name") else self.id_column_name
38
- return [
39
- autodoc.request.Request(
40
- "Delete the " + nice_model + " with an " + id_label + " of {" + id_label + "}",
41
- [
42
- self.documentation_success_response(
43
- autodoc.schema.Object("data", children=[]),
44
- description=f"The {nice_model} was deleted",
45
- ),
46
- *standard_error_responses,
47
- self.documentation_not_found(),
48
- ],
49
- relative_path=self.configuration("base_url").rstrip("/") + "/{" + id_label + "}",
50
- parameters=[
51
- autodoc.request.URLPath(
52
- autodoc.schema.Integer(id_label),
53
- description=f"The {id_label} of the record to delete.",
54
- required=True,
55
- )
56
- ],
57
- root_properties={
58
- "security": self.documentation_request_security(),
59
- },
60
- )
61
- ]
@@ -1,5 +0,0 @@
1
- from .authentication import Authentication
2
- from .authorization import Authorization
3
- from .client_error import ClientError
4
- from .input_error import InputError
5
- from .not_found import NotFound
@@ -1,3 +0,0 @@
1
- class NotFound(Exception):
2
- def __init__(self, error):
3
- super().__init__(self, error)
@@ -1,156 +0,0 @@
1
- from .base import Base
2
- from collections import OrderedDict
3
- from .. import autodoc
4
- from .. import condition_parser
5
- from ..functional import string
6
- import inspect
7
-
8
-
9
- class Get(Base):
10
- _model = None
11
-
12
- _configuration_defaults = {
13
- "model": None,
14
- "model_class": None,
15
- "readable_columns": None,
16
- "where": [],
17
- }
18
-
19
- def __init__(self, di):
20
- super().__init__(di)
21
-
22
- def handle(self, input_output):
23
- model = self.fetch_model(input_output)
24
- if type(model) == str:
25
- return self.error(input_output, model, 404)
26
- return self.success(input_output, self._model_as_json(model, input_output))
27
-
28
- def get_model_id(self, input_output):
29
- routing_data = input_output.routing_data()
30
- if self.id_column_name in routing_data:
31
- return routing_data[self.id_column_name]
32
- if "id" in routing_data:
33
- return routing_data["id"]
34
- raise ValueError("I didn't receive the ID in my routing data. I am probably misconfigured.")
35
-
36
- def fetch_model(self, input_output):
37
- id = self.get_model_id(input_output)
38
- models = self._model.where(f"{self.id_column_name}={id}")
39
- for where in self.configuration("where"):
40
- if type(where) == str:
41
- models = models.where(where)
42
- else:
43
- models = self._di.call_function(
44
- where,
45
- models=models,
46
- input_output=input_output,
47
- routing_data=input_output.routing_data(),
48
- authorization_data=input_output.get_authorization_data(),
49
- )
50
- models = models.where_for_request(
51
- models,
52
- input_output.routing_data(),
53
- input_output.get_authorization_data(),
54
- input_output,
55
- )
56
- authorization = self._configuration.get("authorization", None)
57
- if authorization and hasattr(authorization, "filter_models"):
58
- models = authorization.filter_models(models, input_output.get_authorization_data(), input_output)
59
- model = models.first()
60
- if not model.exists:
61
- return "Not Found"
62
-
63
- return model
64
-
65
- def _check_configuration(self, configuration):
66
- super()._check_configuration(configuration)
67
- error_prefix = "Configuration error for %s:" % (self.__class__.__name__)
68
- has_model_class = ("model_class" in configuration) and configuration["model_class"] is not None
69
- has_model = ("model" in configuration) and configuration["model"] is not None
70
- if not has_model and not has_model_class:
71
- raise KeyError(f"{error_prefix} you must specify 'model' or 'model_class'")
72
- if has_model and has_model_class:
73
- raise KeyError(f"{error_prefix} you specified both 'model' and 'model_class', but can only provide one")
74
- if has_model and inspect.isclass(configuration["model"]):
75
- raise ValueError(
76
- "{error_prefix} you must provide a model instance in the 'model' configuration setting, but a class was provided instead"
77
- )
78
- if "where" in configuration:
79
- if not hasattr(configuration["where"], "__iter__") or type(configuration["where"]) == str:
80
- raise ValueError(
81
- f"{error_prefix} 'where' should be an iterable of coditions or callables "
82
- + ", not "
83
- + str(type(configuration["where"])),
84
- )
85
- for index, where in enumerate(configuration["where"]):
86
- if type(where) != str and not callable(where):
87
- raise ValueError(
88
- f"{error_prefix} 'where' entry should be a string with a condition or a callable that filters models "
89
- + f", but entry #{index+1} is neither of these",
90
- )
91
- self._model = self._di.build(configuration["model_class"]) if has_model_class else configuration["model"]
92
- self._columns = self._model.columns(overrides=configuration.get("column_overrides"))
93
-
94
- def _get_readable_columns(self):
95
- resolved_columns = OrderedDict()
96
- for column_name in self.configuration("readable_columns"):
97
- if column_name not in self._columns:
98
- class_name = self.__class__.__name__
99
- model_class = self._model.__class__.__name__
100
- raise ValueError(
101
- f"Handler {class_name} was configured with readable column '{column_name}' but this "
102
- + f"column doesn't exist for model {model_class}"
103
- )
104
- resolved_columns[column_name] = self._columns[column_name]
105
- return resolved_columns
106
-
107
- def documentation(self):
108
- nice_model = string.camel_case_to_words(self._model.__class__.__name__)
109
-
110
- authentication = self.configuration("authentication")
111
- standard_error_responses = []
112
- if not getattr(authentication, "is_public", False):
113
- standard_error_responses.append(self.documentation_access_denied_response())
114
- if getattr(authentication, "can_authorize", False):
115
- standard_error_responses.append(self.documentation_unauthorized_response())
116
-
117
- id_label = "id" if self.configuration("id_column_name") else self.id_column_name
118
-
119
- return [
120
- autodoc.request.Request(
121
- "Fetch the " + nice_model + " with an " + id_label + " of {" + id_label + "}",
122
- [
123
- self.documentation_success_response(
124
- autodoc.schema.Object(
125
- "data",
126
- children=self.documentation_data_schema(),
127
- model_name=string.camel_case_to_snake_case(self._model.__class__.__name__),
128
- ),
129
- description=f"The {nice_model} record",
130
- ),
131
- *standard_error_responses,
132
- self.documentation_not_found(),
133
- ],
134
- relative_path=self.configuration("base_url").rstrip("/") + "/{" + id_label + "}",
135
- parameters=[
136
- autodoc.request.URLPath(
137
- autodoc.schema.String(id_label),
138
- description=f"The {id_label} of the record to get",
139
- required=True,
140
- )
141
- ],
142
- root_properties={
143
- "security": self.documentation_request_security(),
144
- },
145
- )
146
- ]
147
-
148
- def documentation_models(self):
149
- schema_model_name = string.camel_case_to_snake_case(self._model.__class__.__name__)
150
-
151
- return {
152
- schema_model_name: autodoc.schema.Object(
153
- "data",
154
- children=self.documentation_data_schema(),
155
- ),
156
- }
@@ -1,59 +0,0 @@
1
- from .base import Base
2
- from collections import OrderedDict
3
- from .. import autodoc
4
- from .. import condition_parser
5
- from ..functional import string
6
- import inspect
7
-
8
-
9
- class HealthCheck(Base):
10
- _configuration_defaults = {
11
- "services": [],
12
- "callable": None,
13
- }
14
-
15
- def __init__(self, di):
16
- super().__init__(di)
17
-
18
- def top_level_authentication_and_authorization(self, input_output, authentication=None):
19
- pass
20
-
21
- def handle(self, input_output):
22
- services = self.configuration("services")
23
- health_callable = self.configuration("callable")
24
- try:
25
- if services:
26
- for service in services:
27
- self._di.build(service, cache=True)
28
- if health_callable and not health_callable():
29
- return self.respond(input_output, {"status": "failure"}, 500)
30
- except:
31
- return self.respond(input_output, {"status": "failure"}, 500)
32
-
33
- return self.success(input_output, {})
34
-
35
- def _check_configuration(self, configuration):
36
- error_prefix = "Configuration error for %s:" % (self.__class__.__name__)
37
- services = configuration.get("services")
38
- if services is not None and type(services) != list:
39
- raise ValueError(
40
- f'{error_prefix} "services" should be a list of names, with each name corresponding to a dependency to load'
41
- )
42
- health_callable = configuration.get("callable")
43
- if health_callable is not None and not callable(health_callable):
44
- raise ValueError(
45
- f'{error_prefix} "callable" should be a callable that returns true/false to denote health status'
46
- )
47
-
48
- def documentation(self):
49
- return [
50
- autodoc.request.Request(
51
- "Healthcheck",
52
- [
53
- self.documentation_success_response(
54
- autodoc.schema.Object("data", children=[]),
55
- ),
56
- ],
57
- relative_path=self.configuration("base_url"),
58
- )
59
- ]
@@ -1,79 +0,0 @@
1
- from .exceptions import InputError
2
- from collections import OrderedDict
3
- from abc import abstractmethod
4
-
5
-
6
- class InputProcessing:
7
- _is_create = False
8
-
9
- def _get_writeable_columns(self):
10
- if self._writeable_columns is None:
11
- self._writeable_columns = self._get_rw_columns("writeable")
12
- additional_columns = OrderedDict()
13
- for column in self._writeable_columns.values():
14
- more_columns = column.additional_write_columns(is_create=self._is_create)
15
- for additional_column_name, additional_column in more_columns.items():
16
- additional_columns[additional_column_name] = additional_column
17
- for additional_column_name, additional_column in additional_columns.items():
18
- self._writeable_columns[additional_column_name] = additional_column
19
- return self._writeable_columns
20
-
21
- def _extra_column_errors(self, input_data):
22
- input_errors = {}
23
- allowed = self._get_writeable_columns()
24
- for column_name in input_data.keys():
25
- if column_name not in allowed:
26
- input_errors[column_name] = f"Input column '{column_name}' is not an allowed column"
27
- return input_errors
28
-
29
- def _find_input_errors(self, model, input_data, input_output):
30
- input_errors = {}
31
- for column in self._get_writeable_columns().values():
32
- input_errors = {
33
- **input_errors,
34
- **column.input_errors(model, input_data),
35
- }
36
- input_error_callable = self.configuration("input_error_callable")
37
- if input_error_callable:
38
- more_input_errors = self._di.call_function(
39
- input_error_callable,
40
- input_data=input_data,
41
- request_data=input_data,
42
- input_output=input_output,
43
- routing_data=input_output.routing_data(),
44
- authorization_data=input_output.get_authorization_data(),
45
- )
46
- if type(more_input_errors) != dict:
47
- raise ValueError(
48
- "The input error callable, '"
49
- + str(input_error_callable)
50
- + "', did not return a dictionary as required"
51
- )
52
- input_errors = {
53
- **input_errors,
54
- **more_input_errors,
55
- }
56
- return input_errors
57
-
58
- def request_data(self, input_output, required=True):
59
- # we have to map from internal names to external names, because case mapping
60
- # isn't always one-to-one, so we want to do it exactly the same way that the documentation
61
- # is built.
62
- key_map = {self.auto_case_column_name(key, True): key for key in self._get_writeable_columns().keys()}
63
- # in case the id comes up in the request body
64
- key_map[self.auto_case_internal_column_name("id")] = "id"
65
-
66
- # and make sure we don't drop any data along the way, because the input validation
67
- # needs to return an error for unexpected data.
68
- request_data = {
69
- key_map.get(key, key): value for (key, value) in input_output.request_data(required=required).items()
70
- }
71
- # the parent handler should provide our resource id (we don't do any routing ourselves)
72
- # However, our update/etc handlers need to find the id easily, so I'm going to be lazy and
73
- # just dump it into the request. I'll probably regret that.
74
- routing_data = input_output.routing_data()
75
- # we don't have to worry about casing on the 'id' in routing_data because it doesn't come in from the
76
- # route with a name. Rather, it is populated by clearskies, so will always just be 'id'
77
- if "id" in routing_data:
78
- request_data["id"] = routing_data["id"]
79
- return request_data