clear-skies 1.22.1__py3-none-any.whl → 1.22.3__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-1.22.1.dist-info → clear_skies-1.22.3.dist-info}/METADATA +1 -1
- {clear_skies-1.22.1.dist-info → clear_skies-1.22.3.dist-info}/RECORD +7 -6
- clearskies/backends/api_get_only_backend.py +101 -0
- clearskies/column_types/belongs_to.py +29 -8
- clearskies/column_types/has_many.py +35 -2
- {clear_skies-1.22.1.dist-info → clear_skies-1.22.3.dist-info}/LICENSE +0 -0
- {clear_skies-1.22.1.dist-info → clear_skies-1.22.3.dist-info}/WHEEL +0 -0
|
@@ -47,6 +47,7 @@ clearskies/autodoc/schema/password.py,sha256=Ptj8OeddAL4h69KWqZ6ubZ2awR13xdDIrNe
|
|
|
47
47
|
clearskies/autodoc/schema/string.py,sha256=oxZPCxYYhWnNHdbtwD3QuniStbj8XbBBpDTFXgPR1VU,244
|
|
48
48
|
clearskies/backends/__init__.py,sha256=IqIpTNdA0U14wFNBkotwc9kZr_mMw3ry8LnldOZ0HLs,755
|
|
49
49
|
clearskies/backends/api_backend.py,sha256=PQyT00pMtZZUQhfWySzVbXZ2GpycO93CRKVOmUFeo10,15073
|
|
50
|
+
clearskies/backends/api_get_only_backend.py,sha256=OWXdoaSaF5fWb7GN1OgkGED3edKfoeOBMHc8bKtm2ig,3885
|
|
50
51
|
clearskies/backends/backend.py,sha256=fkL-De0MUdzcS2JG_spSUQZIVL9oRFvaL6SP26JPpcI,7399
|
|
51
52
|
clearskies/backends/cursor_backend.py,sha256=VntlPS6z6bnZOC3XRJ-WFf5gK3pFUhH_qJpnZn8hl9U,11278
|
|
52
53
|
clearskies/backends/example_backend.py,sha256=jVpv0LZpNUEJGko0XqioLkHmZHbCW6M4YyNvzKlZcDw,1413
|
|
@@ -58,7 +59,7 @@ clearskies/backends/secrets_backend.py,sha256=4lzrgdL_O_pgCT5HknV2gotFgp9GzjQ5_2
|
|
|
58
59
|
clearskies/binding_config.py,sha256=bF8LBNEgJacwKCqToAtDqN9hv5omzU7zt_4qB9KPtE0,457
|
|
59
60
|
clearskies/column_types/__init__.py,sha256=wofhLfyW00I6tb6o9DMsMx7j9hlbbqefhDzWfw0Row0,4731
|
|
60
61
|
clearskies/column_types/audit.py,sha256=2YcrZVVElpOUdmxYTQ_6CshL1HVou6fz65dOOs_b8Gw,9659
|
|
61
|
-
clearskies/column_types/belongs_to.py,sha256=
|
|
62
|
+
clearskies/column_types/belongs_to.py,sha256=E7Wi84vr2PnNw7TBCoZa8jkpwiJhO-iQmZ_ekq26kTs,12206
|
|
62
63
|
clearskies/column_types/boolean.py,sha256=1yyM1CUfgD84pPE65c1OP1Qjf_J0Z45hjPrDR51AUkQ,1878
|
|
63
64
|
clearskies/column_types/category_tree.py,sha256=kPx0fNTJxHaaEI_-0JxQ7NBcV2bYgUDGmtf1wmTqoEg,13172
|
|
64
65
|
clearskies/column_types/column.py,sha256=ftuDFswjk-KE9Frxo1rhgkjr4sjSjnUc5ZtfNrnGLIc,15530
|
|
@@ -73,7 +74,7 @@ clearskies/column_types/datetime.py,sha256=xtuZpUC9fA16i1oO80kPIx--8RDPuei9RdsDD
|
|
|
73
74
|
clearskies/column_types/datetime_micro.py,sha256=ewQSniCc2MmNIyX2XNuNcCIwh5Fpf1HcvpLfzB8lz8g,382
|
|
74
75
|
clearskies/column_types/email.py,sha256=qq0Yo_C3KxUqT68q2HWXocBBR4xwMqjxcIdgZRv218U,584
|
|
75
76
|
clearskies/column_types/float.py,sha256=j8jJeBueSOusPtAFCWgLHYBncfLnqT1U7bh1zcAkYiA,1332
|
|
76
|
-
clearskies/column_types/has_many.py,sha256=
|
|
77
|
+
clearskies/column_types/has_many.py,sha256=z1tco_KWLr8WqKk5X8HJwdfjxpT2WwWNb5hgwDHM2fU,7432
|
|
77
78
|
clearskies/column_types/has_one.py,sha256=uphIPUuHLwwmhljLMaKKPujR6TYTT7onn-hHUF6S_IY,2230
|
|
78
79
|
clearskies/column_types/integer.py,sha256=dGIluusPmhLRNg7PplOJLbQI2AXojqRBUHt8ekYWNVI,1386
|
|
79
80
|
clearskies/column_types/json.py,sha256=TbZkdwCoZYhbALUxof2jENGfaq2i5TlcyBcmo7XzDGQ,652
|
|
@@ -206,7 +207,7 @@ clearskies/tests/simple_api/models/__init__.py,sha256=nUA0W6fgXw_Bxa9CudkaDkC80t
|
|
|
206
207
|
clearskies/tests/simple_api/models/status.py,sha256=PEhPbaQh5qdUNHp8O0gz91LOLENAEBtqSaHxUPXchaM,699
|
|
207
208
|
clearskies/tests/simple_api/models/user.py,sha256=5_P4Tp1tTdX7PkMJ__epPM5MA7JAeVYGas69vcWloLc,819
|
|
208
209
|
clearskies/tests/simple_api/users_api.py,sha256=KYXCgEofDxHeRdQK67txN5oYUPvxxmB8JTku7L-apk4,2344
|
|
209
|
-
clear_skies-1.22.
|
|
210
|
-
clear_skies-1.22.
|
|
211
|
-
clear_skies-1.22.
|
|
212
|
-
clear_skies-1.22.
|
|
210
|
+
clear_skies-1.22.3.dist-info/LICENSE,sha256=3Ehd0g3YOpCj8sqj0Xjq5qbOtjjgk9qzhhD9YjRQgOA,1053
|
|
211
|
+
clear_skies-1.22.3.dist-info/METADATA,sha256=YtXu3sQzPtg-LoA92QMTlV0R6cQEuzkO_e5AXs_1JVw,1817
|
|
212
|
+
clear_skies-1.22.3.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
|
213
|
+
clear_skies-1.22.3.dist-info/RECORD,,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from .api_backend import ApiBackend
|
|
2
|
+
from typing import Any, Callable, Dict, List, Tuple
|
|
3
|
+
from .. import model
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ApiGetOnlyBackend(ApiBackend):
|
|
7
|
+
_requests = None
|
|
8
|
+
_auth = None
|
|
9
|
+
_id_column_name = None
|
|
10
|
+
|
|
11
|
+
_allowed_configs = [
|
|
12
|
+
"wheres",
|
|
13
|
+
"table_name",
|
|
14
|
+
"model_columns",
|
|
15
|
+
"select_all",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
def __init__(self, requests):
|
|
19
|
+
self._requests = requests
|
|
20
|
+
|
|
21
|
+
def configure(self, auth=None, origin=None, id_column_name="id"):
|
|
22
|
+
self._auth = auth
|
|
23
|
+
self._origin = origin
|
|
24
|
+
self._id_column_name = id_column_name
|
|
25
|
+
|
|
26
|
+
def records_url(self, configuration):
|
|
27
|
+
record_id = None
|
|
28
|
+
for where in configuration["wheres"]:
|
|
29
|
+
if where["column"] == "id":
|
|
30
|
+
record_id = where["values"][0]
|
|
31
|
+
if record_id:
|
|
32
|
+
return self.auth_origin + configuration["table_name"].strip("/") + f"/{record_id}"
|
|
33
|
+
return self.auth_origin + configuration["table_name"].strip("/") + "/search"
|
|
34
|
+
|
|
35
|
+
def delete_url(self, id: str, model: model.Model) -> str:
|
|
36
|
+
table_name = model.table_name().rstrip("/")
|
|
37
|
+
return f"{table_name}/{id}"
|
|
38
|
+
|
|
39
|
+
def update_url(self, id: str, model: model.Model) -> str:
|
|
40
|
+
table_name = model.table_name().rstrip("/")
|
|
41
|
+
return f"{table_name}/{id}"
|
|
42
|
+
|
|
43
|
+
def create_url(self, data: Dict[str, Any], model: model.Model) -> str:
|
|
44
|
+
return model.table_name().rstrip("/")
|
|
45
|
+
|
|
46
|
+
def records_method(self, configuration: Dict[str, Any]) -> str:
|
|
47
|
+
return "POST"
|
|
48
|
+
|
|
49
|
+
def count_method(self, configuration: Dict[str, Any]) -> str:
|
|
50
|
+
return "POST"
|
|
51
|
+
|
|
52
|
+
def _build_delete_request(self, id, model):
|
|
53
|
+
data = model.data
|
|
54
|
+
(url, data) = self._finalize_url_and_data(self.delete_url(id, model), data)
|
|
55
|
+
return [url, self.delete_method(id, model), {}, {}]
|
|
56
|
+
|
|
57
|
+
def _build_count_request(self, configuration):
|
|
58
|
+
[url, method, json_data, headers] = super()._build_count_request(configuration)
|
|
59
|
+
json_data["count_only"] = True
|
|
60
|
+
return [url, method, json_data, headers]
|
|
61
|
+
|
|
62
|
+
def records(self, configuration, model, next_page_data={}):
|
|
63
|
+
configuration = self._check_query_configuration(configuration)
|
|
64
|
+
[url, method, json_data, headers] = self._build_records_request(configuration)
|
|
65
|
+
response = self._execute_request(url, method, json=json_data, headers=headers).json()
|
|
66
|
+
records = self._map_records_response(response)
|
|
67
|
+
for next_page_key in ["nextPage", "NextPage", "next_page"]:
|
|
68
|
+
if response.get("pagination", {}).get(next_page_key):
|
|
69
|
+
for key, value in response["pagination"][next_page_key].items():
|
|
70
|
+
next_page_data[key] = value
|
|
71
|
+
return records
|
|
72
|
+
|
|
73
|
+
def _as_post_data(self, configuration):
|
|
74
|
+
data = {
|
|
75
|
+
"where": list(
|
|
76
|
+
map(lambda where: self._where_for_post(where, configuration["model_columns"]), configuration["wheres"])
|
|
77
|
+
),
|
|
78
|
+
"sort": configuration["sorts"],
|
|
79
|
+
"start": configuration["pagination"].get("start", 0),
|
|
80
|
+
"limit": configuration["limit"],
|
|
81
|
+
}
|
|
82
|
+
return {key: value for (key, value) in data.items() if value}
|
|
83
|
+
|
|
84
|
+
def _where_for_post(self, where, columns):
|
|
85
|
+
prefix = ""
|
|
86
|
+
if where.get("table"):
|
|
87
|
+
prefix = where["table"] + "."
|
|
88
|
+
return {
|
|
89
|
+
"column": prefix + where["column"],
|
|
90
|
+
"operator": where["operator"],
|
|
91
|
+
"value": self.normalize_outgoing_value(where, columns, where["values"][0]),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
def normalize_outgoing_value(self, where, columns, value):
|
|
95
|
+
column_name = where["column"]
|
|
96
|
+
if where.get("table") or column_name not in columns:
|
|
97
|
+
return value
|
|
98
|
+
normalized_data = self.column_to_backend(columns[column_name], {column_name: value})
|
|
99
|
+
if column_name in normalized_data:
|
|
100
|
+
return normalized_data[column_name]
|
|
101
|
+
return value
|
|
@@ -34,6 +34,7 @@ class BelongsTo(String):
|
|
|
34
34
|
"model_column_name",
|
|
35
35
|
"readable_parent_columns",
|
|
36
36
|
"join_type",
|
|
37
|
+
"where",
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
def __init__(self, di):
|
|
@@ -43,6 +44,7 @@ class BelongsTo(String):
|
|
|
43
44
|
super()._check_configuration(configuration)
|
|
44
45
|
self.validate_models_class(configuration["parent_models_class"])
|
|
45
46
|
|
|
47
|
+
error_prefix = f"Configuration error for '{self.name}' in '{self.model_class.__name__}':"
|
|
46
48
|
if not configuration.get("model_column_name") and self.name[-3:] != "_id":
|
|
47
49
|
raise ValueError(
|
|
48
50
|
f"Invalid name for column '{self.name}' in '{self.model_class.__name__}' - "
|
|
@@ -50,19 +52,14 @@ class BelongsTo(String):
|
|
|
50
52
|
+ "that the parent model can be fetched from."
|
|
51
53
|
)
|
|
52
54
|
if configuration.get("model_column_name") and type(configuration.get("model_column_name")) != str:
|
|
53
|
-
raise ValueError(
|
|
54
|
-
f"Configuration error for '{self.name}' in '{self.model_class.__name__}': 'model_column_name' must be a string."
|
|
55
|
-
)
|
|
55
|
+
raise ValueError(f"{error_prefix} 'model_column_name' must be a string.")
|
|
56
56
|
|
|
57
57
|
join_type = configuration.get("join_type")
|
|
58
58
|
if join_type and join_type.upper() not in ["LEFT", "INNER"]:
|
|
59
|
-
raise ValueError(
|
|
60
|
-
f"Configuration error for '{self.name}' in '{self.model_class.__name__}': join_type must be INNER or LEFT"
|
|
61
|
-
)
|
|
59
|
+
raise ValueError(f"{error_prefix} join_type must be INNER or LEFT")
|
|
62
60
|
|
|
63
61
|
if configuration.get("readable_parent_columns"):
|
|
64
62
|
parent_columns = self.di.build(configuration["parent_models_class"], cache=True).raw_columns_configuration()
|
|
65
|
-
error_prefix = f"Configuration error for '{self.name}' in '{self.model_class.__name__}':"
|
|
66
63
|
readable_parent_columns = configuration["readable_parent_columns"]
|
|
67
64
|
if not hasattr(readable_parent_columns, "__iter__"):
|
|
68
65
|
raise ValueError(
|
|
@@ -81,6 +78,19 @@ class BelongsTo(String):
|
|
|
81
78
|
+ "column does not exist in the model class."
|
|
82
79
|
)
|
|
83
80
|
|
|
81
|
+
wheres = configuration.get("where")
|
|
82
|
+
if wheres:
|
|
83
|
+
if not isinstance(wheres, list):
|
|
84
|
+
raise ValueError(
|
|
85
|
+
f"{error_prefix} 'where' must be a list of where conditions or callables that return where conditions"
|
|
86
|
+
)
|
|
87
|
+
for index, where in enumerate(wheres):
|
|
88
|
+
if callable(where) or isinstance(where, str):
|
|
89
|
+
continue
|
|
90
|
+
raise ValueError(
|
|
91
|
+
f"{error_prefix} 'where' must be a list of where conditions or callables that return where conditions, but the item in entry #${index+1} was neither a string nor a callable"
|
|
92
|
+
)
|
|
93
|
+
|
|
84
94
|
def _finalize_configuration(self, configuration):
|
|
85
95
|
return {
|
|
86
96
|
**super()._finalize_configuration(configuration),
|
|
@@ -89,6 +99,7 @@ class BelongsTo(String):
|
|
|
89
99
|
if configuration.get("model_column_name")
|
|
90
100
|
else self.name[:-3],
|
|
91
101
|
"join_type": configuration.get("join_type", "INNER").upper(),
|
|
102
|
+
"where": configuration.get("where", []),
|
|
92
103
|
},
|
|
93
104
|
}
|
|
94
105
|
|
|
@@ -149,7 +160,17 @@ class BelongsTo(String):
|
|
|
149
160
|
|
|
150
161
|
@property
|
|
151
162
|
def parent_models(self):
|
|
152
|
-
|
|
163
|
+
parents = self.di.build(self.config("parent_models_class"), cache=True)
|
|
164
|
+
for where in self.config("where"):
|
|
165
|
+
if callable(where):
|
|
166
|
+
parents = self.di.call_function(where, model=parents)
|
|
167
|
+
if not parents:
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f"Configuration error for column '{self.name}' in model '{self.model_class.__name__}': when 'where' is a callable, it must return a models class, but when the callable in where entry #{index+1} was called, it did not return the models class"
|
|
170
|
+
)
|
|
171
|
+
else:
|
|
172
|
+
parents = parents.where(where)
|
|
173
|
+
return parents
|
|
153
174
|
|
|
154
175
|
@property
|
|
155
176
|
def parent_columns(self):
|
|
@@ -27,6 +27,7 @@ class HasMany(Column):
|
|
|
27
27
|
"is_readable",
|
|
28
28
|
"readable_child_columns",
|
|
29
29
|
"parent_id_column_name",
|
|
30
|
+
"where",
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
def __init__(self, di):
|
|
@@ -63,9 +64,10 @@ class HasMany(Column):
|
|
|
63
64
|
|
|
64
65
|
def _check_configuration(self, configuration):
|
|
65
66
|
super()._check_configuration(configuration)
|
|
67
|
+
error_prefix = f"Configuration error for '{self.name}' in '{self.model_class.__name__}':"
|
|
68
|
+
|
|
66
69
|
if configuration.get("is_readable"):
|
|
67
70
|
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
71
|
if not "readable_child_columns" in configuration:
|
|
70
72
|
raise ValueError(f"{error_prefix} must provide 'readable_child_columns' if is_readable is set")
|
|
71
73
|
readable_child_columns = configuration["readable_child_columns"]
|
|
@@ -86,6 +88,27 @@ class HasMany(Column):
|
|
|
86
88
|
+ "column does not exist in the model class."
|
|
87
89
|
)
|
|
88
90
|
|
|
91
|
+
wheres = configuration.get("where")
|
|
92
|
+
if wheres:
|
|
93
|
+
if not isinstance(wheres, list):
|
|
94
|
+
raise ValueError(
|
|
95
|
+
f"{error_prefix} 'where' must be a list of where conditions or callables that return where conditions"
|
|
96
|
+
)
|
|
97
|
+
for index, where in enumerate(wheres):
|
|
98
|
+
if callable(where) or isinstance(where, str):
|
|
99
|
+
continue
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f"{error_prefix} 'where' must be a list of where conditions or callables that return where conditions, but the item in entry #${index+1} was neither a string nor a callable"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def _finalize_configuration(self, configuration):
|
|
105
|
+
return {
|
|
106
|
+
**super()._finalize_configuration(configuration),
|
|
107
|
+
**{
|
|
108
|
+
"where": configuration.get("where", []),
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
|
|
89
112
|
def get_child_columns(self):
|
|
90
113
|
if "child_columns" not in self.configuration:
|
|
91
114
|
self.configuration["child_columns"] = self.child_models.columns()
|
|
@@ -119,7 +142,17 @@ class HasMany(Column):
|
|
|
119
142
|
|
|
120
143
|
@property
|
|
121
144
|
def child_models(self):
|
|
122
|
-
|
|
145
|
+
children = self.di.build(self.config("child_models_class"), cache=True)
|
|
146
|
+
for index, where in enumerate(self.config("where")):
|
|
147
|
+
if callable(where):
|
|
148
|
+
children = self.di.call_function(where, model=children)
|
|
149
|
+
if not children:
|
|
150
|
+
raise ValueError(
|
|
151
|
+
f"Configuration error for column '{self.name}' in model '{self.model_class.__name__}': when 'where' is a callable, it must return a models class, but when the callable in where entry #{index+1} was called, it did not return the models class"
|
|
152
|
+
)
|
|
153
|
+
else:
|
|
154
|
+
children = children.where(where)
|
|
155
|
+
return children
|
|
123
156
|
|
|
124
157
|
def documentation(self, name=None, example=None, value=None):
|
|
125
158
|
columns = self.get_child_columns()
|
|
File without changes
|
|
File without changes
|