clear-skies 1.22.10__py3-none-any.whl → 2.0.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- clear_skies-2.0.23.dist-info/METADATA +76 -0
- clear_skies-2.0.23.dist-info/RECORD +265 -0
- {clear_skies-1.22.10.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 +8 -39
- clearskies/authentication/authentication.py +44 -0
- clearskies/authentication/authorization.py +14 -8
- clearskies/authentication/authorization_pass_through.py +14 -10
- 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 +55 -20
- clearskies/backends/api_backend.py +1118 -280
- 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 +115 -4
- clearskies/di/additional_config_auto_import.py +12 -0
- clearskies/di/di.py +714 -125
- 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 -160
- 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 +1874 -193
- 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.22.10.dist-info/METADATA +0 -47
- clear_skies-1.22.10.dist-info/RECORD +0 -213
- 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 -51
- clearskies/backends/api_get_only_backend.py +0 -48
- 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 -103
- clearskies/binding_config.py +0 -16
- clearskies/column_types/__init__.py +0 -203
- clearskies/column_types/audit.py +0 -249
- clearskies/column_types/belongs_to.py +0 -271
- clearskies/column_types/boolean.py +0 -60
- clearskies/column_types/category_tree.py +0 -304
- 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 -109
- clearskies/column_types/datetime_micro.py +0 -13
- clearskies/column_types/email.py +0 -18
- clearskies/column_types/float.py +0 -43
- clearskies/column_types/has_many.py +0 -179
- clearskies/column_types/has_one.py +0 -58
- 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/phone.py +0 -48
- clearskies/column_types/select.py +0 -11
- clearskies/column_types/string.py +0 -24
- clearskies/column_types/timestamp.py +0 -73
- clearskies/column_types/updated.py +0 -26
- clearskies/column_types/updated_micro.py +0 -26
- 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 -151
- 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 -479
- clearskies/handlers/callable.py +0 -191
- 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 -206
- 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 -78
- 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/maximum_value.py +0 -19
- clearskies/input_requirements/minimum_length.py +0 -22
- clearskies/input_requirements/minimum_value.py +0 -19
- 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 -350
- 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.22.10.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,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from clearskies import decorators
|
|
6
|
+
from clearskies.columns.has_many import HasMany
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from clearskies import typing
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HasManySelf(HasMany):
|
|
13
|
+
"""
|
|
14
|
+
This is just like the HasMany column, but is used when the model references itself.
|
|
15
|
+
|
|
16
|
+
This exists because a model can't refer to itself inside it's own class definition. There are
|
|
17
|
+
workarounds, but having this class is usually quicker for the developer.
|
|
18
|
+
|
|
19
|
+
The main difference between this and HasMany is that you don't have to provide the child class.
|
|
20
|
+
Also, the name of the column that contains the id of the parent becomes `parent_id` by default,
|
|
21
|
+
rather than basing it on the name of the model. This is done because, since the model is also
|
|
22
|
+
the child, using the name of the model in the name of the column id is often ambiguous.
|
|
23
|
+
|
|
24
|
+
See also BelongsToSelf.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
_descriptor_config_map = None
|
|
28
|
+
|
|
29
|
+
@decorators.parameters_to_properties
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
foreign_column_name: str | None = None,
|
|
33
|
+
readable_child_columns: list[str] = [],
|
|
34
|
+
where: typing.condition | list[typing.condition] = [],
|
|
35
|
+
is_readable: bool = True,
|
|
36
|
+
on_change_pre_save: typing.action | list[typing.action] = [],
|
|
37
|
+
on_change_post_save: typing.action | list[typing.action] = [],
|
|
38
|
+
on_change_save_finished: typing.action | list[typing.action] = [],
|
|
39
|
+
):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def finalize_configuration(self, model_class, name) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Finalize and check the configuration.
|
|
45
|
+
|
|
46
|
+
This is an external trigger called by the model class when the model class is ready.
|
|
47
|
+
The reason it exists here instead of in the constructor is because some columns are tightly
|
|
48
|
+
connected to the model class, and can't validate configuration until they know what the model is.
|
|
49
|
+
Therefore, we need the model involved, and the only way for a property to know what class it is
|
|
50
|
+
in is if the parent class checks in (which is what happens here).
|
|
51
|
+
"""
|
|
52
|
+
self.child_model_class = model_class
|
|
53
|
+
has_value = False
|
|
54
|
+
try:
|
|
55
|
+
has_value = bool(self.foreign_column_name)
|
|
56
|
+
except KeyError:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
if not has_value:
|
|
60
|
+
self.foreign_column_name = "parent_id"
|
|
61
|
+
|
|
62
|
+
super().finalize_configuration(model_class, name)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from clearskies.columns.has_many import HasMany
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HasOne(HasMany):
|
|
7
|
+
"""
|
|
8
|
+
This operates exactly like the HasMany relationship, except it assumes there is only ever one child.
|
|
9
|
+
|
|
10
|
+
The only real difference between this and HasMany is that the HasMany column type will return a list
|
|
11
|
+
of models, while this returns the first model.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
_descriptor_config_map = None
|
|
15
|
+
|
|
16
|
+
def __get__(self, model, cls):
|
|
17
|
+
if model is None:
|
|
18
|
+
self.model_class = cls
|
|
19
|
+
return self # type: ignore
|
|
20
|
+
|
|
21
|
+
return super().__get__(model, cls).first()
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Callable, Self, overload
|
|
4
|
+
|
|
5
|
+
from clearskies import configs, decorators
|
|
6
|
+
from clearskies.autodoc.schema import Integer as AutoDocInteger
|
|
7
|
+
from clearskies.column import Column
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from clearskies import Model, typing
|
|
11
|
+
from clearskies.autodoc.schema import Schema as AutoDocSchema
|
|
12
|
+
from clearskies.query import Condition
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Integer(Column):
|
|
16
|
+
"""
|
|
17
|
+
A column that stores integer data.
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import clearskies
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MyModel(clearskies.Model):
|
|
24
|
+
backend = clearskies.backends.MemoryBackend()
|
|
25
|
+
id_column_name = "id"
|
|
26
|
+
|
|
27
|
+
id = clearskies.columns.Uuid()
|
|
28
|
+
age = clearskies.columns.Integer()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
wsgi = clearskies.contexts.WsgiRef(
|
|
32
|
+
clearskies.endpoints.Create(
|
|
33
|
+
MyModel,
|
|
34
|
+
writeable_column_names=["age"],
|
|
35
|
+
readable_column_names=["id", "age"],
|
|
36
|
+
),
|
|
37
|
+
classes=[MyModel],
|
|
38
|
+
)
|
|
39
|
+
wsgi()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
And when invoked:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
$ curl 'http://localhost:8080' -d '{"age":20}' | jq
|
|
46
|
+
{
|
|
47
|
+
"status": "success",
|
|
48
|
+
"error": "",
|
|
49
|
+
"data": {
|
|
50
|
+
"id": "6ea74719-a65f-45ae-b6a3-641ce682ed25",
|
|
51
|
+
"age": 20
|
|
52
|
+
},
|
|
53
|
+
"pagination": {},
|
|
54
|
+
"input_errors": {}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
$ curl 'http://localhost:8080' -d '{"age":"asdf"}' | jq
|
|
58
|
+
{
|
|
59
|
+
"status": "input_errors",
|
|
60
|
+
"error": "",
|
|
61
|
+
"data": [],
|
|
62
|
+
"pagination": {},
|
|
63
|
+
"input_errors": {
|
|
64
|
+
"age": "value should be an integer"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
default = configs.Integer(default=None) # type: ignore
|
|
71
|
+
setable = configs.IntegerOrCallable(default=None) # type: ignore
|
|
72
|
+
_allowed_search_operators = ["<=>", "!=", "<=", ">=", ">", "<", "=", "in", "is not null", "is null"]
|
|
73
|
+
|
|
74
|
+
auto_doc_class: type[AutoDocSchema] = AutoDocInteger
|
|
75
|
+
|
|
76
|
+
_descriptor_config_map = None
|
|
77
|
+
|
|
78
|
+
@decorators.parameters_to_properties
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
default: int | None = None,
|
|
82
|
+
setable: int | Callable[..., int] | None = None,
|
|
83
|
+
is_readable: bool = True,
|
|
84
|
+
is_writeable: bool = True,
|
|
85
|
+
is_searchable: bool = True,
|
|
86
|
+
is_temporary: bool = False,
|
|
87
|
+
validators: typing.validator | list[typing.validator] = [],
|
|
88
|
+
on_change_pre_save: typing.action | list[typing.action] = [],
|
|
89
|
+
on_change_post_save: typing.action | list[typing.action] = [],
|
|
90
|
+
on_change_save_finished: typing.action | list[typing.action] = [],
|
|
91
|
+
created_by_source_type: str = "",
|
|
92
|
+
created_by_source_key: str = "",
|
|
93
|
+
created_by_source_strict: bool = True,
|
|
94
|
+
):
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
@overload
|
|
98
|
+
def __get__(self, instance: None, cls: type[Model]) -> Self:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
@overload
|
|
102
|
+
def __get__(self, instance: Model, cls: type[Model]) -> int:
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
def __get__(self, instance, cls):
|
|
106
|
+
if instance is None:
|
|
107
|
+
self.model_class = cls
|
|
108
|
+
return self
|
|
109
|
+
|
|
110
|
+
value = super().__get__(instance, cls)
|
|
111
|
+
return None if value is None else int(value)
|
|
112
|
+
|
|
113
|
+
def __set__(self, instance, value: int) -> None:
|
|
114
|
+
# this makes sure we're initialized
|
|
115
|
+
if "name" not in self._config: # type: ignore
|
|
116
|
+
instance.get_columns()
|
|
117
|
+
|
|
118
|
+
instance._next_data[self.name] = value
|
|
119
|
+
|
|
120
|
+
def from_backend(self, value) -> int | None:
|
|
121
|
+
return None if value is None else int(value)
|
|
122
|
+
|
|
123
|
+
def to_backend(self, data):
|
|
124
|
+
if self.name not in data or data[self.name] is None:
|
|
125
|
+
return data
|
|
126
|
+
|
|
127
|
+
return {**data, self.name: int(data[self.name])}
|
|
128
|
+
|
|
129
|
+
def input_error_for_value(self, value, operator=None):
|
|
130
|
+
try:
|
|
131
|
+
int(value)
|
|
132
|
+
except ValueError:
|
|
133
|
+
return "value should be an integer"
|
|
134
|
+
return ""
|
|
135
|
+
|
|
136
|
+
def equals(self, value: int) -> Condition:
|
|
137
|
+
return super().equals(value)
|
|
138
|
+
|
|
139
|
+
def spaceship(self, value: int) -> Condition:
|
|
140
|
+
return super().spaceship(value)
|
|
141
|
+
|
|
142
|
+
def not_equals(self, value: int) -> Condition:
|
|
143
|
+
return super().not_equals(value)
|
|
144
|
+
|
|
145
|
+
def less_than_equals(self, value: int) -> Condition:
|
|
146
|
+
return super().less_than_equals(value)
|
|
147
|
+
|
|
148
|
+
def greater_than_equals(self, value: int) -> Condition:
|
|
149
|
+
return super().greater_than_equals(value)
|
|
150
|
+
|
|
151
|
+
def less_than(self, value: int) -> Condition:
|
|
152
|
+
return super().less_than(value)
|
|
153
|
+
|
|
154
|
+
def greater_than(self, value: int) -> Condition:
|
|
155
|
+
return super().greater_than(value)
|
|
156
|
+
|
|
157
|
+
def is_in(self, values: list[int]) -> Condition:
|
|
158
|
+
return super().is_in(values)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Self, overload
|
|
5
|
+
|
|
6
|
+
from clearskies import configs, decorators
|
|
7
|
+
from clearskies.column import Column
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from clearskies import Model, typing
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Json(Column):
|
|
14
|
+
"""
|
|
15
|
+
A column to store generic data.
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import clearskies
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MyModel(clearskies.Model):
|
|
22
|
+
backend = clearskies.backends.MemoryBackend()
|
|
23
|
+
id_column_name = "id"
|
|
24
|
+
|
|
25
|
+
id = clearskies.columns.Uuid()
|
|
26
|
+
my_data = clearskies.columns.Json()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
wsgi = clearskies.contexts.WsgiRef(
|
|
30
|
+
clearskies.endpoints.Create(
|
|
31
|
+
MyModel,
|
|
32
|
+
writeable_column_names=["my_data"],
|
|
33
|
+
readable_column_names=["id", "my_data"],
|
|
34
|
+
),
|
|
35
|
+
classes=[MyModel],
|
|
36
|
+
)
|
|
37
|
+
wsgi()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
And when invoked:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
$ curl 'http://localhost:8080' -d '{"my_data":{"count":[1,2,3,4,{"thing":true}]}}' | jq
|
|
44
|
+
{
|
|
45
|
+
"status": "success",
|
|
46
|
+
"error": "",
|
|
47
|
+
"data": {
|
|
48
|
+
"id": "63cbd5e7-a198-4424-bd35-3890075a2a5e",
|
|
49
|
+
"my_data": {
|
|
50
|
+
"count": [
|
|
51
|
+
1,
|
|
52
|
+
2,
|
|
53
|
+
3,
|
|
54
|
+
4,
|
|
55
|
+
{
|
|
56
|
+
"thing": true
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"pagination": {},
|
|
62
|
+
"input_errors": {}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Note that there is no attempt to check the shape of the input passed into a JSON column.
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
setable = configs.Any(default=None) # type: ignore
|
|
71
|
+
default = configs.Any(default=None) # type: ignore
|
|
72
|
+
is_searchable = configs.Boolean(default=False)
|
|
73
|
+
_descriptor_config_map = None
|
|
74
|
+
|
|
75
|
+
@decorators.parameters_to_properties
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
default: dict[str, Any] | list[Any] | None = None,
|
|
79
|
+
setable: dict[str, Any] | list[Any] | Callable[..., dict[str, Any]] | None = None,
|
|
80
|
+
is_readable: bool = True,
|
|
81
|
+
is_writeable: bool = True,
|
|
82
|
+
is_temporary: bool = False,
|
|
83
|
+
validators: typing.validator | list[typing.validator] = [],
|
|
84
|
+
on_change_pre_save: typing.action | list[typing.action] = [],
|
|
85
|
+
on_change_post_save: typing.action | list[typing.action] = [],
|
|
86
|
+
on_change_save_finished: typing.action | list[typing.action] = [],
|
|
87
|
+
created_by_source_type: str = "",
|
|
88
|
+
created_by_source_key: str = "",
|
|
89
|
+
created_by_source_strict: bool = True,
|
|
90
|
+
):
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
@overload
|
|
94
|
+
def __get__(self, instance: None, cls: type[Model]) -> Self:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
@overload
|
|
98
|
+
def __get__(self, instance: Model, cls: type[Model]) -> dict[str, Any]:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
def __get__(self, instance, cls):
|
|
102
|
+
return super().__get__(instance, cls)
|
|
103
|
+
|
|
104
|
+
def __set__(self, instance, value: dict[str, Any]) -> None:
|
|
105
|
+
# this makes sure we're initialized
|
|
106
|
+
if "name" not in self._config: # type: ignore
|
|
107
|
+
instance.get_columns()
|
|
108
|
+
|
|
109
|
+
instance._next_data[self.name] = value
|
|
110
|
+
|
|
111
|
+
def from_backend(self, value) -> dict[str, Any] | list[Any] | None:
|
|
112
|
+
if isinstance(value, (list, dict)):
|
|
113
|
+
return value
|
|
114
|
+
if not value:
|
|
115
|
+
return None
|
|
116
|
+
try:
|
|
117
|
+
return json.loads(value)
|
|
118
|
+
except json.JSONDecodeError:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
def to_backend(self, data):
|
|
122
|
+
if self.name not in data or data[self.name] is None:
|
|
123
|
+
return data
|
|
124
|
+
|
|
125
|
+
value = data[self.name]
|
|
126
|
+
return {**data, self.name: value if isinstance(value, str) else json.dumps(value)}
|