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.
Files changed (52) hide show
  1. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/PKG-INFO +1 -1
  2. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/query/generator.py +71 -63
  3. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/pyproject.toml +1 -1
  4. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/LICENSE +0 -0
  5. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/README.md +0 -0
  6. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/__init__.py +0 -0
  7. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/annotation/__init__.py +0 -0
  8. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/annotation/deprecate.py +0 -0
  9. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/environment.py +0 -0
  10. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/error.py +0 -0
  11. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/__init__.py +0 -0
  12. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/builder.py +0 -0
  13. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/dependable.py +0 -0
  14. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/docs.py +0 -0
  15. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/name.py +0 -0
  16. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/request.py +0 -0
  17. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/resource.py +0 -0
  18. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/response.py +0 -0
  19. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/router.py +0 -0
  20. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/schema.py +0 -0
  21. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fastapi/service.py +0 -0
  22. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/fluent.py +0 -0
  23. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/formatter.py +0 -0
  24. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/__init__.py +0 -0
  25. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/fake.py +0 -0
  26. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/fluent.py +0 -0
  27. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/httpx.py +0 -0
  28. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/json.py +0 -0
  29. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/http/url.py +0 -0
  30. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/id.py +0 -0
  31. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/key_fn.py +0 -0
  32. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/py.typed +0 -0
  33. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/query/__init__.py +0 -0
  34. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/query/query.py +0 -0
  35. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/__init__.py +0 -0
  36. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/base.py +0 -0
  37. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/connector.py +0 -0
  38. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/database.py +0 -0
  39. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/decorator.py +0 -0
  40. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/in_memory.py +0 -0
  41. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/interface.py +0 -0
  42. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/mongo.py +0 -0
  43. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/mssql.py +0 -0
  44. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/sql.py +0 -0
  45. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/repository/sqlite.py +0 -0
  46. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/server.py +0 -0
  47. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/synchronization.py +0 -0
  48. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/testing/__init__.py +0 -0
  49. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/testing/database.py +0 -0
  50. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/testing/fake.py +0 -0
  51. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/testing/rest.py +0 -0
  52. {apexdevkit-1.18.7 → apexdevkit-1.18.9}/apexdevkit/value.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apexdevkit
3
- Version: 1.18.7
3
+ Version: 1.18.9
4
4
  Summary: Apex Development Tools for python.
5
5
  Author: Apex Dev
6
6
  Author-email: dev@apex.ge
@@ -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
- translations: dict[str, str] = field(init=False)
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.translations = value
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.translations),
179
+ selection=MsSqlFooterGenerator(footer.aggregations, self._fields),
177
180
  filter=MsSqlSourceGenerator(self.source, footer.filter),
178
- condition=MsSqlConditionGenerator(footer.condition, self.translations),
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.translations),
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.alias or self.name
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
- translations: dict[str, str]
242
+ fields: list[MsSqlField]
237
243
 
238
244
  def generate(self) -> str:
239
- self._validate()
240
245
  fields = ", ".join(
241
246
  [
242
- f"{footer.aggregation.value}"
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 _validate(self) -> None:
253
- if self.translations.keys():
254
- for footer in self.aggregations:
255
- if footer.name and footer.name not in self.translations.keys():
256
- raise ForbiddenError(message=f"Invalid field name: {footer.name}")
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
- translations: dict[str, str]
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, self.translations
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
- translations: dict[str, str]
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.translations[node.name],
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 (
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "apexdevkit"
3
- version = "1.18.7"
3
+ version = "1.18.9"
4
4
  description = "Apex Development Tools for python."
5
5
  authors = ["Apex Dev <dev@apex.ge>"]
6
6
  readme = "README.md"
File without changes
File without changes