apexdevkit 1.18.7__tar.gz → 1.18.9__tar.gz
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.
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/PKG-INFO +1 -1
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/query/generator.py +71 -63
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/pyproject.toml +1 -1
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/LICENSE +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/README.md +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/__init__.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/annotation/__init__.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/annotation/deprecate.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/environment.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/error.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/__init__.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/builder.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/dependable.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/docs.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/name.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/request.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/resource.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/response.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/router.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/schema.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/service.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fluent.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/formatter.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/__init__.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/fake.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/fluent.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/httpx.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/json.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/url.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/id.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/key_fn.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/py.typed +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/query/__init__.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/query/query.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/__init__.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/base.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/connector.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/database.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/decorator.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/in_memory.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/interface.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/mongo.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/mssql.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/sql.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/sqlite.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/server.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/synchronization.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/testing/__init__.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/testing/database.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/testing/fake.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/testing/rest.py +0 -0
- {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/value.py +0 -0
|
@@ -2,11 +2,12 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
-
from functools import cached_property
|
|
6
5
|
from typing import Any, ClassVar, Generic, Iterable, Protocol, TypeVar
|
|
7
6
|
|
|
7
|
+
from apexdevkit.annotation import deprecated
|
|
8
8
|
from apexdevkit.error import ForbiddenError
|
|
9
9
|
from apexdevkit.query.query import (
|
|
10
|
+
Aggregation,
|
|
10
11
|
AggregationOption,
|
|
11
12
|
Filter,
|
|
12
13
|
FooterOptions,
|
|
@@ -149,11 +150,7 @@ class MsSqlQuery:
|
|
|
149
150
|
class MsSqlQueryBuilder:
|
|
150
151
|
source: str = field(init=False)
|
|
151
152
|
username: str = field(init=False)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
@cached_property
|
|
155
|
-
def _fields(self) -> list[MsSqlField]:
|
|
156
|
-
return [MsSqlField(name, alias) for alias, name in self.translations.items()]
|
|
153
|
+
_fields: list[MsSqlField] = field(init=False)
|
|
157
154
|
|
|
158
155
|
def with_source(self, value: str) -> MsSqlQueryBuilder:
|
|
159
156
|
self.source = value
|
|
@@ -165,17 +162,23 @@ class MsSqlQueryBuilder:
|
|
|
165
162
|
|
|
166
163
|
return self
|
|
167
164
|
|
|
165
|
+
def with_fields(self, values: list[MsSqlField]) -> MsSqlQueryBuilder:
|
|
166
|
+
self._fields = values
|
|
167
|
+
|
|
168
|
+
return self
|
|
169
|
+
|
|
170
|
+
@deprecated(".with_translations is deprecated, use .with_fields instead")
|
|
168
171
|
def with_translations(self, value: dict[str, str]) -> MsSqlQueryBuilder:
|
|
169
|
-
self.
|
|
172
|
+
self._fields = [MsSqlField(name, alias) for alias, name in value.items()]
|
|
170
173
|
|
|
171
174
|
return self
|
|
172
175
|
|
|
173
176
|
def aggregate(self, footer: FooterOptions) -> DatabaseCommand:
|
|
174
177
|
return MsSqlQuery(
|
|
175
178
|
user=MsSqlUserGenerator(self.username),
|
|
176
|
-
selection=MsSqlFooterGenerator(footer.aggregations, self.
|
|
179
|
+
selection=MsSqlFooterGenerator(footer.aggregations, self._fields),
|
|
177
180
|
filter=MsSqlSourceGenerator(self.source, footer.filter),
|
|
178
|
-
condition=MsSqlConditionGenerator(footer.condition, self.
|
|
181
|
+
condition=MsSqlConditionGenerator(footer.condition, self._fields),
|
|
179
182
|
).generate()
|
|
180
183
|
|
|
181
184
|
def filter(self, options: QueryOptions) -> DatabaseCommand:
|
|
@@ -183,7 +186,7 @@ class MsSqlQueryBuilder:
|
|
|
183
186
|
user=MsSqlUserGenerator(self.username),
|
|
184
187
|
selection=MsSqlSelectionGenerator(self._fields),
|
|
185
188
|
filter=MsSqlSourceGenerator(self.source, options.filter),
|
|
186
|
-
condition=MsSqlConditionGenerator(options.condition, self.
|
|
189
|
+
condition=MsSqlConditionGenerator(options.condition, self._fields),
|
|
187
190
|
ordering=MsSqlOrderGenerator(options.ordering, self._fields),
|
|
188
191
|
paging=MsSqlPagingGenerator(options.paging),
|
|
189
192
|
).generate()
|
|
@@ -212,13 +215,16 @@ class MsSqlField:
|
|
|
212
215
|
return result
|
|
213
216
|
|
|
214
217
|
def as_order_part(self, is_descending: bool = False) -> str:
|
|
215
|
-
result = self.
|
|
218
|
+
result = self.name
|
|
216
219
|
|
|
217
220
|
if is_descending:
|
|
218
221
|
result += " DESC"
|
|
219
222
|
|
|
220
223
|
return result
|
|
221
224
|
|
|
225
|
+
def as_aggregation_part(self, option: Aggregation) -> str:
|
|
226
|
+
return f"{option.value}({self.name}) AS {self.alias}_{option.value.lower()}"
|
|
227
|
+
|
|
222
228
|
|
|
223
229
|
@dataclass
|
|
224
230
|
class MsSqlSelectionGenerator:
|
|
@@ -233,27 +239,55 @@ class MsSqlSelectionGenerator:
|
|
|
233
239
|
@dataclass
|
|
234
240
|
class MsSqlFooterGenerator:
|
|
235
241
|
aggregations: list[AggregationOption]
|
|
236
|
-
|
|
242
|
+
fields: list[MsSqlField]
|
|
237
243
|
|
|
238
244
|
def generate(self) -> str:
|
|
239
|
-
self._validate()
|
|
240
245
|
fields = ", ".join(
|
|
241
246
|
[
|
|
242
|
-
|
|
243
|
-
f"({self.translations[footer.name] if footer.name else '*'}) AS "
|
|
244
|
-
f"{footer.name if footer.name else 'general'}"
|
|
245
|
-
f"_{footer.aggregation.value.lower()}"
|
|
247
|
+
self.field_for(footer.name).as_aggregation_part(footer.aggregation)
|
|
246
248
|
for footer in self.aggregations
|
|
247
249
|
]
|
|
248
250
|
)
|
|
249
251
|
|
|
250
252
|
return f"SELECT {fields}"
|
|
251
253
|
|
|
252
|
-
def
|
|
253
|
-
if
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
def field_for(self, name: str | None) -> MsSqlField:
|
|
255
|
+
if name is None:
|
|
256
|
+
return MsSqlField("*", alias="general")
|
|
257
|
+
|
|
258
|
+
for f in self.fields:
|
|
259
|
+
if f.alias == name:
|
|
260
|
+
return f
|
|
261
|
+
|
|
262
|
+
raise ForbiddenError(message=f"Invalid field name: {name}")
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@dataclass
|
|
266
|
+
class MsSqlOrderGenerator:
|
|
267
|
+
ordering: list[Sort]
|
|
268
|
+
|
|
269
|
+
fields: list[MsSqlField] = field(default_factory=list)
|
|
270
|
+
|
|
271
|
+
def generate(self) -> str:
|
|
272
|
+
if not self.ordering:
|
|
273
|
+
return ""
|
|
274
|
+
|
|
275
|
+
clause = ", ".join(self.generate_one(item) for item in self.ordering)
|
|
276
|
+
|
|
277
|
+
return f"ORDER BY {clause}"
|
|
278
|
+
|
|
279
|
+
def generate_one(self, item: Sort) -> str:
|
|
280
|
+
return self.field_for(item.name).as_order_part(item.is_descending)
|
|
281
|
+
|
|
282
|
+
def field_for(self, name: str) -> MsSqlField:
|
|
283
|
+
for f in self.fields:
|
|
284
|
+
if f.alias == name:
|
|
285
|
+
return f
|
|
286
|
+
|
|
287
|
+
if f.alias == "" and f.name == name:
|
|
288
|
+
return f
|
|
289
|
+
|
|
290
|
+
raise ForbiddenError(message=f"Invalid field name: {name}")
|
|
257
291
|
|
|
258
292
|
|
|
259
293
|
@dataclass
|
|
@@ -280,12 +314,11 @@ class MsSqlSourceGenerator:
|
|
|
280
314
|
@dataclass
|
|
281
315
|
class MsSqlConditionGenerator:
|
|
282
316
|
condition: Operator | None
|
|
283
|
-
|
|
317
|
+
fields: list[MsSqlField]
|
|
284
318
|
|
|
285
319
|
def generate(self) -> str:
|
|
286
320
|
if self.condition is None:
|
|
287
321
|
return ""
|
|
288
|
-
self._validate(self.condition)
|
|
289
322
|
|
|
290
323
|
return f"WHERE ({self._traverse(self.condition)})"
|
|
291
324
|
|
|
@@ -306,47 +339,12 @@ class MsSqlConditionGenerator:
|
|
|
306
339
|
assert len(node.operands) == 1
|
|
307
340
|
|
|
308
341
|
return OperationEvaluator(
|
|
309
|
-
node.operation,
|
|
342
|
+
node.operation,
|
|
343
|
+
fields=self.fields,
|
|
310
344
|
).evaluate_for(
|
|
311
345
|
node.operands[0], # type: ignore
|
|
312
346
|
)
|
|
313
347
|
|
|
314
|
-
def _validate(self, condition: Operator | None) -> None:
|
|
315
|
-
if condition is not None and self.translations:
|
|
316
|
-
for operand in condition.operands:
|
|
317
|
-
if isinstance(operand, Operator):
|
|
318
|
-
self._validate(operand)
|
|
319
|
-
else:
|
|
320
|
-
if operand.name not in self.translations.keys():
|
|
321
|
-
raise ForbiddenError(
|
|
322
|
-
message=f"Invalid field name: {operand.name}"
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
@dataclass
|
|
327
|
-
class MsSqlOrderGenerator:
|
|
328
|
-
ordering: list[Sort]
|
|
329
|
-
|
|
330
|
-
fields: list[MsSqlField] = field(default_factory=list)
|
|
331
|
-
|
|
332
|
-
def generate(self) -> str:
|
|
333
|
-
if not self.ordering:
|
|
334
|
-
return ""
|
|
335
|
-
|
|
336
|
-
clause = ", ".join(self.generate_one(item) for item in self.ordering)
|
|
337
|
-
|
|
338
|
-
return f"ORDER BY {clause}"
|
|
339
|
-
|
|
340
|
-
def generate_one(self, item: Sort) -> str:
|
|
341
|
-
return self.field_for(item.name).as_order_part(item.is_descending)
|
|
342
|
-
|
|
343
|
-
def field_for(self, name: str) -> MsSqlField:
|
|
344
|
-
for f in self.fields:
|
|
345
|
-
if f.name == name:
|
|
346
|
-
return f
|
|
347
|
-
|
|
348
|
-
raise ForbiddenError(message=f"Invalid field name: {name}")
|
|
349
|
-
|
|
350
348
|
|
|
351
349
|
@dataclass
|
|
352
350
|
class MsSqlPagingGenerator:
|
|
@@ -365,7 +363,7 @@ class MsSqlPagingGenerator:
|
|
|
365
363
|
@dataclass
|
|
366
364
|
class OperationEvaluator:
|
|
367
365
|
operation: Operation
|
|
368
|
-
|
|
366
|
+
fields: list[MsSqlField]
|
|
369
367
|
|
|
370
368
|
_TEMPLATES: ClassVar[dict[Operation, str]] = defaultdict(
|
|
371
369
|
lambda: "[{column}] {operation} {a}",
|
|
@@ -385,13 +383,23 @@ class OperationEvaluator:
|
|
|
385
383
|
def evaluate_for(self, node: Leaf) -> str:
|
|
386
384
|
return self._TEMPLATES[self.operation].format(
|
|
387
385
|
operation=self.operation.value,
|
|
388
|
-
column=self.
|
|
386
|
+
column=self._column_for(node),
|
|
389
387
|
raw_a=self._get_raw_value(node),
|
|
390
388
|
a=self._get_value(node, 0),
|
|
391
389
|
b=self._get_value(node, 1),
|
|
392
390
|
values=", ".join(val.eval() for val in node.values),
|
|
393
391
|
)
|
|
394
392
|
|
|
393
|
+
def _column_for(self, node: Leaf) -> str:
|
|
394
|
+
for f in self.fields:
|
|
395
|
+
if f.alias == node.name:
|
|
396
|
+
return f.name
|
|
397
|
+
|
|
398
|
+
if f.alias == "" and f.name == node.name:
|
|
399
|
+
return f.name
|
|
400
|
+
|
|
401
|
+
raise ForbiddenError(message=f"Invalid field name: {node.name}")
|
|
402
|
+
|
|
395
403
|
def _get_raw_value(self, node: Leaf) -> str | None:
|
|
396
404
|
raw_a = self._get_value(node, 0)
|
|
397
405
|
return (
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|