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.
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/PKG-INFO +1 -1
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/mssql.py +135 -22
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/pyproject.toml +1 -1
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/LICENSE +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/README.md +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/__init__.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/annotation/__init__.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/annotation/deprecate.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/environment.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/error.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/__init__.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/builder.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/dependable.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/docs.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/name.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/request.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/resource.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/response.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/router.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/schema.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fastapi/service.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/fluent.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/formatter.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/__init__.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/fake.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/fluent.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/httpx/__init__.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/httpx/client.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/httpx/hooks.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/json.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/http/url.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/id.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/key_fn.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/py.typed +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/query/__init__.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/query/generator.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/query/query.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/__init__.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/base.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/connector.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/database.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/decorator.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/in_memory.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/interface.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/sql.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/repository/sqlite.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/server.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/synchronization.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/testing/__init__.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/testing/database.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/testing/fake.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/testing/rest.py +0 -0
- {apexdevkit-1.21.1 → apexdevkit-1.21.2}/apexdevkit/value.py +0 -0
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
237
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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:
|
|
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
|
|
File without changes
|