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
@@ -0,0 +1,297 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Callable, Self
4
+
5
+ import clearskies.configurable
6
+ import clearskies.di
7
+ import clearskies.end
8
+ from clearskies import exceptions
9
+ from clearskies.authentication import Authentication, Authorization, Public
10
+ from clearskies.endpoint import Endpoint
11
+ from clearskies.functional import routing
12
+ from clearskies.input_outputs import InputOutput
13
+
14
+ if TYPE_CHECKING:
15
+ from clearskies import SecurityHeader
16
+
17
+
18
+ class EndpointGroup(
19
+ clearskies.end.End, # type: ignore
20
+ clearskies.configurable.Configurable,
21
+ clearskies.di.InjectableProperties,
22
+ ):
23
+ """
24
+ An endpoint group brings endpoints together: it basically handles routing.
25
+
26
+ The endpoint group accepts a list of endpoints/endpoint groups and routes requests to them. You can set a URL for
27
+ the endpoint group, and this becomes a URL prefix for all of the endpoints under it. Note that all routing is
28
+ greedy, which means you want to put endpoints with more specific URLs first. Here's an example of how
29
+ you can use them to build a fully functional API that manages both users and companies. Each individual
30
+ endpoint is defined for the purpose of the example, but note that in practice you could accomplish this same
31
+ thing with much less code by using the RestfulApi endpoint:
32
+
33
+ ```python
34
+ import clearskies
35
+ from clearskies.validators import Required, Unique
36
+ from clearskies import columns
37
+
38
+
39
+ class Company(clearskies.Model):
40
+ id_column_name = "id"
41
+ backend = clearskies.backends.MemoryBackend()
42
+
43
+ id = columns.Uuid()
44
+ name = columns.String(
45
+ validators=[
46
+ Required(),
47
+ Unique(),
48
+ ]
49
+ )
50
+
51
+
52
+ class User(clearskies.Model):
53
+ id_column_name = "id"
54
+ backend = clearskies.backends.MemoryBackend()
55
+
56
+ id = columns.Uuid()
57
+ name = columns.String(validators=[Required()])
58
+ username = columns.String(
59
+ validators=[
60
+ Required(),
61
+ Unique(),
62
+ ]
63
+ )
64
+ age = columns.Integer(validators=[Required()])
65
+ created_at = columns.Created()
66
+ updated_at = columns.Updated()
67
+ company_id = columns.BelongsToId(
68
+ Company,
69
+ readable_parent_columns=["id", "name"],
70
+ validators=[Required()],
71
+ )
72
+ company = columns.BelongsToModel("company_id")
73
+
74
+
75
+ readable_user_column_names = [
76
+ "id",
77
+ "name",
78
+ "username",
79
+ "age",
80
+ "created_at",
81
+ "updated_at",
82
+ "company",
83
+ ]
84
+ writeable_user_column_names = ["name", "username", "age", "company_id"]
85
+ users_api = clearskies.EndpointGroup(
86
+ [
87
+ clearskies.endpoints.Update(
88
+ model_class=User,
89
+ url="/:id",
90
+ readable_column_names=readable_user_column_names,
91
+ writeable_column_names=writeable_user_column_names,
92
+ ),
93
+ clearskies.endpoints.Delete(
94
+ model_class=User,
95
+ url="/:id",
96
+ ),
97
+ clearskies.endpoints.Get(
98
+ model_class=User,
99
+ url="/:id",
100
+ readable_column_names=readable_user_column_names,
101
+ ),
102
+ clearskies.endpoints.Create(
103
+ model_class=User,
104
+ readable_column_names=readable_user_column_names,
105
+ writeable_column_names=writeable_user_column_names,
106
+ ),
107
+ clearskies.endpoints.SimpleSearch(
108
+ model_class=User,
109
+ readable_column_names=readable_user_column_names,
110
+ sortable_column_names=readable_user_column_names,
111
+ searchable_column_names=readable_user_column_names,
112
+ default_sort_column_name="name",
113
+ ),
114
+ ],
115
+ url="users",
116
+ )
117
+
118
+ readable_company_column_names = ["id", "name"]
119
+ writeable_company_column_names = ["name"]
120
+ companies_api = clearskies.EndpointGroup(
121
+ [
122
+ clearskies.endpoints.Update(
123
+ model_class=Company,
124
+ url="/:id",
125
+ readable_column_names=readable_company_column_names,
126
+ writeable_column_names=writeable_company_column_names,
127
+ ),
128
+ clearskies.endpoints.Delete(
129
+ model_class=Company,
130
+ url="/:id",
131
+ ),
132
+ clearskies.endpoints.Get(
133
+ model_class=Company,
134
+ url="/:id",
135
+ readable_column_names=readable_company_column_names,
136
+ ),
137
+ clearskies.endpoints.Create(
138
+ model_class=Company,
139
+ readable_column_names=readable_company_column_names,
140
+ writeable_column_names=writeable_company_column_names,
141
+ ),
142
+ clearskies.endpoints.SimpleSearch(
143
+ model_class=Company,
144
+ readable_column_names=readable_company_column_names,
145
+ sortable_column_names=readable_company_column_names,
146
+ searchable_column_names=readable_company_column_names,
147
+ default_sort_column_name="name",
148
+ ),
149
+ ],
150
+ url="companies",
151
+ )
152
+
153
+ wsgi = clearskies.contexts.WsgiRef(clearskies.EndpointGroup([users_api, companies_api]))
154
+ wsgi()
155
+ ```
156
+
157
+ Usage then works exactly as expected:
158
+
159
+ ```bash
160
+ $ curl 'http://localhost:8080/companies' -d '{"name": "Box Store"}' | jq
161
+ {
162
+ "status": "success",
163
+ "error": "",
164
+ "data": {
165
+ "id": "f073ee4d-318d-4e0b-a796-f450c40aa771",
166
+ "name": "Box Store"
167
+ },
168
+ "pagination": {},
169
+ "input_errors": {}
170
+ }
171
+
172
+ curl 'http://localhost:8080/users' -d '{"name": "Bob Brown", "username": "bobbrown", "age": 25, "company_id": "f073ee4d-318d-4e0b-a796-f450c40aa771"}'
173
+ curl 'http://localhost:8080/users' -d '{"name": "Jane Doe", "username": "janedoe", "age": 32, "company_id": "f073ee4d-318d-4e0b-a796-f450c40aa771"}'
174
+
175
+ $ curl 'http://localhost:8080/users' | jq
176
+ {
177
+ "status": "success",
178
+ "error": "",
179
+ "data": [
180
+ {
181
+ "id": "68cbb9e9-689a-4ae0-af77-d60e4cb344f1",
182
+ "name": "Bob Brown",
183
+ "username": "bobbrown",
184
+ "age": 25,
185
+ "created_at": "2025-06-08T10:40:37+00:00",
186
+ "updated_at": "2025-06-08T10:40:37+00:00",
187
+ "company": {
188
+ "id": "f073ee4d-318d-4e0b-a796-f450c40aa771",
189
+ "name": "Box Store"
190
+ }
191
+ },
192
+ {
193
+ "id": "e69c4ebf-38b1-40d2-b523-5d58f5befc7b",
194
+ "name": "Jane Doe",
195
+ "username": "janedoe",
196
+ "age": 32,
197
+ "created_at": "2025-06-08T10:41:04+00:00",
198
+ "updated_at": "2025-06-08T10:41:04+00:00",
199
+ "company": {
200
+ "id": "f073ee4d-318d-4e0b-a796-f450c40aa771",
201
+ "name": "Box Store"
202
+ }
203
+ }
204
+ ],
205
+ "pagination": {
206
+ "number_results": 2,
207
+ "limit": 50,
208
+ "next_page": {}
209
+ },
210
+ "input_errors": {}
211
+ }
212
+
213
+ ```
214
+ """
215
+
216
+ """
217
+ The dependency injection container
218
+ """
219
+ di = clearskies.di.inject.Di()
220
+ url = clearskies.configs.String(default="")
221
+ response_headers = clearskies.configs.StringListOrCallable(default=[])
222
+ authentication = clearskies.configs.Authentication(default=Public())
223
+ authorization = clearskies.configs.Authorization(default=Authorization())
224
+ security_headers = clearskies.configs.SecurityHeaders(default=[])
225
+ cors_header: SecurityHeader = None # type: ignore
226
+ has_cors: bool = False
227
+ endpoints_initialized = False
228
+
229
+ @clearskies.parameters_to_properties.parameters_to_properties
230
+ def __init__(
231
+ self,
232
+ endpoints: list[Endpoint | Self],
233
+ url: str = "",
234
+ response_headers: list[str | Callable[..., list[str]]] = [],
235
+ security_headers: list[SecurityHeader] = [],
236
+ authentication: Authentication = Public(),
237
+ authorization: Authorization = Authorization(),
238
+ ):
239
+ self.finalize_and_validate_configuration()
240
+ for security_header in self.security_headers:
241
+ if not security_header.is_cors:
242
+ continue
243
+ self.cors_header = security_header
244
+ self.has_cors = True
245
+ break
246
+
247
+ if not endpoints:
248
+ raise ValueError(
249
+ "An endpoint group must receive a list of endpoints/endpoint groups, but my list of endpoints is empty."
250
+ )
251
+ if not isinstance(endpoints, list):
252
+ raise ValueError(
253
+ f"An endpoint group must receive a list of endpoints/endpoint groups, but instead of a list I found an object of type '{endpoints.__class__.__name__}'"
254
+ )
255
+ for index, endpoint in enumerate(endpoints):
256
+ if not isinstance(endpoint, Endpoint) and not isinstance(endpoint, self.__class__):
257
+ raise ValueError(
258
+ f"An endpoint group must receive a list of endpoints/endpoint groups, but item #{index + 1} was neither an endpoint nor an endpoint group, but an object of type '{endpoints.__class__.__name__}'"
259
+ )
260
+ if self.url.strip("/"):
261
+ endpoint.add_url_prefix(self.url)
262
+
263
+ def matches_request(self, input_output: InputOutput, allow_partial=True) -> bool:
264
+ """Whether or not we can handle an incoming request based on URL and request method."""
265
+ expected_url = self.url.strip("/")
266
+ incoming_url = input_output.get_full_path().strip("/")
267
+ if not expected_url and not incoming_url:
268
+ return True
269
+ (matches, routing_data) = routing.match_route(expected_url, incoming_url, allow_partial=allow_partial)
270
+ return matches
271
+
272
+ def populate_routing_data(self, input_output: InputOutput) -> Any:
273
+ # only endpoints (not the endpoint group) can handle this because the endpoint group doesn't have the full url
274
+ return None
275
+
276
+ def handle(self, input_output):
277
+ if not self.endpoints_initialized:
278
+ self.endpoints_initialized = True
279
+ for endpoint in self.endpoints:
280
+ endpoint.injectable_properties(self.di)
281
+
282
+ has_match = False
283
+ for endpoint in self.endpoints:
284
+ if not endpoint.matches_request(input_output):
285
+ continue
286
+ has_match = True
287
+ break
288
+
289
+ if not has_match:
290
+ return self.error(input_output, "Not Found", 404)
291
+
292
+ self.add_response_headers(input_output)
293
+ return endpoint(input_output)
294
+
295
+ def error(self, input_output: InputOutput, message: str, status_code: int) -> Any:
296
+ """Return a client-side error (e.g. 400)."""
297
+ return self.respond_json(input_output, {"status": "client_error", "error": message}, status_code)
@@ -0,0 +1,23 @@
1
+ from clearskies.endpoints.advanced_search import AdvancedSearch
2
+ from clearskies.endpoints.callable import Callable
3
+ from clearskies.endpoints.create import Create
4
+ from clearskies.endpoints.delete import Delete
5
+ from clearskies.endpoints.get import Get
6
+ from clearskies.endpoints.health_check import HealthCheck
7
+ from clearskies.endpoints.list import List
8
+ from clearskies.endpoints.restful_api import RestfulApi
9
+ from clearskies.endpoints.simple_search import SimpleSearch
10
+ from clearskies.endpoints.update import Update
11
+
12
+ __all__ = [
13
+ "AdvancedSearch",
14
+ "Callable",
15
+ "Create",
16
+ "Delete",
17
+ "Get",
18
+ "HealthCheck",
19
+ "List",
20
+ "RestfulApi",
21
+ "SimpleSearch",
22
+ "Update",
23
+ ]