apexdevkit 1.21.1__tar.gz → 1.21.2__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 (53) hide show
  1. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/PKG-INFO +1 -1
  2. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/mssql.py +135 -22
  3. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/pyproject.toml +1 -1
  4. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/LICENSE +0 -0
  5. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/README.md +0 -0
  6. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/__init__.py +0 -0
  7. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/annotation/__init__.py +0 -0
  8. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/annotation/deprecate.py +0 -0
  9. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/environment.py +0 -0
  10. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/error.py +0 -0
  11. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/__init__.py +0 -0
  12. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/builder.py +0 -0
  13. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/dependable.py +0 -0
  14. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/docs.py +0 -0
  15. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/name.py +0 -0
  16. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/request.py +0 -0
  17. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/resource.py +0 -0
  18. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/response.py +0 -0
  19. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/router.py +0 -0
  20. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/schema.py +0 -0
  21. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/service.py +0 -0
  22. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fluent.py +0 -0
  23. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/formatter.py +0 -0
  24. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/__init__.py +0 -0
  25. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/fake.py +0 -0
  26. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/fluent.py +0 -0
  27. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/httpx/__init__.py +0 -0
  28. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/httpx/client.py +0 -0
  29. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/httpx/hooks.py +0 -0
  30. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/json.py +0 -0
  31. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/url.py +0 -0
  32. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/id.py +0 -0
  33. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/key_fn.py +0 -0
  34. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/py.typed +0 -0
  35. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/query/__init__.py +0 -0
  36. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/query/generator.py +0 -0
  37. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/query/query.py +0 -0
  38. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/__init__.py +0 -0
  39. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/base.py +0 -0
  40. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/connector.py +0 -0
  41. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/database.py +0 -0
  42. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/decorator.py +0 -0
  43. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/in_memory.py +0 -0
  44. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/interface.py +0 -0
  45. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/sql.py +0 -0
  46. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/sqlite.py +0 -0
  47. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/server.py +0 -0
  48. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/synchronization.py +0 -0
  49. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/testing/__init__.py +0 -0
  50. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/testing/database.py +0 -0
  51. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/testing/fake.py +0 -0
  52. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/testing/rest.py +0 -0
  53. {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/value.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: apexdevkit
3
- Version: 1.21.1
3
+ Version: 1.21.2
4
4
  Summary: Apex Development Tools for python.
5
5
  Author: Apex Dev
6
6
  Author-email: dev@apex.ge
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from dataclasses import dataclass
4
- from typing import Any, Generic, Iterable, Iterator, Mapping, TypeVar
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Callable, Generic, Iterable, Iterator, Mapping, TypeVar
5
5
 
6
6
  from pymssql.exceptions import DatabaseError
7
7
 
@@ -142,35 +142,72 @@ class UnknownError(Exception):
142
142
  class MsSqlTableBuilder(Generic[ItemT]):
143
143
  username: str | None = None
144
144
  schema: str | None = None
145
- table: str | None = None
145
+ tables: list[str] = field(default_factory=list)
146
146
  formatter: Formatter[Mapping[str, Any], ItemT] | None = None
147
147
  fields: list[_SqlField] | None = None
148
+ table_mapper: Callable[[ItemT], str] | None = None
149
+ table_id_mapper: Callable[[str], str] | None = None
148
150
 
149
151
  def with_username(self, value: str) -> MsSqlTableBuilder[ItemT]:
150
152
  return MsSqlTableBuilder[ItemT](
151
153
  value,
152
154
  self.schema,
153
- self.table,
155
+ self.tables,
154
156
  self.formatter,
155
157
  self.fields,
158
+ self.table_mapper,
159
+ self.table_id_mapper,
156
160
  )
157
161
 
158
162
  def with_schema(self, value: str) -> MsSqlTableBuilder[ItemT]:
159
163
  return MsSqlTableBuilder[ItemT](
160
164
  self.username,
161
165
  value,
162
- self.table,
166
+ self.tables,
163
167
  self.formatter,
164
168
  self.fields,
169
+ self.table_mapper,
170
+ self.table_id_mapper,
165
171
  )
166
172
 
167
173
  def with_table(self, value: str) -> MsSqlTableBuilder[ItemT]:
168
174
  return MsSqlTableBuilder[ItemT](
169
175
  self.username,
170
176
  self.schema,
177
+ self.tables + [value],
178
+ self.formatter,
179
+ self.fields,
180
+ self.table_mapper,
181
+ self.table_id_mapper,
182
+ )
183
+
184
+ def and_table(self, value: str) -> MsSqlTableBuilder[ItemT]:
185
+ return self.with_table(value)
186
+
187
+ def with_table_mapper(
188
+ self, value: Callable[[ItemT], str]
189
+ ) -> MsSqlTableBuilder[ItemT]:
190
+ return MsSqlTableBuilder[ItemT](
191
+ self.username,
192
+ self.schema,
193
+ self.tables,
194
+ self.formatter,
195
+ self.fields,
171
196
  value,
197
+ self.table_id_mapper,
198
+ )
199
+
200
+ def with_table_id_mapper(
201
+ self, value: Callable[[str], str]
202
+ ) -> MsSqlTableBuilder[ItemT]:
203
+ return MsSqlTableBuilder[ItemT](
204
+ self.username,
205
+ self.schema,
206
+ self.tables,
172
207
  self.formatter,
173
208
  self.fields,
209
+ self.table_mapper,
210
+ value,
174
211
  )
175
212
 
176
213
  def with_formatter(
@@ -179,9 +216,11 @@ class MsSqlTableBuilder(Generic[ItemT]):
179
216
  return MsSqlTableBuilder[ItemT](
180
217
  self.username,
181
218
  self.schema,
182
- self.table,
219
+ self.tables,
183
220
  value,
184
221
  self.fields,
222
+ self.table_mapper,
223
+ self.table_id_mapper,
185
224
  )
186
225
 
187
226
  def with_fields(self, value: Iterable[_SqlField]) -> MsSqlTableBuilder[ItemT]:
@@ -204,42 +243,64 @@ class MsSqlTableBuilder(Generic[ItemT]):
204
243
  return MsSqlTableBuilder[ItemT](
205
244
  self.username,
206
245
  self.schema,
207
- self.table,
246
+ self.tables,
208
247
  self.formatter,
209
248
  key_list,
249
+ self.table_mapper,
250
+ self.table_id_mapper,
210
251
  )
211
252
 
212
253
  def build(self) -> SqlTable[ItemT]:
213
- if not self.schema or not self.table or not self.formatter or not self.fields:
254
+ if (
255
+ not self.schema
256
+ or len(self.tables) < 1
257
+ or not self.formatter
258
+ or not self.fields
259
+ ):
214
260
  raise ValueError("Cannot build sql table.")
215
261
 
216
262
  return DefaultSqlTable(
217
263
  self.schema,
218
- self.table,
264
+ self.tables,
219
265
  self.formatter,
220
266
  SqlFieldManager.Builder().with_fields(self.fields).for_mssql().build(),
221
267
  self.username,
268
+ self.table_mapper,
269
+ self.table_id_mapper,
222
270
  )
223
271
 
224
272
 
225
273
  @dataclass(frozen=True)
226
274
  class DefaultSqlTable(SqlTable[ItemT]):
227
275
  schema: str
228
- table: str
276
+ tables: list[str]
229
277
  formatter: Formatter[Mapping[str, Any], ItemT]
230
278
  fields: SqlFieldManager
231
279
  username: str | None = None
280
+ table_mapper: Callable[[ItemT], str] | None = None
281
+ table_id_mapper: Callable[[str], str] | None = None
232
282
 
233
283
  def count_all(self) -> DatabaseCommand:
284
+ selections = " + ".join(
285
+ [
286
+ f"""(
287
+ SELECT COUNT(*)
288
+ FROM [{self.schema}].[{table}]
289
+ {self.fields.where_statement(include_id=False)}
290
+ )"""
291
+ for table in self.tables
292
+ ]
293
+ )
234
294
  return DatabaseCommand(f"""
235
295
  {self._user_check}
236
- SELECT count(*) AS n_items
237
- FROM [{self.schema}].[{self.table}]
238
- {self.fields.where_statement(include_id=False)}
296
+ SELECT
297
+ {selections} AS n_items
239
298
  REVERT
240
299
  """).with_data(self.fields.with_fixed({}))
241
300
 
242
301
  def insert(self, item: ItemT) -> DatabaseCommand:
302
+ self._require_mapper_if_necessary()
303
+
243
304
  columns = ", ".join(
244
305
  ["[" + field.name + "]" for field in self.fields if field.include_in_insert]
245
306
  )
@@ -252,7 +313,7 @@ class DefaultSqlTable(SqlTable[ItemT]):
252
313
  ["[" + field.name + "] AS " + field.name for field in self.fields]
253
314
  )
254
315
  where_statement = f"""
255
- FROM [{self.schema}].[{self.table}]
316
+ FROM [{self.schema}].[{self._determine_table_by_item(item)}]
256
317
  {self.fields.where_statement(include_id=True, read_id=True)}
257
318
  """
258
319
  except ValueError:
@@ -263,7 +324,7 @@ class DefaultSqlTable(SqlTable[ItemT]):
263
324
 
264
325
  return DatabaseCommand(f"""
265
326
  {self._user_check}
266
- INSERT INTO [{self.schema}].[{self.table}] (
327
+ INSERT INTO [{self.schema}].[{self._determine_table_by_item(item)}] (
267
328
  {columns}
268
329
  )
269
330
  VALUES (
@@ -276,13 +337,15 @@ class DefaultSqlTable(SqlTable[ItemT]):
276
337
  """).with_data(self.fields.with_fixed(self.formatter.dump(item)))
277
338
 
278
339
  def select(self, item_id: str) -> DatabaseCommand:
340
+ self._require_id_mapper_if_necessary()
341
+
279
342
  columns = ", ".join(["[" + field.name + "]" for field in self.fields])
280
343
 
281
344
  return DatabaseCommand(f"""
282
345
  {self._user_check}
283
346
  SELECT
284
347
  {columns}
285
- FROM [{self.schema}].[{self.table}]
348
+ FROM [{self.schema}].[{self._determine_table_by_id(item_id)}]
286
349
  {self.fields.where_statement(include_id=True)}
287
350
  REVERT
288
351
  """).with_data(self.fields.with_fixed({self.fields.id: item_id}))
@@ -290,17 +353,28 @@ class DefaultSqlTable(SqlTable[ItemT]):
290
353
  def select_all(self) -> DatabaseCommand:
291
354
  columns = ", ".join(["[" + field.name + "]" for field in self.fields])
292
355
 
356
+ selections = """
357
+ UNION ALL
358
+ """.join(
359
+ [
360
+ f"""SELECT
361
+ {columns}
362
+ FROM [{self.schema}].[{table}]"""
363
+ for table in self.tables
364
+ ]
365
+ )
366
+
293
367
  return DatabaseCommand(f"""
294
368
  {self._user_check}
295
- SELECT
296
- {columns}
297
- FROM [{self.schema}].[{self.table}]
369
+ {selections}
298
370
  {self.fields.where_statement(include_id=False)}
299
371
  {self.fields.order}
300
372
  REVERT
301
373
  """).with_data(self.fields.with_fixed({}))
302
374
 
303
375
  def update(self, item: ItemT) -> DatabaseCommand:
376
+ self._require_mapper_if_necessary()
377
+
304
378
  updates = ", ".join(
305
379
  [
306
380
  f"{field.name} = %({field.name})s"
@@ -311,7 +385,7 @@ class DefaultSqlTable(SqlTable[ItemT]):
311
385
 
312
386
  return DatabaseCommand(f"""
313
387
  {self._user_check}
314
- UPDATE [{self.schema}].[{self.table}]
388
+ UPDATE [{self.schema}].[{self._determine_table_by_item(item)}]
315
389
  SET
316
390
  {updates}
317
391
  {self.fields.where_statement(include_id=True)}
@@ -319,19 +393,26 @@ class DefaultSqlTable(SqlTable[ItemT]):
319
393
  """).with_data(self.fields.with_fixed(self.formatter.dump(item)))
320
394
 
321
395
  def delete(self, item_id: str) -> DatabaseCommand:
396
+ self._require_mapper_if_necessary()
397
+
322
398
  return DatabaseCommand(f"""
323
399
  {self._user_check}
324
400
  DELETE
325
- FROM [{self.schema}].[{self.table}]
401
+ FROM [{self.schema}].[{self._determine_table_by_id(item_id)}]
326
402
  {self.fields.where_statement(include_id=True)}
327
403
  REVERT
328
404
  """).with_data(self.fields.with_fixed({self.fields.id: item_id}))
329
405
 
330
406
  def delete_all(self) -> DatabaseCommand:
407
+ if len(self.tables) > 1:
408
+ raise RuntimeError(
409
+ f"Deletion of multiple tables {self.tables} not supported"
410
+ )
411
+
331
412
  return DatabaseCommand(f"""
332
413
  {self._user_check}
333
414
  DELETE
334
- FROM [{self.schema}].[{self.table}]
415
+ FROM [{self.schema}].[{self.tables[0]}]
335
416
  {self.fields.where_statement(include_id=False)}
336
417
  REVERT
337
418
  """).with_data(self.fields.with_fixed({}))
@@ -345,6 +426,38 @@ class DefaultSqlTable(SqlTable[ItemT]):
345
426
  lambda i: f"{self.fields.id}<{raw[self.fields.id]}>"
346
427
  )
347
428
 
429
+ def _require_mapper_if_necessary(self) -> None:
430
+ if len(self.tables) > 1 and self.table_mapper is None:
431
+ raise RuntimeError(
432
+ f"Attempt to use multiple tables {self.tables} "
433
+ f"on insert or update when mapping not provided"
434
+ )
435
+
436
+ def _require_id_mapper_if_necessary(self) -> None:
437
+ if len(self.tables) > 1 and self.table_id_mapper is None:
438
+ raise RuntimeError(
439
+ f"Attempt to use multiple tables {self.tables} "
440
+ f"on read or delete when id mapping not provided"
441
+ )
442
+
443
+ def _determine_table_by_item(self, item: ItemT) -> str:
444
+ if len(self.tables) == 1 or self.table_mapper is None:
445
+ return self.tables[0]
446
+ else:
447
+ table = self.table_mapper(item)
448
+ if table not in self.tables:
449
+ raise RuntimeError(f"Illegal table {table} provided")
450
+ return table
451
+
452
+ def _determine_table_by_id(self, identifier: str) -> str:
453
+ if len(self.tables) == 1 or self.table_id_mapper is None:
454
+ return self.tables[0]
455
+ else:
456
+ table = self.table_id_mapper(identifier)
457
+ if table not in self.tables:
458
+ raise RuntimeError(f"Illegal table {table} provided")
459
+ return table
460
+
348
461
  @property
349
462
  def _user_check(self) -> str:
350
463
  if self.username is not None:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "apexdevkit"
3
- version = "1.21.1"
3
+ version = "1.21.2"
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