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
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import urllib.parse
|
|
2
|
-
from .routing import Routing
|
|
3
|
-
from .create import Create
|
|
4
|
-
from .update import Update
|
|
5
|
-
from .delete import Delete
|
|
6
|
-
from .get import Get
|
|
7
|
-
from .list import List
|
|
8
|
-
from .advanced_search import AdvancedSearch
|
|
9
|
-
from .. import autodoc
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class InvalidUrl(Exception):
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class RestfulAPI(Routing):
|
|
17
|
-
_cached_handlers = None
|
|
18
|
-
|
|
19
|
-
_configuration_defaults = {
|
|
20
|
-
"base_url": "",
|
|
21
|
-
"allow_create": True,
|
|
22
|
-
"allow_delete": True,
|
|
23
|
-
"allow_get": True,
|
|
24
|
-
"allow_list": True,
|
|
25
|
-
"allow_search": True,
|
|
26
|
-
"allow_update": True,
|
|
27
|
-
"create_handler": Create,
|
|
28
|
-
"delete_handler": Delete,
|
|
29
|
-
"get_handler": Get,
|
|
30
|
-
"list_handler": List,
|
|
31
|
-
"search_handler": AdvancedSearch,
|
|
32
|
-
"update_handler": Update,
|
|
33
|
-
"read_only": False,
|
|
34
|
-
"create_request_method": "POST",
|
|
35
|
-
"delete_request_method": "DELETE",
|
|
36
|
-
"get_request_method": "GET",
|
|
37
|
-
"list_request_method": "GET",
|
|
38
|
-
"search_request_method": ["GET", "POST"],
|
|
39
|
-
"update_request_method": "PUT",
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
_resource_id = None
|
|
43
|
-
_is_search = False
|
|
44
|
-
|
|
45
|
-
def __init__(self, di):
|
|
46
|
-
super().__init__(di)
|
|
47
|
-
self._cached_handlers = {}
|
|
48
|
-
|
|
49
|
-
def handler_classes(self, configuration):
|
|
50
|
-
classes = []
|
|
51
|
-
for action in ["create", "delete", "get", "list", "search", "update"]:
|
|
52
|
-
allow_key = f"allow_{action}"
|
|
53
|
-
handler_key = f"{action}_handler"
|
|
54
|
-
if allow_key in configuration and not configuration[allow_key]:
|
|
55
|
-
continue
|
|
56
|
-
classes.append(
|
|
57
|
-
configuration[handler_key]
|
|
58
|
-
if handler_key in configuration
|
|
59
|
-
else self._configuration_defaults[handler_key]
|
|
60
|
-
)
|
|
61
|
-
return classes
|
|
62
|
-
|
|
63
|
-
def configure(self, configuration):
|
|
64
|
-
# if we have read only set then we can't allow any write methods
|
|
65
|
-
if configuration.get("read_only"):
|
|
66
|
-
for action in ["update", "delete", "create"]:
|
|
67
|
-
if configuration.get(f"allow_{action}"):
|
|
68
|
-
raise ValueError(
|
|
69
|
-
f"Contradictory configuration for handler '{self.__class__.__name__}': "
|
|
70
|
-
+ f"'read_only' and 'allow_{action} are both set to True"
|
|
71
|
-
)
|
|
72
|
-
configuration[f"allow_{action}"] = False
|
|
73
|
-
|
|
74
|
-
super().configure(configuration)
|
|
75
|
-
|
|
76
|
-
def _finalize_configuration(self, configuration):
|
|
77
|
-
search_request_method = configuration.get("search_request_method")
|
|
78
|
-
if type(search_request_method) == str:
|
|
79
|
-
configuration["search_request_method"] = [search_request_method]
|
|
80
|
-
configuration["search_request_method"] = [method.upper() for method in configuration["search_request_method"]]
|
|
81
|
-
|
|
82
|
-
request_method_keys = [
|
|
83
|
-
"create_request_method",
|
|
84
|
-
"delete_request_method",
|
|
85
|
-
"get_request_method",
|
|
86
|
-
"list_request_method",
|
|
87
|
-
"update_request_method",
|
|
88
|
-
]
|
|
89
|
-
for key in request_method_keys:
|
|
90
|
-
configuration[key] = configuration[key].upper()
|
|
91
|
-
|
|
92
|
-
return super()._finalize_configuration(configuration)
|
|
93
|
-
|
|
94
|
-
def handle(self, input_output):
|
|
95
|
-
[resource_id, handler_class] = self._get_handler_class_for_route(input_output)
|
|
96
|
-
if handler_class is None:
|
|
97
|
-
return self.error(input_output, "Not Found", 404)
|
|
98
|
-
handler = self.fetch_cached_handler(handler_class)
|
|
99
|
-
if resource_id is not None:
|
|
100
|
-
input_output.add_routing_data({"id": resource_id})
|
|
101
|
-
return handler(input_output)
|
|
102
|
-
|
|
103
|
-
def cors(self, input_output):
|
|
104
|
-
cors = self._cors_header
|
|
105
|
-
if not cors:
|
|
106
|
-
return self.error(input_output, "not found", 404)
|
|
107
|
-
authentication = self._configuration.get("authentication")
|
|
108
|
-
if authentication:
|
|
109
|
-
authentication.set_headers_for_cors(cors)
|
|
110
|
-
methods = {}
|
|
111
|
-
for action in ["create", "delete", "list", "search", "update"]:
|
|
112
|
-
if self.configuration(f"allow_{action}"):
|
|
113
|
-
route_methods = self.configuration(f"{action}_request_method")
|
|
114
|
-
if type(route_methods) != list:
|
|
115
|
-
route_methods = [route_methods]
|
|
116
|
-
for route_method in route_methods:
|
|
117
|
-
methods[route_method] = True
|
|
118
|
-
for method in methods.keys():
|
|
119
|
-
cors.add_method(method)
|
|
120
|
-
cors.set_headers_for_input_output(input_output)
|
|
121
|
-
return input_output.respond("", 200)
|
|
122
|
-
|
|
123
|
-
def fetch_cached_handler(self, handler_class):
|
|
124
|
-
cache_key = handler_class.__name__
|
|
125
|
-
if cache_key not in self._cached_handlers:
|
|
126
|
-
self._cached_handlers[cache_key] = self.build_handler(handler_class)
|
|
127
|
-
return self._cached_handlers[cache_key]
|
|
128
|
-
|
|
129
|
-
def build_handler(self, handler_class, configuration=None):
|
|
130
|
-
if not configuration and handler_class == self.configuration("update_handler"):
|
|
131
|
-
configuration = self._configuration
|
|
132
|
-
configuration["include_id_in_path"] = True
|
|
133
|
-
return super().build_handler(handler_class, configuration=configuration)
|
|
134
|
-
|
|
135
|
-
def _get_handler_class_for_route(self, input_output):
|
|
136
|
-
try:
|
|
137
|
-
[is_search, resource_id] = self._parse_url(input_output)
|
|
138
|
-
except InvalidUrl:
|
|
139
|
-
return [None, None]
|
|
140
|
-
request_method = input_output.get_request_method().upper()
|
|
141
|
-
if is_search:
|
|
142
|
-
if request_method not in self.configuration("search_request_method"):
|
|
143
|
-
return [None, None]
|
|
144
|
-
return [resource_id, self.configuration("search_handler") if self.configuration("allow_search") else None]
|
|
145
|
-
if resource_id:
|
|
146
|
-
if request_method == self.configuration("update_request_method"):
|
|
147
|
-
return [
|
|
148
|
-
resource_id,
|
|
149
|
-
self.configuration("update_handler") if self.configuration("allow_update") else None,
|
|
150
|
-
]
|
|
151
|
-
elif request_method == self.configuration("delete_request_method"):
|
|
152
|
-
return [
|
|
153
|
-
resource_id,
|
|
154
|
-
self.configuration("delete_handler") if self.configuration("allow_delete") else None,
|
|
155
|
-
]
|
|
156
|
-
if request_method != self.configuration("get_request_method"):
|
|
157
|
-
return [None, None]
|
|
158
|
-
return [resource_id, self.configuration("get_handler") if self.configuration("allow_get") else None]
|
|
159
|
-
if request_method == self.configuration("create_request_method"):
|
|
160
|
-
return [resource_id, self.configuration("create_handler") if self.configuration("allow_create") else None]
|
|
161
|
-
if request_method == self.configuration("list_request_method"):
|
|
162
|
-
return [resource_id, self.configuration("list_handler") if self.configuration("allow_list") else None]
|
|
163
|
-
return [None, None]
|
|
164
|
-
|
|
165
|
-
def _parse_url(self, input_output):
|
|
166
|
-
resource_id = None
|
|
167
|
-
is_search = False
|
|
168
|
-
full_path = input_output.get_full_path().strip("/")
|
|
169
|
-
base_url = self.configuration("base_url").strip("/")
|
|
170
|
-
for key, value in input_output.routing_data().items():
|
|
171
|
-
base_url = base_url.replace("{" + key + "}", value)
|
|
172
|
-
if base_url and full_path[: len(base_url)] != base_url:
|
|
173
|
-
raise InvalidUrl()
|
|
174
|
-
url = full_path[len(base_url) :].strip("/")
|
|
175
|
-
if url:
|
|
176
|
-
if url == "search" and self.configuration("allow_search"):
|
|
177
|
-
is_search = True
|
|
178
|
-
else:
|
|
179
|
-
resource_id = urllib.parse.unquote(url)
|
|
180
|
-
return [is_search, resource_id]
|
|
181
|
-
|
|
182
|
-
def documentation(self):
|
|
183
|
-
docs = []
|
|
184
|
-
for name in ["list", "get", "search", "create", "update", "delete"]:
|
|
185
|
-
if not self.configuration(f"allow_{name}"):
|
|
186
|
-
continue
|
|
187
|
-
handler = self.build_handler(self.configuration(f"{name}_handler"))
|
|
188
|
-
action_docs = handler.documentation()
|
|
189
|
-
for doc in action_docs:
|
|
190
|
-
request_methods = self.configuration(f"{name}_request_method")
|
|
191
|
-
|
|
192
|
-
# for search, filter down the docs to only the allowed request methods.
|
|
193
|
-
# for the rest, set the request method to match the allowed one.
|
|
194
|
-
# this helps with the fact that the searching has different input methods depending
|
|
195
|
-
# on the request method, and helps with the fact that search has different rules
|
|
196
|
-
# for different methods.
|
|
197
|
-
if name == "search":
|
|
198
|
-
if not set(request_methods).intersection(set(doc.request_methods)):
|
|
199
|
-
continue
|
|
200
|
-
else:
|
|
201
|
-
doc.set_request_methods(request_methods if type(request_methods) == list else [request_methods])
|
|
202
|
-
|
|
203
|
-
if name == "search":
|
|
204
|
-
doc.append_relative_path("search")
|
|
205
|
-
|
|
206
|
-
docs.append(doc)
|
|
207
|
-
|
|
208
|
-
return docs
|
|
209
|
-
|
|
210
|
-
def documentation_models(self):
|
|
211
|
-
# read and write use the same model, so we just need one
|
|
212
|
-
read_handler = self.build_handler(self.configuration("get_handler"))
|
|
213
|
-
return read_handler.documentation_models()
|
|
214
|
-
|
|
215
|
-
def documentation_security_schemes(self):
|
|
216
|
-
# read and write use the same model, so we just need one
|
|
217
|
-
read_handler = self.build_handler(self.configuration("get_handler"))
|
|
218
|
-
return read_handler.documentation_security_schemes()
|
clearskies/handlers/routing.py
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
from .base import Base
|
|
2
|
-
from abc import abstractmethod
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Routing(Base):
|
|
6
|
-
def __init__(self, di):
|
|
7
|
-
super().__init__(di)
|
|
8
|
-
|
|
9
|
-
@abstractmethod
|
|
10
|
-
def handler_classes(self, configuration):
|
|
11
|
-
pass
|
|
12
|
-
|
|
13
|
-
@abstractmethod
|
|
14
|
-
def handle(self, input_output):
|
|
15
|
-
pass
|
|
16
|
-
|
|
17
|
-
def build_handler(self, handler_class, configuration=None):
|
|
18
|
-
if configuration is None:
|
|
19
|
-
configuration = self._configuration
|
|
20
|
-
handler = self._di.build(handler_class, cache=False)
|
|
21
|
-
handler_configuration = {}
|
|
22
|
-
for key in handler._configuration_defaults.keys():
|
|
23
|
-
if key in configuration:
|
|
24
|
-
handler_configuration[key] = configuration[key]
|
|
25
|
-
for key in handler._global_configuration_defaults.keys():
|
|
26
|
-
if key in configuration:
|
|
27
|
-
handler_configuration[key] = configuration[key]
|
|
28
|
-
handler.configure(self._finalize_configuration_for_sub_handler(handler_configuration, handler_class))
|
|
29
|
-
return handler
|
|
30
|
-
|
|
31
|
-
def _finalize_configuration_for_sub_handler(self, configuration, handler_class):
|
|
32
|
-
return configuration
|
|
33
|
-
|
|
34
|
-
def configure(self, configuration):
|
|
35
|
-
# we need to completely clobber the base configuration process because it expects to have
|
|
36
|
-
# the list of all allowed configurations. We don't know what that list is - rather, we
|
|
37
|
-
# just need to fulfill the requirements of the handlers we'll be routing to.
|
|
38
|
-
# We also want to make it possible for handlers that extend this to still define their
|
|
39
|
-
# own possible configuration values. Therefore, we'll loop over all of the handlers
|
|
40
|
-
# which we might route to, make them, have them check the configs, and let them throw exceptions
|
|
41
|
-
# as needed. Finally we'll figure out what configs may not have been "used" by a child handler
|
|
42
|
-
# and see if those are in our own configuration - if not, we'll throw an "Unknown config" exception
|
|
43
|
-
|
|
44
|
-
# First, let's check the configuration for the handlers, which is just a matter of building
|
|
45
|
-
# the handlers (they willl automatically throw exceptions for invalid configurations as part
|
|
46
|
-
# of this process)
|
|
47
|
-
used_configs = list(self._global_configuration_defaults.keys())
|
|
48
|
-
used_configs.extend(self._configuration_defaults.keys())
|
|
49
|
-
for handler_class in self.handler_classes(configuration):
|
|
50
|
-
handler = self.build_handler(handler_class, configuration=configuration)
|
|
51
|
-
used_configs.extend(handler._configuration_defaults.keys())
|
|
52
|
-
|
|
53
|
-
for key in configuration.keys():
|
|
54
|
-
if key not in used_configs and key not in self._global_configuration_defaults:
|
|
55
|
-
class_name = self.__class__.__name__
|
|
56
|
-
raise KeyError(f"Attempt to set unknown configuration setting '{key}' for handler '{class_name}'")
|
|
57
|
-
|
|
58
|
-
self._check_configuration(configuration)
|
|
59
|
-
self._configuration = self._finalize_configuration(self.apply_default_configuration(configuration))
|
|
60
|
-
|
|
61
|
-
def _check_configuration(self, configuration):
|
|
62
|
-
super()._check_configuration(configuration)
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
from collections import OrderedDict
|
|
2
|
-
from abc import ABC
|
|
3
|
-
from ..functional import validations
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class FakeModel:
|
|
7
|
-
def __getattr__(self, key):
|
|
8
|
-
return None
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class SchemaHelper(ABC):
|
|
12
|
-
"""
|
|
13
|
-
A helper for handlers that want to accept arbitrary schemas as configuration inputs.
|
|
14
|
-
|
|
15
|
-
To use this, the schema should be stored in a configuration called `schema`. You can then check
|
|
16
|
-
some data against the schema by doing something like this:
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
input_errors = {
|
|
20
|
-
**self._extra_column_errors(request_data),
|
|
21
|
-
**self._find_input_errors(request_data),
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def _check_schema(self, schema, writeable_columns, error_prefix):
|
|
27
|
-
"""
|
|
28
|
-
Validates that the schema provided in the configuration is valid.
|
|
29
|
-
|
|
30
|
-
The schema is allowed to be one of 3 things:
|
|
31
|
-
|
|
32
|
-
1. A list of column definitions.
|
|
33
|
-
2. A model class.
|
|
34
|
-
3. A model.
|
|
35
|
-
|
|
36
|
-
An example of option #1 would be:
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
{
|
|
40
|
-
'schema': [
|
|
41
|
-
clearskies.column_types.string('name', input_requirements=[clearskies.input_requirements.required()]),
|
|
42
|
-
clearskies.column_types.integer('age'),
|
|
43
|
-
],
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
"""
|
|
47
|
-
is_valid_schema = False
|
|
48
|
-
if validations.is_model_or_class(schema):
|
|
49
|
-
is_valid_schema = True
|
|
50
|
-
else:
|
|
51
|
-
if not hasattr(schema, "__iter__") or type(schema) == str:
|
|
52
|
-
raise ValueError(
|
|
53
|
-
f"{error_prefix} 'schema' should be a list of column definitions, but was instead a " + type(schema)
|
|
54
|
-
)
|
|
55
|
-
for column in schema:
|
|
56
|
-
if type(column) != tuple:
|
|
57
|
-
raise ValueError(
|
|
58
|
-
f"{error_prefix} 'schema' should be a list of column definitions, but one of the entries was not a column definition"
|
|
59
|
-
)
|
|
60
|
-
is_valid_schema = True
|
|
61
|
-
if not is_valid_schema:
|
|
62
|
-
raise ValueError(
|
|
63
|
-
f"{error_prefix} 'schema' should be a model, model class, or list of column definitions, but was instead a "
|
|
64
|
-
+ type(schema)
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
if not writeable_columns and writeable_columns is not None:
|
|
68
|
-
raise ValueError(
|
|
69
|
-
f"{error_prefix} 'writeable_columns' can't be an empty list. It can be 'None', but otherwise I don't know how to handle empty values"
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
if writeable_columns:
|
|
73
|
-
if not hasattr(writeable_columns, "__iter__") or type(writeable_columns) == str:
|
|
74
|
-
raise ValueError(
|
|
75
|
-
f"{error_prefix} 'writeable_columns' should be a list of column names, but was instead a "
|
|
76
|
-
+ type(writeable_columns)
|
|
77
|
-
)
|
|
78
|
-
columns = self._schema_to_columns(schema)
|
|
79
|
-
for column in writeable_columns:
|
|
80
|
-
if type(column) != str:
|
|
81
|
-
raise ValueError(
|
|
82
|
-
f"{error_prefix} 'writeable_columns' should be a list of column names, but one of the entries was not a string"
|
|
83
|
-
)
|
|
84
|
-
if column not in columns:
|
|
85
|
-
raise ValueError(
|
|
86
|
-
f"{error_prefix} 'writeable_columns' references a column named '{column}' but this column does not exist in the schema"
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
def _schema_to_columns(self, schema, columns_to_keep=None):
|
|
90
|
-
"""
|
|
91
|
-
Converts the schema from the developer to a columns object
|
|
92
|
-
"""
|
|
93
|
-
# the schema can be a model, model class, or list of column configs.
|
|
94
|
-
# Each requires a different conversion method
|
|
95
|
-
if validations.is_model(schema):
|
|
96
|
-
columns = schema.columns()
|
|
97
|
-
elif validations.is_model_class(schema):
|
|
98
|
-
columns = self._di.build(schema).columns()
|
|
99
|
-
else:
|
|
100
|
-
columns = self._di.build("columns").configure(OrderedDict(schema), self.__class__)
|
|
101
|
-
|
|
102
|
-
# if we don't have a list of columns to keep, then we're done
|
|
103
|
-
if not columns_to_keep:
|
|
104
|
-
return columns
|
|
105
|
-
|
|
106
|
-
# only keep things that we're allowed to keep
|
|
107
|
-
return OrderedDict([(key, value) for (key, value) in columns.items() if key in columns_to_keep])
|
|
108
|
-
|
|
109
|
-
def _find_input_errors(self, input_data, schema=None):
|
|
110
|
-
if not schema:
|
|
111
|
-
schema = self.configuration("schema")
|
|
112
|
-
input_errors = {}
|
|
113
|
-
fake_model = FakeModel()
|
|
114
|
-
for column in schema.values():
|
|
115
|
-
input_errors = {
|
|
116
|
-
**input_errors,
|
|
117
|
-
**column.input_errors(fake_model, input_data),
|
|
118
|
-
}
|
|
119
|
-
return input_errors
|
|
120
|
-
|
|
121
|
-
def _extra_column_errors(self, input_data, schema=None):
|
|
122
|
-
if not schema:
|
|
123
|
-
schema = self.configuration("schema")
|
|
124
|
-
input_errors = {}
|
|
125
|
-
for column_name in input_data.keys():
|
|
126
|
-
if column_name not in schema:
|
|
127
|
-
input_errors[column_name] = f"Input column '{column_name}' is not an allowed column"
|
|
128
|
-
return input_errors
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
from .base import Base
|
|
2
|
-
from abc import abstractmethod
|
|
3
|
-
from .simple_routing_route import SimpleRoutingRoute
|
|
4
|
-
from . import callable as callable_handler
|
|
5
|
-
from ..functional import string
|
|
6
|
-
from .. import autodoc
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class SimpleRouting(Base):
|
|
10
|
-
_routes = None
|
|
11
|
-
|
|
12
|
-
_configuration_defaults = {
|
|
13
|
-
"base_url": "",
|
|
14
|
-
"authentication": None,
|
|
15
|
-
"routes": [],
|
|
16
|
-
"schema_route": "",
|
|
17
|
-
"schema_configuration": {},
|
|
18
|
-
"schema_format": autodoc.formats.oai3_json.OAI3JSON,
|
|
19
|
-
"schema_authentication": None,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
def __init__(self, di):
|
|
23
|
-
super().__init__(di)
|
|
24
|
-
|
|
25
|
-
def top_level_authentication_and_authorization(self, input_output, authentication=None):
|
|
26
|
-
# Check for separate authentication on the schema route
|
|
27
|
-
schema_authentication = self.configuration("schema_authentication")
|
|
28
|
-
if schema_authentication:
|
|
29
|
-
request_method = input_output.get_request_method()
|
|
30
|
-
full_path = input_output.get_full_path().strip("/")
|
|
31
|
-
if self.configuration("schema_route") and self.configuration("schema_route") == full_path:
|
|
32
|
-
return super().top_level_authentication_and_authorization(input_output, schema_authentication)
|
|
33
|
-
return super().top_level_authentication_and_authorization(input_output)
|
|
34
|
-
|
|
35
|
-
def can_handle(self, full_path, request_method, is_cors=False):
|
|
36
|
-
for route in self._routes:
|
|
37
|
-
route_data = route.matches(full_path, request_method, is_cors=is_cors)
|
|
38
|
-
if route_data is not None:
|
|
39
|
-
return route_data
|
|
40
|
-
return None
|
|
41
|
-
|
|
42
|
-
def handle(self, input_output):
|
|
43
|
-
request_method = input_output.get_request_method()
|
|
44
|
-
full_path = input_output.get_full_path().strip("/")
|
|
45
|
-
if self.configuration("schema_route") and self.configuration("schema_route") == full_path:
|
|
46
|
-
return self.hosted_schema(input_output)
|
|
47
|
-
|
|
48
|
-
if request_method == "OPTIONS":
|
|
49
|
-
return self.cors(input_output)
|
|
50
|
-
|
|
51
|
-
for route in self._routes:
|
|
52
|
-
route_data = route.matches(full_path, request_method)
|
|
53
|
-
if route_data is None:
|
|
54
|
-
continue
|
|
55
|
-
input_output.add_routing_data(route_data)
|
|
56
|
-
|
|
57
|
-
return route(input_output)
|
|
58
|
-
|
|
59
|
-
return self.error(input_output, "Page not found", 404)
|
|
60
|
-
|
|
61
|
-
def cors(self, input_output):
|
|
62
|
-
if not self._cors_header:
|
|
63
|
-
return self.error(input_output, "not found", 404)
|
|
64
|
-
request_method = input_output.get_request_method()
|
|
65
|
-
full_path = input_output.get_full_path().strip("/")
|
|
66
|
-
for route in self._routes:
|
|
67
|
-
route_data = route.matches(full_path, request_method, is_cors=True)
|
|
68
|
-
if route_data is None:
|
|
69
|
-
continue
|
|
70
|
-
|
|
71
|
-
return route.cors(input_output)
|
|
72
|
-
return self.error(input_output, "Page not found", 404)
|
|
73
|
-
|
|
74
|
-
def _check_configuration(self, configuration):
|
|
75
|
-
super()._check_configuration(configuration)
|
|
76
|
-
|
|
77
|
-
if not configuration.get("routes"):
|
|
78
|
-
raise ValueError(f"'routes' must be a list of routes for the {self.__class__.__name__} handler")
|
|
79
|
-
if not hasattr(configuration["routes"], "__iter__"):
|
|
80
|
-
raise ValueError(
|
|
81
|
-
f"'routes' must be a list of routes for the {self.__class__.__name__} handler, "
|
|
82
|
-
+ "but a non-iterable was provided instead"
|
|
83
|
-
)
|
|
84
|
-
if isinstance(configuration["routes"], str):
|
|
85
|
-
raise ValueError(
|
|
86
|
-
f"'routes' must be a list of routes for the {self.__class__.__name__} handler, "
|
|
87
|
-
+ "but a string was provided instead"
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
# we're actually going to build our routes, which will implicitly check the configuration too
|
|
91
|
-
base_url = configuration.get("base_url")
|
|
92
|
-
self._build_routes(
|
|
93
|
-
configuration["routes"],
|
|
94
|
-
base_url if base_url else "/",
|
|
95
|
-
authentication=configuration.get("authentication"),
|
|
96
|
-
response_headers=configuration.get("response_headers"),
|
|
97
|
-
security_headers=configuration.get("security_headers"),
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
def _finalize_configuration(self, configuration):
|
|
101
|
-
configuration = super()._finalize_configuration(configuration)
|
|
102
|
-
if configuration.get("schema_route"):
|
|
103
|
-
base_url = configuration.get("base_url")
|
|
104
|
-
configuration["schema_route"] = (
|
|
105
|
-
base_url.strip("/") + "/" + configuration["schema_route"].strip("/")
|
|
106
|
-
).strip("/")
|
|
107
|
-
if configuration.get("schema_authentication") is not None:
|
|
108
|
-
configuration["schema_authentication"] = self._di.build(configuration["schema_authentication"])
|
|
109
|
-
return configuration
|
|
110
|
-
|
|
111
|
-
def _build_routes(self, routes, base_url, authentication=None, response_headers=None, security_headers=None):
|
|
112
|
-
self._routes = []
|
|
113
|
-
if base_url is None:
|
|
114
|
-
base_url = ""
|
|
115
|
-
for i, route_config in enumerate(routes):
|
|
116
|
-
# in general the route should be a dictionary with the route configuration,
|
|
117
|
-
# but there are two exceptions. The first is a "plain" callable. In that case,
|
|
118
|
-
# wrap it in a callable handler and define the path from the name
|
|
119
|
-
if type(route_config) != dict:
|
|
120
|
-
if callable(route_config):
|
|
121
|
-
route_config = {
|
|
122
|
-
"path": route_config.__name__,
|
|
123
|
-
"handler_class": callable_handler.Callable,
|
|
124
|
-
"handler_config": {"callable": route_config},
|
|
125
|
-
}
|
|
126
|
-
# the other option is an application with another simple routing handler, in which
|
|
127
|
-
# case just skip the path (which is optional anyway)
|
|
128
|
-
elif (
|
|
129
|
-
hasattr(route_config, "handler_class")
|
|
130
|
-
and hasattr(route_config, "handler_config")
|
|
131
|
-
and issubclass(route_config.handler_class, SimpleRouting)
|
|
132
|
-
):
|
|
133
|
-
route_config = {
|
|
134
|
-
"path": "",
|
|
135
|
-
"application": route_config,
|
|
136
|
-
}
|
|
137
|
-
if type(route_config) != dict:
|
|
138
|
-
raise ValueError(
|
|
139
|
-
f"Routing config expected a dictionary with route information but found something else for route #{i+1}"
|
|
140
|
-
)
|
|
141
|
-
path = route_config.get("path")
|
|
142
|
-
if path is None:
|
|
143
|
-
path = ""
|
|
144
|
-
if route_config.get("application"):
|
|
145
|
-
application = route_config.get("application")
|
|
146
|
-
if not hasattr(application, "handler_config") or not hasattr(application, "handler_class"):
|
|
147
|
-
raise ValueError(f"A non application was passed in the 'application' key of route #{i+1}")
|
|
148
|
-
route_config["handler_class"] = application.handler_class
|
|
149
|
-
route_config["handler_config"] = application.handler_config
|
|
150
|
-
if not route_config.get("handler_class"):
|
|
151
|
-
raise ValueError(
|
|
152
|
-
"Each route must specify a handler class via 'handler_class' key, "
|
|
153
|
-
+ f"but 'handler_class' was missing for route #{i+1}"
|
|
154
|
-
)
|
|
155
|
-
if route_config.get("handler_config") is None:
|
|
156
|
-
raise ValueError(
|
|
157
|
-
"Each route must specify the handler configuration via 'handler_config' key, "
|
|
158
|
-
+ f"but 'handler_config' was missing for route #{i+1}"
|
|
159
|
-
)
|
|
160
|
-
route = SimpleRoutingRoute(self._di)
|
|
161
|
-
route.configure(
|
|
162
|
-
route_config["handler_class"],
|
|
163
|
-
route_config["handler_config"],
|
|
164
|
-
path=base_url.rstrip("/") + "/" + path.lstrip("/"),
|
|
165
|
-
methods=route_config.get("methods"),
|
|
166
|
-
path_parameter_with_slashes=route_config.get("path_parameter_with_slashes"),
|
|
167
|
-
authentication=authentication,
|
|
168
|
-
response_headers=response_headers,
|
|
169
|
-
security_headers=security_headers,
|
|
170
|
-
has_sub_paths=route_config.get("has_sub_paths", True),
|
|
171
|
-
)
|
|
172
|
-
self._routes.append(route)
|
|
173
|
-
|
|
174
|
-
def documentation(self):
|
|
175
|
-
docs = []
|
|
176
|
-
for route in self._routes:
|
|
177
|
-
docs.extend(route.documentation())
|
|
178
|
-
return docs
|
|
179
|
-
|
|
180
|
-
def documentation_security_schemes(self):
|
|
181
|
-
schemes = {}
|
|
182
|
-
for route in self._routes:
|
|
183
|
-
schemes = {**schemes, **route.documentation_security_schemes()}
|
|
184
|
-
return schemes
|
|
185
|
-
|
|
186
|
-
def documentation_models(self):
|
|
187
|
-
models = {}
|
|
188
|
-
for route in self._routes:
|
|
189
|
-
models = {**models, **route.documentation_models()}
|
|
190
|
-
return models
|
|
191
|
-
|
|
192
|
-
def hosted_schema(self, input_output):
|
|
193
|
-
schema = self._di.build(self.configuration("schema_format"))
|
|
194
|
-
schema.set_requests(self.documentation())
|
|
195
|
-
schema.set_components(self.documentation_components())
|
|
196
|
-
extra_schema_config = self.configuration("schema_configuration")
|
|
197
|
-
if "info" not in extra_schema_config:
|
|
198
|
-
extra_schema_config["info"] = {"title": "Auto generated by clearskies", "version": "1.0"}
|
|
199
|
-
response_headers = self.configuration("response_headers")
|
|
200
|
-
if response_headers:
|
|
201
|
-
input_output.set_headers(response_headers)
|
|
202
|
-
for security_header in self.configuration("security_headers"):
|
|
203
|
-
security_header.set_headers_for_input_output(input_output)
|
|
204
|
-
return input_output.respond(schema.pretty(root_properties=extra_schema_config), 200)
|