clear-skies 2.0.27__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.
Potentially problematic release.
This version of clear-skies might be problematic. Click here for more details.
- clear_skies-2.0.27.dist-info/METADATA +78 -0
- clear_skies-2.0.27.dist-info/RECORD +270 -0
- clear_skies-2.0.27.dist-info/WHEEL +4 -0
- clear_skies-2.0.27.dist-info/licenses/LICENSE +7 -0
- clearskies/__init__.py +69 -0
- clearskies/action.py +7 -0
- clearskies/authentication/__init__.py +15 -0
- clearskies/authentication/authentication.py +44 -0
- clearskies/authentication/authorization.py +23 -0
- clearskies/authentication/authorization_pass_through.py +22 -0
- clearskies/authentication/jwks.py +165 -0
- clearskies/authentication/public.py +5 -0
- clearskies/authentication/secret_bearer.py +551 -0
- clearskies/autodoc/__init__.py +8 -0
- clearskies/autodoc/formats/__init__.py +5 -0
- clearskies/autodoc/formats/oai3_json/__init__.py +7 -0
- clearskies/autodoc/formats/oai3_json/oai3_json.py +87 -0
- clearskies/autodoc/formats/oai3_json/oai3_schema_resolver.py +15 -0
- clearskies/autodoc/formats/oai3_json/parameter.py +35 -0
- clearskies/autodoc/formats/oai3_json/request.py +68 -0
- clearskies/autodoc/formats/oai3_json/response.py +28 -0
- clearskies/autodoc/formats/oai3_json/schema/__init__.py +11 -0
- clearskies/autodoc/formats/oai3_json/schema/array.py +9 -0
- clearskies/autodoc/formats/oai3_json/schema/default.py +13 -0
- clearskies/autodoc/formats/oai3_json/schema/enum.py +7 -0
- clearskies/autodoc/formats/oai3_json/schema/object.py +35 -0
- clearskies/autodoc/formats/oai3_json/test.json +1985 -0
- clearskies/autodoc/py.typed +0 -0
- clearskies/autodoc/request/__init__.py +15 -0
- clearskies/autodoc/request/header.py +6 -0
- clearskies/autodoc/request/json_body.py +6 -0
- clearskies/autodoc/request/parameter.py +8 -0
- clearskies/autodoc/request/request.py +47 -0
- clearskies/autodoc/request/url_parameter.py +6 -0
- clearskies/autodoc/request/url_path.py +6 -0
- clearskies/autodoc/response/__init__.py +5 -0
- clearskies/autodoc/response/response.py +9 -0
- clearskies/autodoc/schema/__init__.py +31 -0
- clearskies/autodoc/schema/array.py +10 -0
- clearskies/autodoc/schema/base64.py +8 -0
- clearskies/autodoc/schema/boolean.py +5 -0
- clearskies/autodoc/schema/date.py +5 -0
- clearskies/autodoc/schema/datetime.py +5 -0
- clearskies/autodoc/schema/double.py +5 -0
- clearskies/autodoc/schema/enum.py +17 -0
- clearskies/autodoc/schema/integer.py +6 -0
- clearskies/autodoc/schema/long.py +5 -0
- clearskies/autodoc/schema/number.py +6 -0
- clearskies/autodoc/schema/object.py +13 -0
- clearskies/autodoc/schema/password.py +5 -0
- clearskies/autodoc/schema/schema.py +11 -0
- clearskies/autodoc/schema/string.py +5 -0
- clearskies/backends/__init__.py +67 -0
- clearskies/backends/api_backend.py +1194 -0
- clearskies/backends/backend.py +137 -0
- clearskies/backends/cursor_backend.py +339 -0
- clearskies/backends/graphql_backend.py +977 -0
- clearskies/backends/memory_backend.py +794 -0
- clearskies/backends/secrets_backend.py +100 -0
- clearskies/clients/__init__.py +5 -0
- clearskies/clients/graphql_client.py +182 -0
- 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 +145 -0
- clearskies/columns/belongs_to_self.py +109 -0
- clearskies/columns/boolean.py +110 -0
- clearskies/columns/category_tree.py +274 -0
- clearskies/columns/category_tree_ancestors.py +51 -0
- clearskies/columns/category_tree_children.py +125 -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 +552 -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 +281 -0
- clearskies/columns/many_to_many_models.py +163 -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 +11 -0
- clearskies/contexts/cli.py +130 -0
- clearskies/contexts/context.py +99 -0
- clearskies/contexts/wsgi.py +79 -0
- clearskies/contexts/wsgi_ref.py +87 -0
- clearskies/cursors/__init__.py +10 -0
- clearskies/cursors/cursor.py +161 -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 +15 -0
- clearskies/di/additional_config.py +130 -0
- clearskies/di/additional_config_auto_import.py +17 -0
- clearskies/di/di.py +948 -0
- clearskies/di/inject/__init__.py +25 -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/logger.py +16 -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 +106 -0
- clearskies/exceptions/__init__.py +19 -0
- clearskies/exceptions/authentication.py +2 -0
- clearskies/exceptions/authorization.py +2 -0
- clearskies/exceptions/client_error.py +2 -0
- clearskies/exceptions/input_errors.py +4 -0
- clearskies/exceptions/missing_dependency.py +2 -0
- clearskies/exceptions/moved_permanently.py +3 -0
- clearskies/exceptions/moved_temporarily.py +3 -0
- clearskies/exceptions/not_found.py +2 -0
- clearskies/functional/__init__.py +7 -0
- clearskies/functional/json.py +47 -0
- clearskies/functional/routing.py +92 -0
- clearskies/functional/string.py +112 -0
- clearskies/functional/validations.py +76 -0
- clearskies/input_outputs/__init__.py +13 -0
- clearskies/input_outputs/cli.py +157 -0
- clearskies/input_outputs/exceptions/__init__.py +7 -0
- clearskies/input_outputs/exceptions/cli_input_error.py +2 -0
- clearskies/input_outputs/exceptions/cli_not_found.py +2 -0
- clearskies/input_outputs/headers.py +54 -0
- clearskies/input_outputs/input_output.py +116 -0
- clearskies/input_outputs/programmatic.py +62 -0
- clearskies/input_outputs/py.typed +0 -0
- clearskies/input_outputs/wsgi.py +80 -0
- clearskies/loggable.py +19 -0
- clearskies/model.py +2039 -0
- clearskies/py.typed +0 -0
- clearskies/query/__init__.py +12 -0
- clearskies/query/condition.py +228 -0
- clearskies/query/join.py +136 -0
- clearskies/query/query.py +195 -0
- clearskies/query/sort.py +27 -0
- clearskies/schema.py +82 -0
- clearskies/secrets/__init__.py +7 -0
- clearskies/secrets/additional_configs/__init__.py +32 -0
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +61 -0
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +160 -0
- clearskies/secrets/akeyless.py +507 -0
- clearskies/secrets/exceptions/__init__.py +7 -0
- clearskies/secrets/exceptions/not_found_error.py +2 -0
- clearskies/secrets/exceptions/permissions_error.py +2 -0
- clearskies/secrets/secrets.py +39 -0
- clearskies/security_header.py +17 -0
- clearskies/security_headers/__init__.py +11 -0
- clearskies/security_headers/cache_control.py +68 -0
- clearskies/security_headers/cors.py +51 -0
- clearskies/security_headers/csp.py +95 -0
- clearskies/security_headers/hsts.py +23 -0
- clearskies/security_headers/x_content_type_options.py +0 -0
- clearskies/security_headers/x_frame_options.py +0 -0
- 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/validators/required.py +32 -0
- clearskies/validators/timedelta.py +58 -0
- clearskies/validators/unique.py +28 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
2
|
+
|
|
3
|
+
from clearskies.autodoc.schema import Schema as AutoDocSchema
|
|
4
|
+
from clearskies.backends.backend import Backend
|
|
5
|
+
from clearskies.di import inject
|
|
6
|
+
from clearskies.query import Condition, Query
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from clearskies import Model
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SecretsBackend(Backend):
|
|
13
|
+
"""
|
|
14
|
+
Fetch and store data from a secret provider.
|
|
15
|
+
|
|
16
|
+
## Installing Dependencies
|
|
17
|
+
|
|
18
|
+
Clearskies uses Akeyless by default to manage the secrets.
|
|
19
|
+
This is not installed by default, but is a named extra that you can install when needed via:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install clear-skies[secrets]
|
|
23
|
+
```
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
"""The secrets instance."""
|
|
27
|
+
secrets = inject.Secrets()
|
|
28
|
+
|
|
29
|
+
can_count: bool = False
|
|
30
|
+
|
|
31
|
+
def __init__(self):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
def check_query(self, query: Query) -> None:
|
|
35
|
+
if not query.conditions:
|
|
36
|
+
raise KeyError(f"You must search by an id when using the secrets backend.")
|
|
37
|
+
|
|
38
|
+
def update(self, id: str, data: dict[str, Any], model: Model) -> dict[str, Any]: # type: ignore[override]
|
|
39
|
+
"""Update the record with the given id with the information from the data dictionary."""
|
|
40
|
+
folder_path = self._make_folder_path(model, id)
|
|
41
|
+
for key, value in data.items():
|
|
42
|
+
if key == model.id_column_name:
|
|
43
|
+
continue
|
|
44
|
+
self.secrets.update(f"{folder_path}{key}", value)
|
|
45
|
+
|
|
46
|
+
# and now query again to fetch the updated record.
|
|
47
|
+
return self.records(Query(model.__class__, conditions=[Condition(f"{model.id_column_name}={id}")]))[0]
|
|
48
|
+
|
|
49
|
+
def create(self, data: dict[str, Any], model: Model) -> dict[str, Any]:
|
|
50
|
+
if not model.id_column_name in data:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"You must provide '{model.id_column_name}' when creating a record with the secrets backend"
|
|
53
|
+
)
|
|
54
|
+
return self.update(data[model.id_column_name], data, model)
|
|
55
|
+
|
|
56
|
+
def delete(self, id: str, model: Model) -> bool: # type: ignore[override]
|
|
57
|
+
"""
|
|
58
|
+
Delete the record with the given id.
|
|
59
|
+
|
|
60
|
+
Note that this isn't implemented yet, and always returns True.
|
|
61
|
+
"""
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
def records(self, query: Query, next_page_data: dict[str, str | int] | None = None) -> list[dict[str, Any]]:
|
|
65
|
+
"""Return a list of records that match the given query configuration."""
|
|
66
|
+
self.check_query(query)
|
|
67
|
+
for condition in query.conditions:
|
|
68
|
+
if condition.operator != "=":
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"I'm not very smart and only know how to search with the equals operator, but I received a condition of {condition.parsed}. If you need to support this, you'll have to extend the ApiBackend and overwrite the build_records_request method."
|
|
71
|
+
)
|
|
72
|
+
if condition.column_name == query.model_class.id_column_name:
|
|
73
|
+
id = condition.values[0]
|
|
74
|
+
break
|
|
75
|
+
if id is None:
|
|
76
|
+
raise ValueError(f"You must search by '{query.model_class.id_column_name}' when using the secrets backend")
|
|
77
|
+
|
|
78
|
+
folder_path = self._make_folder_path(query.model_class, id)
|
|
79
|
+
data = {query.model_class.id_column_name: id}
|
|
80
|
+
for path in self.secrets.list_secrets(folder_path):
|
|
81
|
+
data[path[len(folder_path) :]] = self.secrets.get(path)
|
|
82
|
+
return [data]
|
|
83
|
+
|
|
84
|
+
def _make_folder_path(self, model, id):
|
|
85
|
+
return model.table_name().rstrip("/") + "/" + id.strip("/") + "/"
|
|
86
|
+
|
|
87
|
+
def validate_pagination_data(self, data: dict[str, Any], case_mapping: Callable[[str], str]) -> str:
|
|
88
|
+
return ""
|
|
89
|
+
|
|
90
|
+
def allowed_pagination_keys(self) -> list[str]:
|
|
91
|
+
return []
|
|
92
|
+
|
|
93
|
+
def documentation_pagination_next_page_response(self, case_mapping: Callable) -> list[Any]:
|
|
94
|
+
return []
|
|
95
|
+
|
|
96
|
+
def documentation_pagination_parameters(self, case_mapping: Callable) -> list[tuple[AutoDocSchema, str]]:
|
|
97
|
+
return []
|
|
98
|
+
|
|
99
|
+
def documentation_pagination_next_page_example(self, case_mapping: Callable) -> dict[str, Any]:
|
|
100
|
+
return {}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any
|
|
2
|
+
|
|
3
|
+
from clearskies import configs, configurable, decorators, loggable
|
|
4
|
+
from clearskies.authentication import Authentication
|
|
5
|
+
from clearskies.di import InjectableProperties, inject
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from gql import Client
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GraphqlClient(configurable.Configurable, loggable.Loggable, InjectableProperties):
|
|
12
|
+
"""
|
|
13
|
+
A simple GraphQL client wrapper using gql library.
|
|
14
|
+
|
|
15
|
+
This client handles the connection to GraphQL APIs and provides methods to execute
|
|
16
|
+
queries and mutations. It supports authentication, custom headers, and configurable timeouts.
|
|
17
|
+
|
|
18
|
+
Example usage:
|
|
19
|
+
```python
|
|
20
|
+
import clearskies
|
|
21
|
+
|
|
22
|
+
client = clearskies.clients.GraphqlClient(
|
|
23
|
+
endpoint="https://api.example.com/graphql",
|
|
24
|
+
authentication=clearskies.authentication.SecretBearer(
|
|
25
|
+
environment_key="API_TOKEN", header_prefix="Bearer "
|
|
26
|
+
),
|
|
27
|
+
headers={"X-Custom-Header": "value"},
|
|
28
|
+
timeout=30,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Execute a query
|
|
32
|
+
result = client.execute('''
|
|
33
|
+
query {
|
|
34
|
+
projects {
|
|
35
|
+
id
|
|
36
|
+
name
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
''')
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
The GraphQL API endpoint URL.
|
|
45
|
+
|
|
46
|
+
The full URL to your GraphQL API endpoint. Example:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
client = clearskies.clients.GraphqlClient(
|
|
50
|
+
endpoint="https://api.example.com/graphql"
|
|
51
|
+
)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Defaults to "http://localhost:4000/graphql" for local development.
|
|
55
|
+
"""
|
|
56
|
+
endpoint = configs.String(default="http://localhost:4000/graphql")
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
Authentication mechanism for the GraphQL API.
|
|
60
|
+
|
|
61
|
+
An instance of a clearskies authentication class that provides credentials for the API.
|
|
62
|
+
The authentication object's headers() method will be called to add authorization headers
|
|
63
|
+
to requests. Example:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
client = clearskies.clients.GraphqlClient(
|
|
67
|
+
endpoint="https://api.example.com/graphql",
|
|
68
|
+
authentication=clearskies.authentication.SecretBearer(
|
|
69
|
+
environment_key="GRAPHQL_TOKEN",
|
|
70
|
+
header_prefix="Bearer "
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Set to None for public APIs that don't require authentication.
|
|
76
|
+
"""
|
|
77
|
+
authentication = configs.Authentication(default=None)
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
Additional HTTP headers to include in requests.
|
|
81
|
+
|
|
82
|
+
A dictionary of header names and values to send with every request. These headers
|
|
83
|
+
are merged with any headers provided by the authentication mechanism. Example:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
client = clearskies.clients.GraphqlClient(
|
|
87
|
+
endpoint="https://api.example.com/graphql",
|
|
88
|
+
headers={
|
|
89
|
+
"X-API-Version": "v1",
|
|
90
|
+
"X-Client-Name": "my-app"
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Defaults to an empty dictionary.
|
|
96
|
+
"""
|
|
97
|
+
headers = configs.AnyDict(default={})
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
Request timeout in seconds.
|
|
101
|
+
|
|
102
|
+
Maximum time to wait for a response from the GraphQL API before raising a timeout error.
|
|
103
|
+
Applies to both connection and read timeouts. Example:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
client = clearskies.clients.GraphqlClient(
|
|
107
|
+
endpoint="https://api.example.com/graphql",
|
|
108
|
+
timeout=60 # Wait up to 60 seconds for responses
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Defaults to 10 seconds.
|
|
113
|
+
"""
|
|
114
|
+
timeout = configs.Integer(default=10)
|
|
115
|
+
|
|
116
|
+
di = inject.Di()
|
|
117
|
+
|
|
118
|
+
_client: Any
|
|
119
|
+
|
|
120
|
+
@decorators.parameters_to_properties
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
endpoint="http://localhost:4000/graphql",
|
|
124
|
+
headers={},
|
|
125
|
+
authentication: Authentication | None = None,
|
|
126
|
+
timeout=10,
|
|
127
|
+
):
|
|
128
|
+
self.finalize_and_validate_configuration()
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def client(self) -> "Client":
|
|
132
|
+
"""
|
|
133
|
+
Get the underlying gql Client instance.
|
|
134
|
+
|
|
135
|
+
Lazily creates and caches a gql Client with the configured endpoint, authentication,
|
|
136
|
+
headers, and timeout. The client handles the HTTP transport layer for GraphQL requests.
|
|
137
|
+
|
|
138
|
+
This property is primarily used internally by the execute() method, but can be accessed
|
|
139
|
+
directly if you need lower-level control over GraphQL operations.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Client: A gql Client instance configured with this GraphqlClient's settings.
|
|
143
|
+
"""
|
|
144
|
+
from gql import Client
|
|
145
|
+
from gql.transport.requests import RequestsHTTPTransport
|
|
146
|
+
|
|
147
|
+
if hasattr(self, "_client"):
|
|
148
|
+
return self._client # type: ignore
|
|
149
|
+
|
|
150
|
+
if self.authentication:
|
|
151
|
+
# Inject dependencies if the authentication object supports it
|
|
152
|
+
if hasattr(self.authentication, "injectable_properties"):
|
|
153
|
+
self.authentication.injectable_properties(self.di) # type: ignore[attr-defined]
|
|
154
|
+
transport = RequestsHTTPTransport(
|
|
155
|
+
url=self.endpoint,
|
|
156
|
+
headers=self.headers,
|
|
157
|
+
auth=self.authentication,
|
|
158
|
+
timeout=self.timeout,
|
|
159
|
+
)
|
|
160
|
+
self._client = Client(transport=transport, fetch_schema_from_transport=False)
|
|
161
|
+
return self._client
|
|
162
|
+
|
|
163
|
+
def execute(self, query: str, variable_values: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
164
|
+
"""
|
|
165
|
+
Execute a GraphQL query or mutation.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
query (str): The GraphQL query or mutation string.
|
|
169
|
+
variable_values (dict, optional): Variables for the query/mutation. Defaults to None
|
|
170
|
+
Returns:
|
|
171
|
+
dict: The response data from the GraphQL API.
|
|
172
|
+
"""
|
|
173
|
+
from gql import gql
|
|
174
|
+
|
|
175
|
+
client = self.client
|
|
176
|
+
prepared_query = gql(query)
|
|
177
|
+
self.logger.debug(
|
|
178
|
+
f"Executing GraphQL query: {prepared_query} on endpoint: {self.endpoint} with variables: {variable_values}"
|
|
179
|
+
)
|
|
180
|
+
result = client.execute(prepared_query, variable_values=variable_values)
|
|
181
|
+
self.logger.debug(f"GraphQL response: {result}")
|
|
182
|
+
return result
|