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
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
from .simple_search import SimpleSearch
|
|
2
|
-
from .. import autodoc
|
|
3
|
-
from .. import condition_parser
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class AdvancedSearch(SimpleSearch):
|
|
7
|
-
expected_request_methods = "POST"
|
|
8
|
-
|
|
9
|
-
@property
|
|
10
|
-
def allowed_request_keys(self):
|
|
11
|
-
return ["sort", "where", "limit"]
|
|
12
|
-
|
|
13
|
-
def configure_models_from_request_data(self, models, request_data, query_parameters, pagination_data):
|
|
14
|
-
limit = int(
|
|
15
|
-
self._from_either(request_data, query_parameters, "limit", default=self.configuration("default_limit"))
|
|
16
|
-
)
|
|
17
|
-
models = models.limit(limit)
|
|
18
|
-
if pagination_data:
|
|
19
|
-
models = models.pagination(**pagination_data)
|
|
20
|
-
if "sort" in request_data:
|
|
21
|
-
primary_column = request_data["sort"][0]["column"]
|
|
22
|
-
primary_direction = request_data["sort"][0]["direction"]
|
|
23
|
-
secondary_column = None
|
|
24
|
-
secondary_direction = None
|
|
25
|
-
secondary_table = None
|
|
26
|
-
if len(request_data["sort"]) == 2:
|
|
27
|
-
secondary_column = request_data["sort"][1]["column"]
|
|
28
|
-
secondary_direction = request_data["sort"][1]["direction"]
|
|
29
|
-
# call _add_join first because it's expecting something like table.column_name, and
|
|
30
|
-
# _resolve_references_for_query will break that up.
|
|
31
|
-
models = self._add_join(secondary_column, models)
|
|
32
|
-
[secondary_column, secondary_table] = self._resolve_references_for_query(secondary_column)
|
|
33
|
-
|
|
34
|
-
# call _add_join first because it's expecting column_name to be something like 'table.column_name', and
|
|
35
|
-
# _resolve_references_for_query will break up the table and column name reference.
|
|
36
|
-
models = self._add_join(primary_column, models)
|
|
37
|
-
[primary_column, primary_table] = self._resolve_references_for_query(primary_column)
|
|
38
|
-
models = models.sort_by(
|
|
39
|
-
primary_column,
|
|
40
|
-
primary_direction,
|
|
41
|
-
primary_table=primary_table,
|
|
42
|
-
secondary_column=secondary_column,
|
|
43
|
-
secondary_direction=secondary_direction,
|
|
44
|
-
secondary_table=secondary_table,
|
|
45
|
-
)
|
|
46
|
-
if "where" in request_data:
|
|
47
|
-
for where in request_data["where"]:
|
|
48
|
-
column_name = where["column"]
|
|
49
|
-
if column_name == "id":
|
|
50
|
-
column_name = self.id_column_name
|
|
51
|
-
models = self._add_join(column_name, models)
|
|
52
|
-
[column_name, relationship_reference] = self._unpack_column_name_with_reference(column_name)
|
|
53
|
-
column = self._columns[column_name]
|
|
54
|
-
models = column.add_search(
|
|
55
|
-
models,
|
|
56
|
-
where["value"],
|
|
57
|
-
operator=where["operator"].lower() if "operator" in where else None,
|
|
58
|
-
relationship_reference=relationship_reference,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
return [models, limit]
|
|
62
|
-
|
|
63
|
-
def check_request_data(self, request_data, query_parameters, pagination_data):
|
|
64
|
-
# first, check the pagination data
|
|
65
|
-
if pagination_data:
|
|
66
|
-
error = self._model.validate_pagination_kwargs(pagination_data, self.auto_case_internal_column_name)
|
|
67
|
-
if error:
|
|
68
|
-
return error
|
|
69
|
-
# next, check that they didn't provide something unexpected in the rest of the request
|
|
70
|
-
allowed_request_keys = self.allowed_request_keys
|
|
71
|
-
for key in request_data.keys():
|
|
72
|
-
if key not in allowed_request_keys:
|
|
73
|
-
return f"Invalid request parameter found in request body: '{key}'"
|
|
74
|
-
# and ensure that the data we expect is not in the query parameters. This is not as strict
|
|
75
|
-
# of a check as ensuring that *nothing* is in the query parameters, but query parameters get
|
|
76
|
-
# used for a lot of things, so that could backfire
|
|
77
|
-
for key in allowed_request_keys:
|
|
78
|
-
if key in query_parameters:
|
|
79
|
-
return f"Invalid request: key '{key}' was found in a URL parameter but should only be in the JSON body"
|
|
80
|
-
limit = request_data.get("limit", None)
|
|
81
|
-
if limit is not None and type(limit) != int and type(limit) != float and type(limit) != str:
|
|
82
|
-
return "Invalid request: 'limit' should be an integer"
|
|
83
|
-
if limit:
|
|
84
|
-
try:
|
|
85
|
-
limit = int(limit)
|
|
86
|
-
except ValueError:
|
|
87
|
-
return "Invalid request: 'limit' should be an integer"
|
|
88
|
-
if limit and limit > self.configuration("max_limit"):
|
|
89
|
-
return f"Invalid request: 'limit' must be at most {self.configuration('max_limit')}"
|
|
90
|
-
allowed_sort_columns = self.configuration("sortable_columns")
|
|
91
|
-
if not allowed_sort_columns:
|
|
92
|
-
allowed_sort_columns = self._columns
|
|
93
|
-
if "sort" in request_data:
|
|
94
|
-
if type(request_data["sort"]) != list:
|
|
95
|
-
return (
|
|
96
|
-
"Invalid request: if provided, 'sort' must be a list of "
|
|
97
|
-
+ "objects with 'column' and 'direction' keys"
|
|
98
|
-
)
|
|
99
|
-
if len(request_data["sort"]) > 2:
|
|
100
|
-
return "Invalid request: at most 2 sort directives may be specified"
|
|
101
|
-
for index, sort in enumerate(request_data["sort"]):
|
|
102
|
-
error_prefix = (
|
|
103
|
-
"Invalid request: 'sort' must be a list of objects with 'column' and 'direction'"
|
|
104
|
-
+ f" keys, but entry #{index+1}"
|
|
105
|
-
)
|
|
106
|
-
if type(sort) != dict:
|
|
107
|
-
return f"{error_prefix} was not an object"
|
|
108
|
-
if "column" not in sort or "direction" not in sort or not sort["column"] or not sort["direction"]:
|
|
109
|
-
return f"{error_prefix} did not declare both 'column' and 'direction'"
|
|
110
|
-
if len(sort) != 2:
|
|
111
|
-
return f"{error_prefix} had extra keys present"
|
|
112
|
-
if sort["direction"].lower() not in ["asc", "desc"]:
|
|
113
|
-
return (
|
|
114
|
-
"Invalid request: sort direction must be 'asc' or 'desc'"
|
|
115
|
-
+ f" but found something else in sort entry #{index+1}"
|
|
116
|
-
)
|
|
117
|
-
if sort["column"] not in allowed_sort_columns:
|
|
118
|
-
return f"Invalid request: invalid sort column specified in sort entry #{index+1}"
|
|
119
|
-
return self.check_search_in_request_data(request_data, query_parameters)
|
|
120
|
-
|
|
121
|
-
def check_search_in_request_data(self, request_data, query_parameters):
|
|
122
|
-
if "where" in request_data:
|
|
123
|
-
if type(request_data["where"]) != list:
|
|
124
|
-
return "Invalid request: if provided, 'where' must be a list of objects"
|
|
125
|
-
for index, where in enumerate(request_data["where"]):
|
|
126
|
-
if type(where) != dict:
|
|
127
|
-
return f"Invalid request: 'where' must be a list of objects, entry #{index+1} was not an object"
|
|
128
|
-
if "column" not in where or not where["column"]:
|
|
129
|
-
return f"Invalid request: 'column' missing in 'where' entry #{index+1}"
|
|
130
|
-
column_name = where["column"]
|
|
131
|
-
if column_name not in self.configuration("searchable_columns"):
|
|
132
|
-
return f"Invalid request: invalid search column specified in where entry #{index+1}"
|
|
133
|
-
[column_name, relationship_reference] = self._unpack_column_name_with_reference(column_name)
|
|
134
|
-
if column_name == "id":
|
|
135
|
-
column_name = self.id_column_name
|
|
136
|
-
if "value" not in where:
|
|
137
|
-
return f"Invalid request: 'value' missing in 'where' entry #{index+1}"
|
|
138
|
-
operator = None
|
|
139
|
-
if "operator" in where:
|
|
140
|
-
if type(where["operator"]) != str:
|
|
141
|
-
return f"Invalid request: operator must be a string in 'where' entry #{index+1}"
|
|
142
|
-
if not self._columns[column_name].is_allowed_operator(
|
|
143
|
-
where["operator"], relationship_reference=relationship_reference
|
|
144
|
-
):
|
|
145
|
-
return f"Invalid request: given operator is not allowed for column in 'where' entry #{index+1}"
|
|
146
|
-
operator = where["operator"].lower()
|
|
147
|
-
value_error = self._columns[column_name].check_search_value(
|
|
148
|
-
where["value"], operator, relationship_reference=relationship_reference
|
|
149
|
-
)
|
|
150
|
-
if value_error:
|
|
151
|
-
return f"Invalid request: {value_error} for 'where' entry #{index+1}"
|
|
152
|
-
# similarly, query parameters mean search conditions
|
|
153
|
-
for column_name, value in query_parameters.items():
|
|
154
|
-
if column_name not in self.configuration("searchable_columns"):
|
|
155
|
-
return f"Invalid request: invalid search column: '{column_name}'"
|
|
156
|
-
value_error = self._columns[column_name].check_search_value(value)
|
|
157
|
-
if value_error:
|
|
158
|
-
return f"Invalid request: {value_error} for search column '{column_name}'"
|
|
159
|
-
|
|
160
|
-
return None
|
|
161
|
-
|
|
162
|
-
def map_input_to_internal_names(self, input):
|
|
163
|
-
input = super().map_input_to_internal_names(input)
|
|
164
|
-
|
|
165
|
-
# the base will take care of most of this, but it won't handle the data inside of
|
|
166
|
-
# input['where']. Therefore, we need to handle that
|
|
167
|
-
if "where" not in input or type(input["where"]) != list:
|
|
168
|
-
return input
|
|
169
|
-
|
|
170
|
-
mapped_wheres = []
|
|
171
|
-
|
|
172
|
-
# a map between the internal column name and the external column name: easier to have before hand
|
|
173
|
-
search_column_map = {}
|
|
174
|
-
for internal_name in self.configuration("searchable_columns"):
|
|
175
|
-
external_name = self.auto_case_column_name(internal_name, True)
|
|
176
|
-
search_column_map[external_name] = internal_name
|
|
177
|
-
|
|
178
|
-
# it's important that as we do this we only change things that we know we can change.
|
|
179
|
-
# we don't want to remove things that don't belong or things that seem wrong, otherwise
|
|
180
|
-
# the input checking won't be able to return meaningful error messages.
|
|
181
|
-
for where in input["where"]:
|
|
182
|
-
if type(where) != dict:
|
|
183
|
-
mapped_wheres.append(where)
|
|
184
|
-
continue
|
|
185
|
-
mapped_where = {**where}
|
|
186
|
-
for internal_name in ["column", "operator", "value"]:
|
|
187
|
-
external_name = self.auto_case_internal_column_name(internal_name)
|
|
188
|
-
if external_name != internal_name and external_name in mapped_where:
|
|
189
|
-
mapped_where[internal_name] = mapped_where[external_name]
|
|
190
|
-
del mapped_where[external_name]
|
|
191
|
-
if "column" in mapped_where and type(mapped_where["column"]) == str:
|
|
192
|
-
if mapped_where["column"] in search_column_map:
|
|
193
|
-
mapped_where["column"] = search_column_map[mapped_where["column"]]
|
|
194
|
-
|
|
195
|
-
mapped_wheres.append(mapped_where)
|
|
196
|
-
|
|
197
|
-
input["where"] = mapped_wheres
|
|
198
|
-
return input
|
|
199
|
-
|
|
200
|
-
def documentation(self):
|
|
201
|
-
return [
|
|
202
|
-
self._documentation_request(
|
|
203
|
-
"POST",
|
|
204
|
-
[
|
|
205
|
-
*self.documentation_request_parameters(),
|
|
206
|
-
],
|
|
207
|
-
),
|
|
208
|
-
]
|
|
209
|
-
|
|
210
|
-
def documentation_request_parameters(self):
|
|
211
|
-
return [
|
|
212
|
-
*self.documentation_json_parameters(),
|
|
213
|
-
]
|
|
214
|
-
|
|
215
|
-
def documentation_json_parameters(self):
|
|
216
|
-
# named 'where' in the request
|
|
217
|
-
where_condition = autodoc.schema.Object(
|
|
218
|
-
self.auto_case_internal_column_name("condition"),
|
|
219
|
-
[
|
|
220
|
-
autodoc.schema.Enum(
|
|
221
|
-
self.auto_case_internal_column_name("column"),
|
|
222
|
-
[
|
|
223
|
-
self.auto_case_column_name(column.name, True)
|
|
224
|
-
for column in self._get_searchable_columns().values()
|
|
225
|
-
],
|
|
226
|
-
autodoc.schema.String(self.auto_case_column_name("column_name", True)),
|
|
227
|
-
example="name",
|
|
228
|
-
),
|
|
229
|
-
autodoc.schema.Enum(
|
|
230
|
-
self.auto_case_internal_column_name("operator"),
|
|
231
|
-
condition_parser.ConditionParser.operators,
|
|
232
|
-
autodoc.schema.String(self.auto_case_internal_column_name("operator")),
|
|
233
|
-
example="=",
|
|
234
|
-
),
|
|
235
|
-
autodoc.schema.String(self.auto_case_internal_column_name("value"), example="Jane"),
|
|
236
|
-
],
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
allowed_sort_columns = self.configuration("sortable_columns")
|
|
240
|
-
if not allowed_sort_columns:
|
|
241
|
-
allowed_sort_columns = [self.auto_case_column_name(key, True) for key in self._columns.keys()]
|
|
242
|
-
|
|
243
|
-
sort_item = autodoc.schema.Object(
|
|
244
|
-
self.auto_case_internal_column_name("sort"),
|
|
245
|
-
[
|
|
246
|
-
autodoc.schema.Enum(
|
|
247
|
-
self.auto_case_internal_column_name("column"),
|
|
248
|
-
allowed_sort_columns,
|
|
249
|
-
autodoc.schema.String(self.auto_case_internal_column_name("column")),
|
|
250
|
-
example=self.auto_case_internal_column_name("name"),
|
|
251
|
-
),
|
|
252
|
-
autodoc.schema.Enum(
|
|
253
|
-
self.auto_case_internal_column_name("direction"),
|
|
254
|
-
["asc", "desc"],
|
|
255
|
-
autodoc.schema.String(self.auto_case_internal_column_name("direction")),
|
|
256
|
-
example="asc",
|
|
257
|
-
),
|
|
258
|
-
],
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
return [
|
|
262
|
-
autodoc.request.JSONBody(
|
|
263
|
-
autodoc.schema.Array(self.auto_case_internal_column_name("where"), where_condition),
|
|
264
|
-
description="List of search conditions",
|
|
265
|
-
),
|
|
266
|
-
autodoc.request.JSONBody(
|
|
267
|
-
autodoc.schema.Array(self.auto_case_internal_column_name("sort"), sort_item),
|
|
268
|
-
description="List of sort directives (max 2)",
|
|
269
|
-
),
|
|
270
|
-
*self.documentation_json_pagination_parameters(),
|
|
271
|
-
]
|