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,26 +0,0 @@
|
|
|
1
|
-
from .string import String
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class CreatedByAuthorizationData(String):
|
|
5
|
-
required_configs = [
|
|
6
|
-
"authorization_data_key_name",
|
|
7
|
-
]
|
|
8
|
-
|
|
9
|
-
def __init__(self, di):
|
|
10
|
-
super().__init__(di)
|
|
11
|
-
|
|
12
|
-
@property
|
|
13
|
-
def is_writeable(self):
|
|
14
|
-
return False
|
|
15
|
-
|
|
16
|
-
def pre_save(self, data, model):
|
|
17
|
-
if model.exists:
|
|
18
|
-
return data
|
|
19
|
-
|
|
20
|
-
authorization_data = self.di.build("input_output", cache=True).get_authorization_data()
|
|
21
|
-
# data comes last so that it can override the info in the authorization data. This seems counter-intuitive,
|
|
22
|
-
# but is important. You would think that you *don't* want the data from the authorization data to be
|
|
23
|
-
# overridden (since this is mainly used for logging), but the trouble is that there are a variety of use-cases
|
|
24
|
-
# where the application must provide the audit data. Examples include registration and login. In these
|
|
25
|
-
# cases, authorization data will be empty, and must be provided by the applicaiton.
|
|
26
|
-
return {self.name: authorization_data.get(self.config("authorization_data_key_name"), "N/A"), **data}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from .string import String
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class CreatedByHeader(String):
|
|
5
|
-
required_configs = [
|
|
6
|
-
"header_name",
|
|
7
|
-
]
|
|
8
|
-
|
|
9
|
-
def __init__(self, di):
|
|
10
|
-
super().__init__(di)
|
|
11
|
-
|
|
12
|
-
@property
|
|
13
|
-
def is_writeable(self):
|
|
14
|
-
return False
|
|
15
|
-
|
|
16
|
-
def pre_save(self, data, model):
|
|
17
|
-
if model.exists:
|
|
18
|
-
return data
|
|
19
|
-
|
|
20
|
-
input_output = self.di.build("input_output", cache=True)
|
|
21
|
-
return {
|
|
22
|
-
**data,
|
|
23
|
-
self.name: input_output.get_request_header(self.config("header_name")),
|
|
24
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
from .string import String
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class CreatedByIp(String):
|
|
5
|
-
def __init__(self, di):
|
|
6
|
-
super().__init__(di)
|
|
7
|
-
|
|
8
|
-
@property
|
|
9
|
-
def is_writeable(self):
|
|
10
|
-
return False
|
|
11
|
-
|
|
12
|
-
def pre_save(self, data, model):
|
|
13
|
-
if model.exists:
|
|
14
|
-
return data
|
|
15
|
-
|
|
16
|
-
input_output = self.di.build("input_output", cache=True)
|
|
17
|
-
return {**data, self.name: input_output.get_client_ip()}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
from .string import String
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class CreatedByRoutingData(String):
|
|
5
|
-
required_configs = [
|
|
6
|
-
"routing_data_name",
|
|
7
|
-
]
|
|
8
|
-
|
|
9
|
-
def __init__(self, di):
|
|
10
|
-
super().__init__(di)
|
|
11
|
-
|
|
12
|
-
@property
|
|
13
|
-
def is_writeable(self):
|
|
14
|
-
return False
|
|
15
|
-
|
|
16
|
-
def pre_save(self, data, model):
|
|
17
|
-
if model.exists:
|
|
18
|
-
return data
|
|
19
|
-
|
|
20
|
-
input_output = self.di.build("input_output", cache=True)
|
|
21
|
-
routing_data = input_output.routing_data()
|
|
22
|
-
return {
|
|
23
|
-
**data,
|
|
24
|
-
self.name: routing_data[self.config("routing_data_name")],
|
|
25
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
from .string import String
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class CreatedByUserAgent(String):
|
|
5
|
-
def __init__(self, di):
|
|
6
|
-
super().__init__(di)
|
|
7
|
-
|
|
8
|
-
@property
|
|
9
|
-
def is_writeable(self):
|
|
10
|
-
return False
|
|
11
|
-
|
|
12
|
-
def pre_save(self, data, model):
|
|
13
|
-
if model.exists:
|
|
14
|
-
return data
|
|
15
|
-
|
|
16
|
-
input_output = self.di.build("input_output", cache=True)
|
|
17
|
-
return {**data, self.name: input_output.get_request_header("user-agent")}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from .datetime_micro import DateTimeMicro
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class CreatedMicro(DateTimeMicro):
|
|
5
|
-
my_configs = [
|
|
6
|
-
"date_format",
|
|
7
|
-
"default_date",
|
|
8
|
-
"utc",
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
def __init__(self, di, datetime):
|
|
12
|
-
super().__init__(di)
|
|
13
|
-
self.datetime = datetime
|
|
14
|
-
|
|
15
|
-
@property
|
|
16
|
-
def is_writeable(self):
|
|
17
|
-
return False
|
|
18
|
-
|
|
19
|
-
def pre_save(self, data, model):
|
|
20
|
-
if model.exists:
|
|
21
|
-
return data
|
|
22
|
-
if self.config("utc", silent=True):
|
|
23
|
-
now = self.datetime.datetime.now(self.datetime.timezone.utc)
|
|
24
|
-
else:
|
|
25
|
-
now = self.datetime.datetime.now()
|
|
26
|
-
return {**data, self.name: now}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
from .column import Column
|
|
2
|
-
from datetime import datetime, timezone
|
|
3
|
-
import dateparser
|
|
4
|
-
from ..autodoc.schema import DateTime as AutoDocDateTime
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class DateTime(Column):
|
|
8
|
-
_auto_doc_class = AutoDocDateTime
|
|
9
|
-
_date_format = "%Y-%m-%d %H:%M:%S"
|
|
10
|
-
_default_date = "0000-00-00 00:00:00"
|
|
11
|
-
|
|
12
|
-
my_configs = [
|
|
13
|
-
"date_format",
|
|
14
|
-
"default_date",
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
def __init__(self, di):
|
|
18
|
-
super().__init__(di)
|
|
19
|
-
|
|
20
|
-
def _finalize_configuration(self, configuration):
|
|
21
|
-
return {
|
|
22
|
-
**{
|
|
23
|
-
"date_format": self._date_format,
|
|
24
|
-
"default_date": self._default_date,
|
|
25
|
-
},
|
|
26
|
-
**super()._finalize_configuration(configuration),
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
def from_backend(self, value):
|
|
30
|
-
if not value or value == self.config("default_date"):
|
|
31
|
-
date = None
|
|
32
|
-
elif type(value) == str:
|
|
33
|
-
date = dateparser.parse(value)
|
|
34
|
-
else:
|
|
35
|
-
date = value
|
|
36
|
-
return date.replace(tzinfo=timezone.utc) if date else None
|
|
37
|
-
|
|
38
|
-
def to_backend(self, data):
|
|
39
|
-
if not self.name in data or type(data[self.name]) == str or data[self.name] == None:
|
|
40
|
-
return data
|
|
41
|
-
|
|
42
|
-
# hopefully this is a Python datetime object in UTC timezone...
|
|
43
|
-
return {**data, **{self.name: data[self.name].strftime(self.config("date_format"))}}
|
|
44
|
-
|
|
45
|
-
def to_json(self, model):
|
|
46
|
-
datetime = model.get(self.name, silent=True)
|
|
47
|
-
return {self.name: datetime.isoformat() if datetime else None}
|
|
48
|
-
|
|
49
|
-
def build_condition(self, value, operator=None, column_prefix=""):
|
|
50
|
-
date = dateparser.parse(value).astimezone(timezone.utc).strftime(self.config("date_format"))
|
|
51
|
-
if not operator:
|
|
52
|
-
operator = "="
|
|
53
|
-
return f"{column_prefix}{self.name}{operator}{date}"
|
|
54
|
-
|
|
55
|
-
def is_allowed_operator(self, operator, relationship_reference=None):
|
|
56
|
-
"""
|
|
57
|
-
This is called when processing user data to decide if the end-user is specifying an allowed operator
|
|
58
|
-
"""
|
|
59
|
-
return operator in ["=", "<", ">", "<=", ">="]
|
|
60
|
-
|
|
61
|
-
def input_error_for_value(self, value, operator=None):
|
|
62
|
-
value = dateparser.parse(value)
|
|
63
|
-
if not value:
|
|
64
|
-
return "given value did not appear to be a valid date"
|
|
65
|
-
if not value.tzinfo:
|
|
66
|
-
return "date is missing timezone information"
|
|
67
|
-
return ""
|
|
68
|
-
|
|
69
|
-
def values_match(self, value_1, value_2):
|
|
70
|
-
"""
|
|
71
|
-
Compares two values to see if they are the same
|
|
72
|
-
"""
|
|
73
|
-
# in this function we deal with data directly out of the backend, so our date is likely
|
|
74
|
-
# to be string-ified and we want to look for default (e.g. null) values in string form.
|
|
75
|
-
if type(value_1) == str and "0000-00-00" in value_1:
|
|
76
|
-
value_1 = None
|
|
77
|
-
if type(value_2) == str and "0000-00-00" in value_2:
|
|
78
|
-
value_2 = None
|
|
79
|
-
number_values = 0
|
|
80
|
-
if value_1:
|
|
81
|
-
number_values += 1
|
|
82
|
-
if value_2:
|
|
83
|
-
number_values += 1
|
|
84
|
-
if number_values == 0:
|
|
85
|
-
return True
|
|
86
|
-
if number_values == 1:
|
|
87
|
-
return False
|
|
88
|
-
|
|
89
|
-
if type(value_1) == str:
|
|
90
|
-
value_1 = dateparser.parse(value_1)
|
|
91
|
-
if type(value_2) == str:
|
|
92
|
-
value_2 = dateparser.parse(value_2)
|
|
93
|
-
|
|
94
|
-
# we need to make sure we're comparing in the same timezones. For our purposes, a difference in timezone
|
|
95
|
-
# is fine as long as they represent the same time (e.g. 16:00EST == 20:00UTC). For python, same time in different
|
|
96
|
-
# timezones is treated as different datetime objects.
|
|
97
|
-
if value_1.tzinfo is not None and value_2.tzinfo is not None:
|
|
98
|
-
value_1 = value_1.astimezone(value_2.tzinfo)
|
|
99
|
-
|
|
100
|
-
# two times can be the same but if one is datetime-aware and one is not, python will treat them as not equal.
|
|
101
|
-
# we want to treat such times as being the same. Therefore, check for equality but ignore the timezone.
|
|
102
|
-
for to_check in ["year", "month", "day", "hour", "minute", "second", "microsecond"]:
|
|
103
|
-
if getattr(value_1, to_check) != getattr(value_2, to_check):
|
|
104
|
-
return False
|
|
105
|
-
|
|
106
|
-
# and since we already converted the timezones to match (or one has a timezone and one doesn't), we're good to go.
|
|
107
|
-
# if we passed the above loop then the times are the same.
|
|
108
|
-
return True
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
from .datetime import DateTime
|
|
2
|
-
from datetime import datetime, timezone
|
|
3
|
-
import dateparser
|
|
4
|
-
from ..autodoc.schema import DateTime as AutoDocDateTime
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class DateTimeMicro(DateTime):
|
|
8
|
-
_date_format = "%Y-%m-%d %H:%M:%S.%f"
|
|
9
|
-
_default_date = "0000-00-00 00:00:00.000000"
|
|
10
|
-
|
|
11
|
-
def __init__(self, di):
|
|
12
|
-
super().__init__(di)
|
clearskies/column_types/email.py
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from .string import String
|
|
2
|
-
import re
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Email(String):
|
|
6
|
-
def __init__(self, di):
|
|
7
|
-
super().__init__(di)
|
|
8
|
-
|
|
9
|
-
def input_error_for_value(self, value, operator=None):
|
|
10
|
-
if type(value) != str:
|
|
11
|
-
return f"Value must be a string for {self.name}"
|
|
12
|
-
if operator and operator.lower() == "like":
|
|
13
|
-
# don't check for an email if doing a fuzzy search, since we may be searching
|
|
14
|
-
# for a partial email
|
|
15
|
-
return ""
|
|
16
|
-
if re.search(r"^[^@\s]+@[^@]+\.[^@]+$", value):
|
|
17
|
-
return ""
|
|
18
|
-
return "Invalid email address"
|
clearskies/column_types/float.py
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from .column import Column
|
|
2
|
-
from ..autodoc.schema import Number as AutoDocNumber
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Float(Column):
|
|
6
|
-
_auto_doc_class = AutoDocNumber
|
|
7
|
-
|
|
8
|
-
def __init__(self, di):
|
|
9
|
-
super().__init__(di)
|
|
10
|
-
|
|
11
|
-
def from_backend(self, value):
|
|
12
|
-
return float(value)
|
|
13
|
-
|
|
14
|
-
def to_backend(self, data):
|
|
15
|
-
if self.name not in data or data[self.name] is None:
|
|
16
|
-
return data
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
**data,
|
|
20
|
-
self.name: float(data[self.name]),
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
def check_input(self, model, data):
|
|
24
|
-
if not self.name in data:
|
|
25
|
-
return ""
|
|
26
|
-
if isinstance(data[self.name], int) or isinstance(data[self.name], float) or data[self.name] == None:
|
|
27
|
-
return ""
|
|
28
|
-
return f"Invalid input: {self.name} must be an integer or float"
|
|
29
|
-
|
|
30
|
-
def build_condition(self, value, operator=None, column_prefix=""):
|
|
31
|
-
if not operator:
|
|
32
|
-
operator = "="
|
|
33
|
-
return f"{column_prefix}{self.name}{operator}{value}"
|
|
34
|
-
|
|
35
|
-
def is_allowed_operator(self, operator, relationship_reference=None):
|
|
36
|
-
return operator in ["=", "<", ">", "<=", ">="]
|
|
37
|
-
|
|
38
|
-
def input_error_for_value(self, value, operator=None):
|
|
39
|
-
return (
|
|
40
|
-
"value should be an integer or float"
|
|
41
|
-
if (type(value) != int and type(value) != float and value is not None)
|
|
42
|
-
else ""
|
|
43
|
-
)
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
from .column import Column
|
|
2
|
-
import re
|
|
3
|
-
from collections import OrderedDict
|
|
4
|
-
from ..autodoc.schema import Array as AutoDocArray
|
|
5
|
-
from ..autodoc.schema import Object as AutoDocObject
|
|
6
|
-
from ..autodoc.schema import String as AutoDocString
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class HasMany(Column):
|
|
10
|
-
"""
|
|
11
|
-
Controls a has-many relationship.
|
|
12
|
-
|
|
13
|
-
This is a readonly column. When used in a model context it will return an iterable with the related child records.
|
|
14
|
-
When used in an API context, it will convert the child records into a list of objects.
|
|
15
|
-
|
|
16
|
-
It assumes that the foreign id in the child table is `[parent_model_class_name]_id` in all lower case.
|
|
17
|
-
e.g., if the parent model class is named Status, then it assumes an id in the child class called `status_id`.
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
required_configs = [
|
|
21
|
-
"child_models_class",
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
my_configs = [
|
|
25
|
-
"foreign_column_name",
|
|
26
|
-
"child_columns",
|
|
27
|
-
"is_readable",
|
|
28
|
-
"readable_child_columns",
|
|
29
|
-
"parent_id_column_name",
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
def __init__(self, di):
|
|
33
|
-
super().__init__(di)
|
|
34
|
-
|
|
35
|
-
@property
|
|
36
|
-
def is_writeable(self):
|
|
37
|
-
return False
|
|
38
|
-
|
|
39
|
-
@property
|
|
40
|
-
def is_readable(self):
|
|
41
|
-
is_readable = self.config("is_readable", True)
|
|
42
|
-
# default is_readable to False
|
|
43
|
-
return True if (is_readable and is_readable is not None) else False
|
|
44
|
-
|
|
45
|
-
def configure(self, name, configuration, model_class):
|
|
46
|
-
if "child_models_class" not in configuration:
|
|
47
|
-
raise KeyError(
|
|
48
|
-
f"Missing required configuration 'child_models_class' for column '{name}' in model class "
|
|
49
|
-
+ f"'{model_class.__name__}'"
|
|
50
|
-
)
|
|
51
|
-
self.validate_models_class(configuration["child_models_class"])
|
|
52
|
-
configuration["parent_id_column_name"] = model_class.id_column_name
|
|
53
|
-
|
|
54
|
-
# if readable_child_columns is set then load up the child models/columns now, because we'll need it in the
|
|
55
|
-
# _check_configuration step, but we don't want to load it there because we can't save it back into the config
|
|
56
|
-
if "foreign_column_name" not in configuration:
|
|
57
|
-
configuration["foreign_column_name"] = (
|
|
58
|
-
re.sub(r"(?<!^)(?=[A-Z])", "_", model_class.__name__.replace("_", "")).lower() + "_id"
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
# continue normally now...
|
|
62
|
-
super().configure(name, configuration, model_class)
|
|
63
|
-
|
|
64
|
-
def _check_configuration(self, configuration):
|
|
65
|
-
super()._check_configuration(configuration)
|
|
66
|
-
if configuration.get("is_readable"):
|
|
67
|
-
child_columns = self.di.build(configuration["child_models_class"], cache=True).raw_columns_configuration()
|
|
68
|
-
error_prefix = f"Configuration error for '{self.name}' in '{self.model_class.__name__}':"
|
|
69
|
-
if not "readable_child_columns" in configuration:
|
|
70
|
-
raise ValueError(f"{error_prefix} must provide 'readable_child_columns' if is_readable is set")
|
|
71
|
-
readable_child_columns = configuration["readable_child_columns"]
|
|
72
|
-
if not hasattr(readable_child_columns, "__iter__"):
|
|
73
|
-
raise ValueError(
|
|
74
|
-
f"{error_prefix} 'readable_child_columns' should be an iterable "
|
|
75
|
-
+ "with the list of child columns to output."
|
|
76
|
-
)
|
|
77
|
-
if isinstance(readable_child_columns, str):
|
|
78
|
-
raise ValueError(
|
|
79
|
-
f"{error_prefix} 'readable_child_columns' should be an iterable "
|
|
80
|
-
+ "with the list of child columns to output."
|
|
81
|
-
)
|
|
82
|
-
for column_name in readable_child_columns:
|
|
83
|
-
if column_name not in child_columns:
|
|
84
|
-
raise ValueError(
|
|
85
|
-
f"{error_prefix} 'readable_child_columns' references column named '{column_name}' but this"
|
|
86
|
-
+ "column does not exist in the model class."
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
def get_child_columns(self):
|
|
90
|
-
if "child_columns" not in self.configuration:
|
|
91
|
-
self.configuration["child_columns"] = self.child_models.columns()
|
|
92
|
-
return self.configuration["child_columns"]
|
|
93
|
-
|
|
94
|
-
def can_provide(self, column_name):
|
|
95
|
-
return column_name == self.name
|
|
96
|
-
|
|
97
|
-
def provide(self, data, column_name):
|
|
98
|
-
foreign_column_name = self.config("foreign_column_name")
|
|
99
|
-
id_column_name = self.config("parent_id_column_name")
|
|
100
|
-
return self.child_models.where(f"{foreign_column_name}={data[id_column_name]}")
|
|
101
|
-
|
|
102
|
-
def to_json(self, model):
|
|
103
|
-
children = []
|
|
104
|
-
columns = self.get_child_columns()
|
|
105
|
-
for child in model.__getattr__(self.name):
|
|
106
|
-
json = OrderedDict()
|
|
107
|
-
child_id_column_name = child.id_column_name
|
|
108
|
-
json = {
|
|
109
|
-
**json,
|
|
110
|
-
**columns[child_id_column_name].to_json(child),
|
|
111
|
-
}
|
|
112
|
-
for column_name in self.config("readable_child_columns"):
|
|
113
|
-
json = {
|
|
114
|
-
**json,
|
|
115
|
-
**columns[column_name].to_json(child),
|
|
116
|
-
}
|
|
117
|
-
children.append(json)
|
|
118
|
-
return {self.name: children}
|
|
119
|
-
|
|
120
|
-
@property
|
|
121
|
-
def child_models(self):
|
|
122
|
-
return self.di.build(self.config("child_models_class"), cache=True)
|
|
123
|
-
|
|
124
|
-
def documentation(self, name=None, example=None, value=None):
|
|
125
|
-
columns = self.get_child_columns()
|
|
126
|
-
child_id_column_name = self.child_models.get_id_column_name()
|
|
127
|
-
child_properties = [columns[child_id_column_name].documentation()]
|
|
128
|
-
|
|
129
|
-
for column_name in self.config("readable_child_columns"):
|
|
130
|
-
child_docs = columns[column_name].documentation()
|
|
131
|
-
if type(child_docs) != list:
|
|
132
|
-
child_docs = [child_docs]
|
|
133
|
-
child_properties.extend(child_docs)
|
|
134
|
-
|
|
135
|
-
child_object = AutoDocObject(
|
|
136
|
-
self.camel_to_nice(self.child_models.model_class().__name__),
|
|
137
|
-
child_properties,
|
|
138
|
-
)
|
|
139
|
-
return AutoDocArray(name if name is not None else self.name, child_object, value=value)
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
from .column import Column
|
|
2
|
-
from ..autodoc.schema import Integer as AutoDocInteger
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Integer(Column):
|
|
6
|
-
_auto_doc_class = AutoDocInteger
|
|
7
|
-
|
|
8
|
-
def __init__(self, di):
|
|
9
|
-
super().__init__(di)
|
|
10
|
-
|
|
11
|
-
def to_backend(self, data):
|
|
12
|
-
if self.name not in data or data[self.name] is None:
|
|
13
|
-
return data
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
**data,
|
|
17
|
-
self.name: int(data[self.name]),
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
def from_backend(self, value):
|
|
21
|
-
return int(value)
|
|
22
|
-
|
|
23
|
-
def input_error_for_value(self, value, operator=None):
|
|
24
|
-
if operator == "in":
|
|
25
|
-
if type(value) != list:
|
|
26
|
-
return f'{self.name} must be an integer when searching with the "IN" operator'
|
|
27
|
-
for val in value:
|
|
28
|
-
if type(val) != int:
|
|
29
|
-
return f"All items in {self.name} must be integers"
|
|
30
|
-
return ""
|
|
31
|
-
return f"{self.name} must be an integer" if type(value) != int else ""
|
|
32
|
-
|
|
33
|
-
def build_condition(self, value, operator=None, column_prefix=""):
|
|
34
|
-
if operator == "in":
|
|
35
|
-
return f"{column_prefix}{self.name} IN (" + ",".join([str(val) for val in value]) + ")"
|
|
36
|
-
if not operator:
|
|
37
|
-
operator = "="
|
|
38
|
-
return f"{column_prefix}{self.name}{operator}{value}"
|
|
39
|
-
|
|
40
|
-
def is_allowed_operator(self, operator, relationship_reference=None):
|
|
41
|
-
return operator in ["=", "<", ">", "<=", ">=", "in"]
|
clearskies/column_types/json.py
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from .column import Column
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class JSON(Column):
|
|
6
|
-
def __init__(self, di):
|
|
7
|
-
super().__init__(di)
|
|
8
|
-
|
|
9
|
-
def from_backend(self, value):
|
|
10
|
-
if type(value) == list or type(value) == dict:
|
|
11
|
-
return value
|
|
12
|
-
if not value:
|
|
13
|
-
return None
|
|
14
|
-
try:
|
|
15
|
-
return json.loads(value)
|
|
16
|
-
except json.JSONDecodeError:
|
|
17
|
-
return None
|
|
18
|
-
|
|
19
|
-
def to_backend(self, data):
|
|
20
|
-
if self.name in data:
|
|
21
|
-
data[self.name] = json.dumps(data[self.name]) if data[self.name] else ""
|
|
22
|
-
return data
|
|
23
|
-
|
|
24
|
-
def to_json(self, model):
|
|
25
|
-
return {self.name: model.get(self.name, silent=True)}
|