clear-skies 1.19.22__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.
- clear_skies-2.0.23.dist-info/METADATA +76 -0
- clear_skies-2.0.23.dist-info/RECORD +265 -0
- {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info}/WHEEL +1 -1
- clearskies/__init__.py +37 -21
- clearskies/action.py +7 -0
- clearskies/authentication/__init__.py +9 -38
- clearskies/authentication/authentication.py +44 -0
- clearskies/authentication/authorization.py +14 -8
- clearskies/authentication/authorization_pass_through.py +22 -0
- clearskies/authentication/jwks.py +135 -58
- clearskies/authentication/public.py +3 -26
- clearskies/authentication/secret_bearer.py +515 -44
- clearskies/autodoc/formats/oai3_json/__init__.py +2 -2
- clearskies/autodoc/formats/oai3_json/oai3_json.py +11 -9
- clearskies/autodoc/formats/oai3_json/parameter.py +6 -3
- clearskies/autodoc/formats/oai3_json/request.py +7 -5
- clearskies/autodoc/formats/oai3_json/response.py +7 -4
- clearskies/autodoc/formats/oai3_json/schema/object.py +10 -1
- clearskies/autodoc/request/__init__.py +2 -0
- clearskies/autodoc/request/header.py +4 -6
- clearskies/autodoc/request/json_body.py +4 -6
- clearskies/autodoc/request/parameter.py +8 -0
- clearskies/autodoc/request/request.py +16 -4
- clearskies/autodoc/request/url_parameter.py +4 -6
- clearskies/autodoc/request/url_path.py +4 -6
- clearskies/autodoc/schema/__init__.py +4 -2
- clearskies/autodoc/schema/array.py +5 -6
- clearskies/autodoc/schema/boolean.py +4 -10
- clearskies/autodoc/schema/date.py +0 -3
- clearskies/autodoc/schema/datetime.py +1 -4
- clearskies/autodoc/schema/double.py +0 -3
- clearskies/autodoc/schema/enum.py +4 -2
- clearskies/autodoc/schema/integer.py +4 -9
- clearskies/autodoc/schema/long.py +0 -3
- clearskies/autodoc/schema/number.py +4 -9
- clearskies/autodoc/schema/object.py +5 -7
- clearskies/autodoc/schema/password.py +0 -3
- clearskies/autodoc/schema/schema.py +11 -0
- clearskies/autodoc/schema/string.py +4 -10
- clearskies/backends/__init__.py +56 -17
- clearskies/backends/api_backend.py +1128 -166
- clearskies/backends/backend.py +54 -85
- clearskies/backends/cursor_backend.py +246 -191
- clearskies/backends/memory_backend.py +514 -208
- clearskies/backends/secrets_backend.py +68 -31
- clearskies/column.py +1221 -0
- clearskies/columns/__init__.py +71 -0
- clearskies/columns/audit.py +306 -0
- clearskies/columns/belongs_to_id.py +478 -0
- clearskies/columns/belongs_to_model.py +129 -0
- clearskies/columns/belongs_to_self.py +109 -0
- clearskies/columns/boolean.py +110 -0
- clearskies/columns/category_tree.py +273 -0
- clearskies/columns/category_tree_ancestors.py +51 -0
- clearskies/columns/category_tree_children.py +126 -0
- clearskies/columns/category_tree_descendants.py +48 -0
- clearskies/columns/created.py +92 -0
- clearskies/columns/created_by_authorization_data.py +114 -0
- clearskies/columns/created_by_header.py +103 -0
- clearskies/columns/created_by_ip.py +90 -0
- clearskies/columns/created_by_routing_data.py +102 -0
- clearskies/columns/created_by_user_agent.py +89 -0
- clearskies/columns/date.py +232 -0
- clearskies/columns/datetime.py +284 -0
- clearskies/columns/email.py +78 -0
- clearskies/columns/float.py +149 -0
- clearskies/columns/has_many.py +529 -0
- clearskies/columns/has_many_self.py +62 -0
- clearskies/columns/has_one.py +21 -0
- clearskies/columns/integer.py +158 -0
- clearskies/columns/json.py +126 -0
- clearskies/columns/many_to_many_ids.py +335 -0
- clearskies/columns/many_to_many_ids_with_data.py +274 -0
- clearskies/columns/many_to_many_models.py +156 -0
- clearskies/columns/many_to_many_pivots.py +132 -0
- clearskies/columns/phone.py +162 -0
- clearskies/columns/select.py +95 -0
- clearskies/columns/string.py +102 -0
- clearskies/columns/timestamp.py +164 -0
- clearskies/columns/updated.py +107 -0
- clearskies/columns/uuid.py +83 -0
- clearskies/configs/README.md +105 -0
- clearskies/configs/__init__.py +170 -0
- clearskies/configs/actions.py +43 -0
- clearskies/configs/any.py +15 -0
- clearskies/configs/any_dict.py +24 -0
- clearskies/configs/any_dict_or_callable.py +25 -0
- clearskies/configs/authentication.py +23 -0
- clearskies/configs/authorization.py +23 -0
- clearskies/configs/boolean.py +18 -0
- clearskies/configs/boolean_or_callable.py +20 -0
- clearskies/configs/callable_config.py +20 -0
- clearskies/configs/columns.py +34 -0
- clearskies/configs/conditions.py +30 -0
- clearskies/configs/config.py +26 -0
- clearskies/configs/datetime.py +20 -0
- clearskies/configs/datetime_or_callable.py +21 -0
- clearskies/configs/email.py +10 -0
- clearskies/configs/email_list.py +17 -0
- clearskies/configs/email_list_or_callable.py +17 -0
- clearskies/configs/email_or_email_list_or_callable.py +59 -0
- clearskies/configs/endpoint.py +23 -0
- clearskies/configs/endpoint_list.py +29 -0
- clearskies/configs/float.py +18 -0
- clearskies/configs/float_or_callable.py +20 -0
- clearskies/configs/headers.py +28 -0
- clearskies/configs/integer.py +18 -0
- clearskies/configs/integer_or_callable.py +20 -0
- clearskies/configs/joins.py +30 -0
- clearskies/configs/list_any_dict.py +32 -0
- clearskies/configs/list_any_dict_or_callable.py +33 -0
- clearskies/configs/model_class.py +35 -0
- clearskies/configs/model_column.py +67 -0
- clearskies/configs/model_columns.py +58 -0
- clearskies/configs/model_destination_name.py +26 -0
- clearskies/configs/model_to_id_column.py +45 -0
- clearskies/configs/readable_model_column.py +11 -0
- clearskies/configs/readable_model_columns.py +11 -0
- clearskies/configs/schema.py +23 -0
- clearskies/configs/searchable_model_columns.py +11 -0
- clearskies/configs/security_headers.py +39 -0
- clearskies/configs/select.py +28 -0
- clearskies/configs/select_list.py +49 -0
- clearskies/configs/string.py +31 -0
- clearskies/configs/string_dict.py +34 -0
- clearskies/configs/string_list.py +47 -0
- clearskies/configs/string_list_or_callable.py +48 -0
- clearskies/configs/string_or_callable.py +18 -0
- clearskies/configs/timedelta.py +20 -0
- clearskies/configs/timezone.py +20 -0
- clearskies/configs/url.py +25 -0
- clearskies/configs/validators.py +45 -0
- clearskies/configs/writeable_model_column.py +11 -0
- clearskies/configs/writeable_model_columns.py +11 -0
- clearskies/configurable.py +78 -0
- clearskies/contexts/__init__.py +8 -8
- clearskies/contexts/cli.py +129 -43
- clearskies/contexts/context.py +93 -56
- clearskies/contexts/wsgi.py +79 -33
- clearskies/contexts/wsgi_ref.py +87 -0
- clearskies/cursors/__init__.py +7 -0
- clearskies/cursors/cursor.py +166 -0
- clearskies/cursors/from_environment/__init__.py +5 -0
- clearskies/cursors/from_environment/mysql.py +51 -0
- clearskies/cursors/from_environment/postgresql.py +49 -0
- clearskies/cursors/from_environment/sqlite.py +35 -0
- clearskies/cursors/mysql.py +61 -0
- clearskies/cursors/postgresql.py +61 -0
- clearskies/cursors/sqlite.py +62 -0
- clearskies/decorators.py +33 -0
- clearskies/decorators.pyi +10 -0
- clearskies/di/__init__.py +11 -7
- clearskies/di/additional_config.py +117 -3
- clearskies/di/additional_config_auto_import.py +12 -0
- clearskies/di/di.py +717 -126
- clearskies/di/inject/__init__.py +23 -0
- clearskies/di/inject/akeyless_sdk.py +16 -0
- clearskies/di/inject/by_class.py +24 -0
- clearskies/di/inject/by_name.py +22 -0
- clearskies/di/inject/di.py +16 -0
- clearskies/di/inject/environment.py +15 -0
- clearskies/di/inject/input_output.py +19 -0
- clearskies/di/inject/now.py +16 -0
- clearskies/di/inject/requests.py +16 -0
- clearskies/di/inject/secrets.py +15 -0
- clearskies/di/inject/utcnow.py +16 -0
- clearskies/di/inject/uuid.py +16 -0
- clearskies/di/injectable.py +32 -0
- clearskies/di/injectable_properties.py +131 -0
- clearskies/end.py +219 -0
- clearskies/endpoint.py +1303 -0
- clearskies/endpoint_group.py +333 -0
- clearskies/endpoints/__init__.py +25 -0
- clearskies/endpoints/advanced_search.py +519 -0
- clearskies/endpoints/callable.py +382 -0
- clearskies/endpoints/create.py +201 -0
- clearskies/endpoints/delete.py +133 -0
- clearskies/endpoints/get.py +267 -0
- clearskies/endpoints/health_check.py +181 -0
- clearskies/endpoints/list.py +567 -0
- clearskies/endpoints/restful_api.py +417 -0
- clearskies/endpoints/schema.py +185 -0
- clearskies/endpoints/simple_search.py +279 -0
- clearskies/endpoints/update.py +188 -0
- clearskies/environment.py +7 -3
- clearskies/exceptions/__init__.py +19 -0
- clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
- clearskies/exceptions/missing_dependency.py +2 -0
- clearskies/exceptions/moved_permanently.py +3 -0
- clearskies/exceptions/moved_temporarily.py +3 -0
- clearskies/functional/__init__.py +2 -2
- clearskies/functional/json.py +47 -0
- clearskies/functional/routing.py +92 -0
- clearskies/functional/string.py +19 -11
- clearskies/functional/validations.py +61 -9
- clearskies/input_outputs/__init__.py +9 -7
- clearskies/input_outputs/cli.py +135 -152
- clearskies/input_outputs/exceptions/__init__.py +6 -1
- clearskies/input_outputs/headers.py +54 -0
- clearskies/input_outputs/input_output.py +77 -123
- clearskies/input_outputs/programmatic.py +62 -0
- clearskies/input_outputs/wsgi.py +36 -48
- clearskies/model.py +1894 -199
- clearskies/query/__init__.py +12 -0
- clearskies/query/condition.py +228 -0
- clearskies/query/join.py +136 -0
- clearskies/query/query.py +193 -0
- clearskies/query/sort.py +27 -0
- clearskies/schema.py +82 -0
- clearskies/secrets/__init__.py +4 -31
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
- clearskies/secrets/akeyless.py +421 -155
- clearskies/secrets/exceptions/__init__.py +7 -1
- clearskies/secrets/exceptions/not_found_error.py +2 -0
- clearskies/secrets/exceptions/permissions_error.py +2 -0
- clearskies/secrets/secrets.py +12 -11
- clearskies/security_header.py +17 -0
- clearskies/security_headers/__init__.py +8 -8
- clearskies/security_headers/cache_control.py +47 -109
- clearskies/security_headers/cors.py +38 -92
- clearskies/security_headers/csp.py +76 -150
- clearskies/security_headers/hsts.py +14 -15
- clearskies/typing.py +11 -0
- clearskies/validator.py +36 -0
- clearskies/validators/__init__.py +33 -0
- clearskies/validators/after_column.py +61 -0
- clearskies/validators/before_column.py +15 -0
- clearskies/validators/in_the_future.py +29 -0
- clearskies/validators/in_the_future_at_least.py +13 -0
- clearskies/validators/in_the_future_at_most.py +12 -0
- clearskies/validators/in_the_past.py +29 -0
- clearskies/validators/in_the_past_at_least.py +12 -0
- clearskies/validators/in_the_past_at_most.py +12 -0
- clearskies/validators/maximum_length.py +25 -0
- clearskies/validators/maximum_value.py +28 -0
- clearskies/validators/minimum_length.py +25 -0
- clearskies/validators/minimum_value.py +28 -0
- clearskies/{input_requirements → validators}/required.py +18 -9
- clearskies/validators/timedelta.py +58 -0
- clearskies/validators/unique.py +28 -0
- clear_skies-1.19.22.dist-info/METADATA +0 -46
- clear_skies-1.19.22.dist-info/RECORD +0 -206
- clearskies/application.py +0 -29
- clearskies/authentication/auth0_jwks.py +0 -118
- clearskies/authentication/auth_exception.py +0 -2
- clearskies/authentication/jwks_jwcrypto.py +0 -39
- clearskies/backends/example_backend.py +0 -43
- clearskies/backends/file_backend.py +0 -48
- clearskies/backends/json_backend.py +0 -7
- clearskies/backends/restful_api_advanced_search_backend.py +0 -138
- clearskies/binding_config.py +0 -16
- clearskies/column_types/__init__.py +0 -184
- clearskies/column_types/audit.py +0 -235
- clearskies/column_types/belongs_to.py +0 -250
- clearskies/column_types/boolean.py +0 -60
- clearskies/column_types/category_tree.py +0 -226
- clearskies/column_types/column.py +0 -373
- clearskies/column_types/created.py +0 -26
- clearskies/column_types/created_by_authorization_data.py +0 -26
- clearskies/column_types/created_by_header.py +0 -24
- clearskies/column_types/created_by_ip.py +0 -17
- clearskies/column_types/created_by_routing_data.py +0 -25
- clearskies/column_types/created_by_user_agent.py +0 -17
- clearskies/column_types/created_micro.py +0 -26
- clearskies/column_types/datetime.py +0 -108
- clearskies/column_types/datetime_micro.py +0 -12
- clearskies/column_types/email.py +0 -18
- clearskies/column_types/float.py +0 -43
- clearskies/column_types/has_many.py +0 -139
- clearskies/column_types/integer.py +0 -41
- clearskies/column_types/json.py +0 -25
- clearskies/column_types/many_to_many.py +0 -278
- clearskies/column_types/many_to_many_with_data.py +0 -162
- clearskies/column_types/select.py +0 -11
- clearskies/column_types/string.py +0 -24
- clearskies/column_types/updated.py +0 -24
- clearskies/column_types/updated_micro.py +0 -24
- clearskies/column_types/uuid.py +0 -25
- clearskies/columns.py +0 -123
- clearskies/condition_parser.py +0 -172
- clearskies/contexts/build_context.py +0 -54
- clearskies/contexts/convert_to_application.py +0 -190
- clearskies/contexts/extract_handler.py +0 -37
- clearskies/contexts/test.py +0 -94
- clearskies/decorators/__init__.py +0 -39
- clearskies/decorators/auth0_jwks.py +0 -22
- clearskies/decorators/authorization.py +0 -10
- clearskies/decorators/binding_classes.py +0 -9
- clearskies/decorators/binding_modules.py +0 -9
- clearskies/decorators/bindings.py +0 -9
- clearskies/decorators/create.py +0 -10
- clearskies/decorators/delete.py +0 -10
- clearskies/decorators/docs.py +0 -14
- clearskies/decorators/get.py +0 -10
- clearskies/decorators/jwks.py +0 -26
- clearskies/decorators/merge.py +0 -124
- clearskies/decorators/patch.py +0 -10
- clearskies/decorators/post.py +0 -10
- clearskies/decorators/public.py +0 -11
- clearskies/decorators/response_headers.py +0 -10
- clearskies/decorators/return_raw_response.py +0 -9
- clearskies/decorators/schema.py +0 -10
- clearskies/decorators/secret_bearer.py +0 -24
- clearskies/decorators/security_headers.py +0 -10
- clearskies/di/standard_dependencies.py +0 -140
- clearskies/di/test_module/__init__.py +0 -6
- clearskies/di/test_module/another_module/__init__.py +0 -2
- clearskies/di/test_module/module_class.py +0 -5
- clearskies/handlers/__init__.py +0 -41
- clearskies/handlers/advanced_search.py +0 -271
- clearskies/handlers/base.py +0 -473
- clearskies/handlers/callable.py +0 -189
- clearskies/handlers/create.py +0 -35
- clearskies/handlers/crud_by_method.py +0 -18
- clearskies/handlers/database_connector.py +0 -32
- clearskies/handlers/delete.py +0 -61
- clearskies/handlers/exceptions/__init__.py +0 -5
- clearskies/handlers/exceptions/not_found.py +0 -3
- clearskies/handlers/get.py +0 -156
- clearskies/handlers/health_check.py +0 -59
- clearskies/handlers/input_processing.py +0 -79
- clearskies/handlers/list.py +0 -530
- clearskies/handlers/mygrations.py +0 -82
- clearskies/handlers/request_method_routing.py +0 -47
- clearskies/handlers/restful_api.py +0 -218
- clearskies/handlers/routing.py +0 -62
- clearskies/handlers/schema_helper.py +0 -128
- clearskies/handlers/simple_routing.py +0 -204
- clearskies/handlers/simple_routing_route.py +0 -192
- clearskies/handlers/simple_search.py +0 -136
- clearskies/handlers/update.py +0 -96
- clearskies/handlers/write.py +0 -193
- clearskies/input_requirements/__init__.py +0 -68
- clearskies/input_requirements/after.py +0 -36
- clearskies/input_requirements/before.py +0 -36
- clearskies/input_requirements/in_the_future_at_least.py +0 -19
- clearskies/input_requirements/in_the_future_at_most.py +0 -19
- clearskies/input_requirements/in_the_past_at_least.py +0 -19
- clearskies/input_requirements/in_the_past_at_most.py +0 -19
- clearskies/input_requirements/maximum_length.py +0 -19
- clearskies/input_requirements/minimum_length.py +0 -22
- clearskies/input_requirements/requirement.py +0 -25
- clearskies/input_requirements/time_delta.py +0 -38
- clearskies/input_requirements/unique.py +0 -18
- clearskies/mocks/__init__.py +0 -7
- clearskies/mocks/input_output.py +0 -124
- clearskies/mocks/models.py +0 -142
- clearskies/models.py +0 -345
- clearskies/security_headers/base.py +0 -12
- clearskies/tests/simple_api/models/__init__.py +0 -2
- clearskies/tests/simple_api/models/status.py +0 -23
- clearskies/tests/simple_api/models/user.py +0 -21
- clearskies/tests/simple_api/users_api.py +0 -64
- {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info/licenses}/LICENSE +0 -0
- /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
- /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
- /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
- /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
- /clearskies/{secrets/exceptions → exceptions}/not_found.py +0 -0
- /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
- /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
5
|
+
|
|
6
|
+
from clearskies import configs, decorators
|
|
7
|
+
from clearskies.authentication import Authentication, Authorization, Public
|
|
8
|
+
from clearskies.endpoint_group import EndpointGroup
|
|
9
|
+
from clearskies.endpoints.create import Create
|
|
10
|
+
from clearskies.endpoints.delete import Delete
|
|
11
|
+
from clearskies.endpoints.get import Get
|
|
12
|
+
from clearskies.endpoints.simple_search import SimpleSearch
|
|
13
|
+
from clearskies.endpoints.update import Update
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from clearskies import Column, Endpoint, Model, Schema, SecurityHeader
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RestfulApi(EndpointGroup):
|
|
20
|
+
"""
|
|
21
|
+
Full CRUD operations for a model.
|
|
22
|
+
|
|
23
|
+
This endpoint group sets up all five standard endpoints to manage a model:
|
|
24
|
+
|
|
25
|
+
1. Create
|
|
26
|
+
2. Update
|
|
27
|
+
3. Delete
|
|
28
|
+
4. Get
|
|
29
|
+
5. List
|
|
30
|
+
|
|
31
|
+
As such, you can set any option for all of the above endpoints. All five endpoints are enabled by default
|
|
32
|
+
but can be turned off individually. It's important to understand that the actual API behavior is controlled
|
|
33
|
+
by other endoints. This endpoint group creates them and routes requests to them. So, to fully understand
|
|
34
|
+
the behavior of the subsequent Restful API, you have to consult the documentation for the endpoints themselves.
|
|
35
|
+
|
|
36
|
+
For routing purposes, the create and list endpoints are reachable via the URL specified for this endpoint group
|
|
37
|
+
and are separated by request method (POST for create by default, GET for list). The update, delete, and get
|
|
38
|
+
endoints all expect the id to be appended to the base URL, and then are separated by request method
|
|
39
|
+
(PATCH for update, DELETE for delete, and GET for get). See the example app and calls below:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import clearskies
|
|
43
|
+
from clearskies.validators import Required, Unique
|
|
44
|
+
from clearskies import columns
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class User(clearskies.Model):
|
|
48
|
+
id_column_name = "id"
|
|
49
|
+
backend = clearskies.backends.MemoryBackend()
|
|
50
|
+
|
|
51
|
+
id = columns.Uuid()
|
|
52
|
+
name = columns.String(validators=[Required()])
|
|
53
|
+
username = columns.String(
|
|
54
|
+
validators=[
|
|
55
|
+
Required(),
|
|
56
|
+
Unique(),
|
|
57
|
+
]
|
|
58
|
+
)
|
|
59
|
+
age = columns.Integer(validators=[Required()])
|
|
60
|
+
created_at = columns.Created()
|
|
61
|
+
updated_at = columns.Updated()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
wsgi = clearskies.contexts.WsgiRef(
|
|
65
|
+
clearskies.endpoints.RestfulApi(
|
|
66
|
+
url="users",
|
|
67
|
+
model_class=User,
|
|
68
|
+
readable_column_names=["id", "name", "username", "age", "created_at", "updated_at"],
|
|
69
|
+
writeable_column_names=["name", "username", "age"],
|
|
70
|
+
sortable_column_names=["id", "name", "username", "age", "created_at", "updated_at"],
|
|
71
|
+
searchable_column_names=["id", "name", "username", "age", "created_at", "updated_at"],
|
|
72
|
+
default_sort_column_name="name",
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
wsgi()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Which spins up a fully functional API. In the below usage examples we create two users, fetch
|
|
79
|
+
one of them, update a user, delete the other, and then list all users.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
$ curl 'http://localhost:8080/users' -d '{"name":"Bob", "username": "bob", "age": 25}' | jq
|
|
83
|
+
{
|
|
84
|
+
"status": "success",
|
|
85
|
+
"error": "",
|
|
86
|
+
"data": {
|
|
87
|
+
"id": "8bd9c03f-bb0c-41bd-afbc-f9526ded88f4",
|
|
88
|
+
"name": "Bob",
|
|
89
|
+
"username": "bob",
|
|
90
|
+
"age": 25,
|
|
91
|
+
"created_at": "2025-06-10T12:39:35+00:00",
|
|
92
|
+
"updated_at": "2025-06-10T12:39:35+00:00"
|
|
93
|
+
},
|
|
94
|
+
"pagination": {},
|
|
95
|
+
"input_errors": {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
$ curl 'http://localhost:8080/users' -d '{"name":"Alice", "username": "alice", "age": 22}' | jq
|
|
99
|
+
{
|
|
100
|
+
"status": "success",
|
|
101
|
+
"error": "",
|
|
102
|
+
"data": {
|
|
103
|
+
"id": "16d483c6-0eb1-4104-b07b-32f3d736223f",
|
|
104
|
+
"name": "Alice",
|
|
105
|
+
"username": "alice",
|
|
106
|
+
"age": 22,
|
|
107
|
+
"created_at": "2025-06-10T12:42:59+00:00",
|
|
108
|
+
"updated_at": "2025-06-10T12:42:59+00:00"
|
|
109
|
+
},
|
|
110
|
+
"pagination": {},
|
|
111
|
+
"input_errors": {}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
$ curl 'http://localhost:8080/users/8bd9c03f-bb0c-41bd-afbc-f9526ded88f4' | jq
|
|
115
|
+
{
|
|
116
|
+
"status": "success",
|
|
117
|
+
"error": "",
|
|
118
|
+
"data": {
|
|
119
|
+
"id": "8bd9c03f-bb0c-41bd-afbc-f9526ded88f4",
|
|
120
|
+
"name": "Bob",
|
|
121
|
+
"username": "bob",
|
|
122
|
+
"age": 25,
|
|
123
|
+
"created_at": "2025-06-10T12:39:35+00:00",
|
|
124
|
+
"updated_at": "2025-06-10T12:39:35+00:00"
|
|
125
|
+
},
|
|
126
|
+
"pagination": {},
|
|
127
|
+
"input_errors": {}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
$ curl 'http://localhost:8080/users/16d483c6-0eb1-4104-b07b-32f3d736223f' -d '{"name":"Alice Smith", "age": 23}' -X PATCH | jq
|
|
131
|
+
{
|
|
132
|
+
"status": "success",
|
|
133
|
+
"error": "",
|
|
134
|
+
"data": {
|
|
135
|
+
"id": "16d483c6-0eb1-4104-b07b-32f3d736223f",
|
|
136
|
+
"name": "Alice Smith",
|
|
137
|
+
"username": "alice",
|
|
138
|
+
"age": 23,
|
|
139
|
+
"created_at": "2025-06-10T12:42:59+00:00",
|
|
140
|
+
"updated_at": "2025-06-10T12:45:01+00:00"
|
|
141
|
+
},
|
|
142
|
+
"pagination": {},
|
|
143
|
+
"input_errors": {}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
$ curl 'http://localhost:8080/users/8bd9c03f-bb0c-41bd-afbc-f9526ded88f4' -X DELETE | jq
|
|
147
|
+
{
|
|
148
|
+
"status": "success",
|
|
149
|
+
"error": "",
|
|
150
|
+
"data": {},
|
|
151
|
+
"pagination": {},
|
|
152
|
+
"input_errors": {}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
$ curl 'http://localhost:8080/users/' | jq
|
|
156
|
+
{
|
|
157
|
+
"status": "success",
|
|
158
|
+
"error": "",
|
|
159
|
+
"data": [
|
|
160
|
+
{
|
|
161
|
+
"id": "16d483c6-0eb1-4104-b07b-32f3d736223f",
|
|
162
|
+
"name": "Alice Smith",
|
|
163
|
+
"username": "alice",
|
|
164
|
+
"age": 23,
|
|
165
|
+
"created_at": "2025-06-10T12:42:59+00:00",
|
|
166
|
+
"updated_at": "2025-06-10T12:45:01+00:00"
|
|
167
|
+
}
|
|
168
|
+
],
|
|
169
|
+
"pagination": {
|
|
170
|
+
"number_results": 1,
|
|
171
|
+
"limit": 50,
|
|
172
|
+
"next_page": {}
|
|
173
|
+
},
|
|
174
|
+
"input_errors": {}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
"""
|
|
180
|
+
The endpoint class to use for managing the create operation.
|
|
181
|
+
|
|
182
|
+
This defaults to `clearskies.endpoints.Create`. To disable the create operation all together,
|
|
183
|
+
set this to None.
|
|
184
|
+
"""
|
|
185
|
+
create_endpoint = configs.Endpoint(default=Create)
|
|
186
|
+
|
|
187
|
+
"""
|
|
188
|
+
The endpoint class to use for managing the delete operation.
|
|
189
|
+
|
|
190
|
+
This defaults to `clearskies.endpoints.Delete`. To disable the delete operation all together,
|
|
191
|
+
set this to None.
|
|
192
|
+
"""
|
|
193
|
+
delete_endpoint = configs.Endpoint(default=Delete)
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
The endpoint class to use for managing the update operation.
|
|
197
|
+
|
|
198
|
+
This defaults to `clearskies.endpoints.Update`. To disable the update operation all together,
|
|
199
|
+
set this to None.
|
|
200
|
+
"""
|
|
201
|
+
update_endpoint = configs.Endpoint(default=Update)
|
|
202
|
+
|
|
203
|
+
"""
|
|
204
|
+
The endpoint class to use to fetch individual records.
|
|
205
|
+
|
|
206
|
+
This defaults to `clearskies.endpoints.Get`. To disable the get operation all together,
|
|
207
|
+
set this to None.
|
|
208
|
+
"""
|
|
209
|
+
get_endpoint = configs.Endpoint(default=Get)
|
|
210
|
+
|
|
211
|
+
"""
|
|
212
|
+
The endpoint class to use to list records.
|
|
213
|
+
|
|
214
|
+
This defaults to `clearskies.endpoints.SimpleSearch`. To disable the list operation all together,
|
|
215
|
+
set this to None.
|
|
216
|
+
"""
|
|
217
|
+
list_endpoint = configs.Endpoint(default=SimpleSearch)
|
|
218
|
+
|
|
219
|
+
"""
|
|
220
|
+
The request method(s) to use to route to the create operation. Default is ["POST"].
|
|
221
|
+
"""
|
|
222
|
+
create_request_methods = configs.SelectList(
|
|
223
|
+
allowed_values=["GET", "POST", "PUT", "DELETE", "PATCH"], default=["POST"]
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
"""
|
|
227
|
+
The request method(s) to use to route to the update operation. Default is ["PATCH"].
|
|
228
|
+
"""
|
|
229
|
+
update_request_methods = configs.SelectList(
|
|
230
|
+
allowed_values=["GET", "POST", "PUT", "DELETE", "PATCH"], default=["PATCH"]
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
"""
|
|
234
|
+
The request method(s) to use to route to the delete operation. Default is ["DELETE"].
|
|
235
|
+
"""
|
|
236
|
+
delete_request_methods = configs.SelectList(
|
|
237
|
+
allowed_values=["GET", "POST", "PUT", "DELETE", "PATCH"], default=["DELETE"]
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
"""
|
|
241
|
+
The request method(s) to use to route to the get operation. Default is ["GET"].
|
|
242
|
+
"""
|
|
243
|
+
get_request_methods = configs.SelectList(allowed_values=["GET", "POST", "PUT", "DELETE", "PATCH"], default=["GET"])
|
|
244
|
+
|
|
245
|
+
"""
|
|
246
|
+
The request method(s) to use to route to the create operation. Default is ["GET"].
|
|
247
|
+
"""
|
|
248
|
+
list_request_methods = configs.SelectList(
|
|
249
|
+
allowed_values=["GET", "POST", "PUT", "DELETE", "PATCH", "QUERY"], default=["GET", "POST", "QUERY"]
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
"""
|
|
253
|
+
The request method(s) to use to route to the create operation. Default is ["POST"].
|
|
254
|
+
"""
|
|
255
|
+
id_column_name = configs.ModelColumn("model_class", default=None)
|
|
256
|
+
|
|
257
|
+
"""
|
|
258
|
+
The base URL to be used for all the endpoints.
|
|
259
|
+
"""
|
|
260
|
+
url = configs.String(default="")
|
|
261
|
+
|
|
262
|
+
authentication = configs.Authentication(default=Public())
|
|
263
|
+
authorization = configs.Authorization(default=Authorization())
|
|
264
|
+
output_map = configs.Callable(default=None)
|
|
265
|
+
output_schema = configs.Schema(default=None)
|
|
266
|
+
model_class = configs.ModelClass(default=None)
|
|
267
|
+
readable_column_names = configs.ReadableModelColumns("model_class", default=[])
|
|
268
|
+
writeable_column_names = configs.WriteableModelColumns("model_class", default=[])
|
|
269
|
+
searchable_column_names = configs.SearchableModelColumns("model_class", default=[])
|
|
270
|
+
sortable_column_names = configs.ReadableModelColumns("model_class", default=[])
|
|
271
|
+
default_sort_column_name = configs.ModelColumn("model_class", required=True)
|
|
272
|
+
default_sort_direction = configs.Select(["ASC", "DESC"], default="ASC")
|
|
273
|
+
default_limit = configs.Integer(default=50)
|
|
274
|
+
maximum_limit = configs.Integer(default=200)
|
|
275
|
+
group_by_column_name = configs.ModelColumn("model_class")
|
|
276
|
+
input_validation_callable = configs.Callable(default=None)
|
|
277
|
+
include_routing_data_in_request_data = configs.Boolean(default=False)
|
|
278
|
+
column_overrides = configs.Columns(default={})
|
|
279
|
+
internal_casing = configs.Select(["snake_case", "camelCase", "TitleCase"], default="snake_case")
|
|
280
|
+
external_casing = configs.Select(["snake_case", "camelCase", "TitleCase"], default="snake_case")
|
|
281
|
+
security_headers = configs.SecurityHeaders(default=[])
|
|
282
|
+
description = configs.String(default="")
|
|
283
|
+
where = configs.Conditions(default=[])
|
|
284
|
+
_descriptor_config_map = None
|
|
285
|
+
|
|
286
|
+
@decorators.parameters_to_properties
|
|
287
|
+
def __init__(
|
|
288
|
+
self,
|
|
289
|
+
model_class: type[Model],
|
|
290
|
+
writeable_column_names: list[str],
|
|
291
|
+
readable_column_names: list[str],
|
|
292
|
+
searchable_column_names: list[str],
|
|
293
|
+
sortable_column_names: list[str],
|
|
294
|
+
default_sort_column_name: str,
|
|
295
|
+
read_only: bool = False,
|
|
296
|
+
create_endpoint: type[Endpoint] | None = Create,
|
|
297
|
+
delete_endpoint: type[Endpoint] | None = Delete,
|
|
298
|
+
update_endpoint: type[Endpoint] | None = Update,
|
|
299
|
+
get_endpoint: type[Endpoint] | None = Get,
|
|
300
|
+
list_endpoint: type[Endpoint] | None = SimpleSearch,
|
|
301
|
+
create_request_methods: list[str] = ["POST"],
|
|
302
|
+
update_request_methods: list[str] = ["PATCH"],
|
|
303
|
+
delete_request_methods: list[str] = ["DELETE"],
|
|
304
|
+
get_request_methods: list[str] = ["GET"],
|
|
305
|
+
list_request_methods: list[str] = ["GET"],
|
|
306
|
+
id_column_name: str = "",
|
|
307
|
+
group_by_column_name: str = "",
|
|
308
|
+
input_validation_callable: Callable | None = None,
|
|
309
|
+
include_routing_data_in_request_data: bool = False,
|
|
310
|
+
url: str = "",
|
|
311
|
+
default_sort_direction: str = "ASC",
|
|
312
|
+
default_limit: int = 50,
|
|
313
|
+
maximum_limit: int = 200,
|
|
314
|
+
request_methods: list[str] = ["POST"],
|
|
315
|
+
response_headers: list[str | Callable[..., list[str]]] = [],
|
|
316
|
+
output_map: Callable[..., dict[str, Any]] | None = None,
|
|
317
|
+
output_schema: Schema | None = None,
|
|
318
|
+
column_overrides: dict[str, Column] = {},
|
|
319
|
+
internal_casing: str = "snake_case",
|
|
320
|
+
external_casing: str = "snake_case",
|
|
321
|
+
security_headers: list[SecurityHeader] = [],
|
|
322
|
+
description: str = "",
|
|
323
|
+
authentication: Authentication = Public(),
|
|
324
|
+
authorization: Authorization = Authorization(),
|
|
325
|
+
):
|
|
326
|
+
self.finalize_and_validate_configuration()
|
|
327
|
+
|
|
328
|
+
id_column_name = id_column_name if id_column_name else model_class.id_column_name
|
|
329
|
+
|
|
330
|
+
# figure out which endpoints we actually need
|
|
331
|
+
endpoints_to_build = []
|
|
332
|
+
if not read_only:
|
|
333
|
+
if create_endpoint:
|
|
334
|
+
endpoints_to_build.append(
|
|
335
|
+
{
|
|
336
|
+
"class": create_endpoint,
|
|
337
|
+
"request_methods": create_request_methods,
|
|
338
|
+
}
|
|
339
|
+
)
|
|
340
|
+
if delete_endpoint:
|
|
341
|
+
endpoints_to_build.append(
|
|
342
|
+
{
|
|
343
|
+
"class": delete_endpoint,
|
|
344
|
+
"request_methods": delete_request_methods,
|
|
345
|
+
"url_suffix": f"/:{id_column_name}",
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
if update_endpoint:
|
|
349
|
+
endpoints_to_build.append(
|
|
350
|
+
{
|
|
351
|
+
"class": update_endpoint,
|
|
352
|
+
"request_methods": update_request_methods,
|
|
353
|
+
"url_suffix": f"/:{id_column_name}",
|
|
354
|
+
}
|
|
355
|
+
)
|
|
356
|
+
if get_endpoint:
|
|
357
|
+
endpoints_to_build.append(
|
|
358
|
+
{
|
|
359
|
+
"class": get_endpoint,
|
|
360
|
+
"request_methods": get_request_methods,
|
|
361
|
+
"url_suffix": f"/:{id_column_name}",
|
|
362
|
+
}
|
|
363
|
+
)
|
|
364
|
+
if list_endpoint:
|
|
365
|
+
endpoints_to_build.append(
|
|
366
|
+
{
|
|
367
|
+
"class": list_endpoint,
|
|
368
|
+
"request_methods": list_request_methods,
|
|
369
|
+
}
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
# and now build them! Pass along our own kwargs to the endoints when we build them. Now, technically, I
|
|
373
|
+
# know what the kwargs are for each endpoint. However, it would be a lot of duplication to manually
|
|
374
|
+
# instantiate each endpoint and pass along all the kwargs. So, fetch the list of kwargs from our own
|
|
375
|
+
# __init__ and then compare that with the kwargs of the __init__ for each endpoint and map everything
|
|
376
|
+
# automatically like that. Then add in the individual config from above.
|
|
377
|
+
|
|
378
|
+
# these lines take all of the arguments we were initialized with and dumps it into a dict. It's the
|
|
379
|
+
# equivalent of combining both *args and **kwargs without using either
|
|
380
|
+
my_args = inspect.getfullargspec(self.__class__)
|
|
381
|
+
local_variables = inspect.currentframe().f_locals # type: ignore
|
|
382
|
+
available_args = {arg: local_variables[arg] for arg in my_args.args[1:]}
|
|
383
|
+
|
|
384
|
+
# we handle this one manually
|
|
385
|
+
del available_args["url"]
|
|
386
|
+
|
|
387
|
+
# now loop through the list of endpoints
|
|
388
|
+
endpoints = []
|
|
389
|
+
for endpoint_to_build in endpoints_to_build:
|
|
390
|
+
# grab our class and any pre-defined configs
|
|
391
|
+
endpoint_class = endpoint_to_build["class"]
|
|
392
|
+
url_suffix = endpoint_to_build.get("url_suffix")
|
|
393
|
+
|
|
394
|
+
# now get the allowed args out of the init and fill them out with our own.
|
|
395
|
+
endpoint_args = inspect.getfullargspec(endpoint_class)
|
|
396
|
+
nendpoint_args = len(endpoint_args.args)
|
|
397
|
+
nendpoint_kwargs = len(endpoint_args.defaults) if endpoint_args.defaults else 0
|
|
398
|
+
final_args: list[str] = []
|
|
399
|
+
final_kwargs: dict[str, Any] = {}
|
|
400
|
+
for arg in endpoint_args.args[1:]:
|
|
401
|
+
if not available_args.get(arg):
|
|
402
|
+
continue
|
|
403
|
+
final_kwargs[arg] = available_args[arg]
|
|
404
|
+
|
|
405
|
+
if url_suffix:
|
|
406
|
+
final_kwargs["url"] = url_suffix
|
|
407
|
+
final_kwargs["request_methods"] = endpoint_to_build["request_methods"]
|
|
408
|
+
endpoints.append(endpoint_class(*final_args, **final_kwargs)) # type: ignore
|
|
409
|
+
|
|
410
|
+
super().__init__(
|
|
411
|
+
endpoints,
|
|
412
|
+
url=url,
|
|
413
|
+
response_headers=response_headers,
|
|
414
|
+
security_headers=security_headers,
|
|
415
|
+
authentication=authentication,
|
|
416
|
+
authorization=authorization,
|
|
417
|
+
)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
4
|
+
|
|
5
|
+
from clearskies import authentication, autodoc, configs, decorators
|
|
6
|
+
from clearskies.endpoint import Endpoint
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from clearskies import SecurityHeader
|
|
10
|
+
from clearskies.authentication import Authentication
|
|
11
|
+
from clearskies.input_outputs import InputOutput
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Schema(Endpoint):
|
|
15
|
+
"""
|
|
16
|
+
An endpoint that automatically creates a swagger doc for the application.
|
|
17
|
+
|
|
18
|
+
The schema endpoint must always be attached to an endpoint group. It will document all endpoints
|
|
19
|
+
attached to its parent endpoint group.
|
|
20
|
+
|
|
21
|
+
Keep in mind that the routing in the endpoint group is greedy and goes from top-down. As a result,
|
|
22
|
+
since the schema endpoint (typically) has a specific URL, it's usually best for it to be at the top
|
|
23
|
+
of your endpoint list. The following example builds an application with two endpoint groups, each
|
|
24
|
+
of which has a schema endpoint:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
import clearskies
|
|
28
|
+
from clearskies.validators import Required, Unique
|
|
29
|
+
from clearskies import columns
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class User(clearskies.Model):
|
|
33
|
+
id_column_name = "id"
|
|
34
|
+
backend = clearskies.backends.MemoryBackend()
|
|
35
|
+
|
|
36
|
+
id = columns.Uuid()
|
|
37
|
+
name = columns.String(validators=[Required()])
|
|
38
|
+
username = columns.String(
|
|
39
|
+
validators=[
|
|
40
|
+
Required(),
|
|
41
|
+
Unique(),
|
|
42
|
+
]
|
|
43
|
+
)
|
|
44
|
+
age = columns.Integer(validators=[Required()])
|
|
45
|
+
company_name = columns.String()
|
|
46
|
+
created_at = columns.Created()
|
|
47
|
+
updated_at = columns.Updated()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
readable_column_names = [
|
|
51
|
+
"id",
|
|
52
|
+
"name",
|
|
53
|
+
"username",
|
|
54
|
+
"age",
|
|
55
|
+
"company_name",
|
|
56
|
+
"created_at",
|
|
57
|
+
"updated_at",
|
|
58
|
+
]
|
|
59
|
+
writeable_user_column_names = ["name", "username", "age", "company_name"]
|
|
60
|
+
users_api = clearskies.EndpointGroup(
|
|
61
|
+
[
|
|
62
|
+
clearskies.endpoints.Schema(url="schema"),
|
|
63
|
+
clearskies.endpoints.RestfulApi(
|
|
64
|
+
url="users",
|
|
65
|
+
model_class=User,
|
|
66
|
+
readable_column_names=readable_column_names,
|
|
67
|
+
writeable_column_names=writeable_user_column_names,
|
|
68
|
+
sortable_column_names=readable_column_names,
|
|
69
|
+
searchable_column_names=readable_column_names,
|
|
70
|
+
default_sort_column_name="name",
|
|
71
|
+
),
|
|
72
|
+
],
|
|
73
|
+
url="/users",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class SomeThing(clearskies.Model):
|
|
78
|
+
id_column_name = "id"
|
|
79
|
+
backend = clearskies.backends.MemoryBackend()
|
|
80
|
+
|
|
81
|
+
id = clearskies.columns.Uuid()
|
|
82
|
+
thing_1 = clearskies.columns.String(validators=[Required()])
|
|
83
|
+
thing_2 = clearskies.columns.String(validators=[Unique()])
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
more_endpoints = clearskies.EndpointGroup(
|
|
87
|
+
[
|
|
88
|
+
clearskies.endpoints.HealthCheck(url="health"),
|
|
89
|
+
clearskies.endpoints.Schema(url="schema"),
|
|
90
|
+
clearskies.endpoints.Callable(
|
|
91
|
+
lambda request_data, some_things: some_things.create(request_data),
|
|
92
|
+
model_class=SomeThing,
|
|
93
|
+
readable_column_names=["id", "thing_1", "thing_2"],
|
|
94
|
+
writeable_column_names=["thing_1", "thing_2"],
|
|
95
|
+
request_methods=["POST"],
|
|
96
|
+
url="some_thing",
|
|
97
|
+
),
|
|
98
|
+
users_api,
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
wsgi = clearskies.contexts.WsgiRef(more_endpoints)
|
|
103
|
+
wsgi()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
We attach the `more_endpoints` endpoint group to our context, and this contains 4 endpoints:
|
|
107
|
+
|
|
108
|
+
1. A healthcheck
|
|
109
|
+
2. A schema endpoint
|
|
110
|
+
3. A callable endpoint
|
|
111
|
+
4. The `users_api` endpoint group.
|
|
112
|
+
|
|
113
|
+
The `users_api` endpoint group then contains it's own schema endpoint and a restful api endpoint
|
|
114
|
+
with all our standard user CRUD operations. As a result, we can fetch two different schema endpoints:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
curl 'http://localhost/schema'
|
|
118
|
+
|
|
119
|
+
curl 'http://localhost/users/schema'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The former documents all endpoints in the system. The latter only documents the endpoints under the `/users`
|
|
123
|
+
path provided by the `users_api` endpoint group.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
The doc builder class/format to use
|
|
128
|
+
"""
|
|
129
|
+
schema_format = configs.Any(default=autodoc.formats.oai3_json.Oai3Json)
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
Addiional data to inject into the schema doc.
|
|
133
|
+
|
|
134
|
+
This is typically used for setting info/server settings in the resultant swagger doc. Anything
|
|
135
|
+
in this dictionary is injected into the "root" of the generated documentation file.
|
|
136
|
+
"""
|
|
137
|
+
schema_configuration = configs.AnyDict(default={})
|
|
138
|
+
|
|
139
|
+
@decorators.parameters_to_properties
|
|
140
|
+
def __init__(
|
|
141
|
+
self,
|
|
142
|
+
url: str,
|
|
143
|
+
schema_format=autodoc.formats.oai3_json.Oai3Json,
|
|
144
|
+
request_methods: list[str] = ["GET"],
|
|
145
|
+
response_headers: list[str | Callable[..., list[str]]] = [],
|
|
146
|
+
security_headers: list[SecurityHeader] = [],
|
|
147
|
+
schema_configuration: dict[str, Any] = {},
|
|
148
|
+
authentication: Authentication = authentication.Public(),
|
|
149
|
+
):
|
|
150
|
+
# we need to call the parent but don't have to pass along any of our kwargs. They are all optional in our parent, and our parent class
|
|
151
|
+
# just stores them in parameters, which we have already done. However, the parent does do some extra initialization stuff that we need,
|
|
152
|
+
# which is why we have to call the parent.
|
|
153
|
+
super().__init__()
|
|
154
|
+
|
|
155
|
+
def handle(self, input_output: InputOutput) -> Any:
|
|
156
|
+
current_endpoint_groups = self.di.build_from_name("endpoint_groups", cache=True)
|
|
157
|
+
if not current_endpoint_groups:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
f"{self.__class__.__name__} endpoint was attached directly to the context, but it must be attached to an endpoint group (otherwise it has no application to document)."
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# the endpoint group at the end of the list is the one that invoked us. Let's grab it
|
|
163
|
+
# if we don't hvae any endpoint groups then we've been attached directly to a context,
|
|
164
|
+
# which is pointless - there's nothing for us to document. So, treat it as an error.
|
|
165
|
+
endpoint_group = current_endpoint_groups[-1]
|
|
166
|
+
requests: list[Any] = []
|
|
167
|
+
models: dict[str, Any] = {}
|
|
168
|
+
security_schemes: dict[str, Any] = {}
|
|
169
|
+
for endpoint in endpoint_group.all_endpoints():
|
|
170
|
+
requests.extend(endpoint.documentation())
|
|
171
|
+
models = {**models, **endpoint.documentation_models()}
|
|
172
|
+
# if "user" in models:
|
|
173
|
+
# print(models["user"].children)
|
|
174
|
+
# print(endpoint.__class__.__name__)
|
|
175
|
+
security_schemes = {**security_schemes, **endpoint.documentation_security_schemes()}
|
|
176
|
+
# print(models["user"].children)
|
|
177
|
+
|
|
178
|
+
schema = self.di.build(self.schema_format)
|
|
179
|
+
schema.set_requests(requests)
|
|
180
|
+
schema.set_components({"models": models, "securitySchemes": security_schemes})
|
|
181
|
+
extra_schema_config = {**self.schema_configuration}
|
|
182
|
+
if "info" not in extra_schema_config:
|
|
183
|
+
extra_schema_config["info"] = {"title": "Auto generated by clearskies", "version": "1.0"}
|
|
184
|
+
self.add_response_headers(input_output)
|
|
185
|
+
return input_output.respond(schema.pretty(root_properties=extra_schema_config), 200)
|