alpha-python 0.1.0__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.
Files changed (62) hide show
  1. alpha/__init__.py +0 -0
  2. alpha/adapters/__init__.py +0 -0
  3. alpha/adapters/sqla_unit_of_work.py +120 -0
  4. alpha/domain/__init__.py +0 -0
  5. alpha/domain/models/__init__.py +0 -0
  6. alpha/domain/models/base_model.py +25 -0
  7. alpha/encoder.py +62 -0
  8. alpha/exceptions.py +99 -0
  9. alpha/factories/__init__.py +0 -0
  10. alpha/factories/_type_conversion_matrix.py +233 -0
  11. alpha/factories/_type_mapping.py +29 -0
  12. alpha/factories/class_factories.py +496 -0
  13. alpha/factories/default_field_factory.py +50 -0
  14. alpha/factories/field_iterator.py +188 -0
  15. alpha/factories/logging_handler_factory.py +86 -0
  16. alpha/factories/model_class_factory.py +176 -0
  17. alpha/factories/models/__init__.py +0 -0
  18. alpha/factories/models/factory_classes.py +20 -0
  19. alpha/factories/request_factory.py +211 -0
  20. alpha/factories/response_factory.py +186 -0
  21. alpha/factories/type_factories.py +204 -0
  22. alpha/infra/__init__.py +0 -0
  23. alpha/infra/database/__init__.py +0 -0
  24. alpha/infra/database/sql_alchemy_database.py +159 -0
  25. alpha/infra/database/sql_alchemy_view.py +48 -0
  26. alpha/infra/models/__init__.py +0 -0
  27. alpha/infra/models/filter_operators.py +98 -0
  28. alpha/infra/models/json_patch.py +21 -0
  29. alpha/infra/models/order_by.py +69 -0
  30. alpha/infra/models/query_clause.py +45 -0
  31. alpha/infra/models/search_filter.py +586 -0
  32. alpha/interfaces/__init__.py +0 -0
  33. alpha/interfaces/attrs_instance.py +10 -0
  34. alpha/interfaces/dataclass_instance.py +11 -0
  35. alpha/interfaces/factories.py +102 -0
  36. alpha/interfaces/openapi_model.py +21 -0
  37. alpha/interfaces/patchable.py +8 -0
  38. alpha/interfaces/sql_database.py +36 -0
  39. alpha/interfaces/sql_mapper.py +23 -0
  40. alpha/interfaces/sql_repository.py +380 -0
  41. alpha/interfaces/token_factory.py +56 -0
  42. alpha/interfaces/unit_of_work.py +53 -0
  43. alpha/interfaces/updateable.py +7 -0
  44. alpha/py.typed +0 -0
  45. alpha/repositories/__init__.py +0 -0
  46. alpha/repositories/default_sql_repository.py +679 -0
  47. alpha/repositories/models/__init__.py +0 -0
  48. alpha/repositories/models/repository_model.py +16 -0
  49. alpha/services/__init__.py +0 -0
  50. alpha/services/authentication_service.py +71 -0
  51. alpha/utils/__init__.py +0 -0
  52. alpha/utils/_http_codes.py +148 -0
  53. alpha/utils/is_attrs.py +18 -0
  54. alpha/utils/logging_configurator.py +133 -0
  55. alpha/utils/logging_level_checker.py +26 -0
  56. alpha/utils/response_object.py +26 -0
  57. alpha/utils/version_check.py +17 -0
  58. alpha_python-0.1.0.dist-info/METADATA +22 -0
  59. alpha_python-0.1.0.dist-info/RECORD +62 -0
  60. alpha_python-0.1.0.dist-info/WHEEL +5 -0
  61. alpha_python-0.1.0.dist-info/licenses/LICENSE +21 -0
  62. alpha_python-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,679 @@
1
+ """Contains the DefaultSqlRepository implementation which provides
2
+ basic CRUD operations for domain models using SqlAlchemy."""
3
+
4
+ import json
5
+ import logging
6
+ from enum import Enum
7
+ from typing import Any, Generic, Iterable, cast
8
+ from uuid import UUID
9
+
10
+ from sqlalchemy import BinaryExpression, ColumnElement, ColumnOperators
11
+ from sqlalchemy.exc import IntegrityError
12
+ from sqlalchemy.orm import (
13
+ Query,
14
+ Session,
15
+ )
16
+ from sqlalchemy.orm.attributes import InstrumentedAttribute
17
+ from sqlalchemy.sql.elements import UnaryExpression
18
+
19
+ from alpha import exceptions
20
+ from alpha.domain.models.base_model import (
21
+ BaseDomainModel,
22
+ DomainModel,
23
+ )
24
+ from alpha.encoder import JSONEncoder
25
+ from alpha.infra.models.order_by import OrderBy
26
+ from alpha.infra.models.search_filter import SearchFilter
27
+ from alpha.infra.models.query_clause import QueryClause
28
+ from alpha.infra.models.filter_operators import FilterOperator
29
+ from alpha.infra.models.json_patch import JsonPatch
30
+ from alpha.interfaces.updateable import Updateable
31
+ from alpha.utils.logging_level_checker import logging_level_checker as llc
32
+
33
+
34
+ class DefaultSqlRepository(Generic[DomainModel]):
35
+ """SqlAlchemy default repository implementation. Provides basic
36
+ CRUD operations for domain models.
37
+
38
+ The repository uses a SqlAlchemy session to interact with the database. It
39
+ requires a default domain model type to be specified which will be used
40
+ for operations where no specific model type is provided. The following
41
+ operations are supported:
42
+ - add
43
+ - add_all
44
+ - count
45
+ - get
46
+ - get_all
47
+ - get_one
48
+ - get_one_or_none
49
+ - get_by_id
50
+ - patch
51
+ - remove
52
+ - remove_all
53
+ - select
54
+ - update
55
+ - view
56
+
57
+ You can also extend this repository to add custom methods by inheriting
58
+ from it and adding your own methods.
59
+
60
+ Example:
61
+ ```python
62
+ class CustomRepository(DefaultSqlRepository[MyDomainModel]):
63
+ def custom_method(self, param: str) -> list[MyDomainModel]:
64
+ # Custom query logic here
65
+ pass
66
+ ```
67
+ """
68
+
69
+ def __init__(self, session: Session, default_model: DomainModel) -> None:
70
+ """_summary_
71
+
72
+ Parameters
73
+ ----------
74
+ session : Session
75
+ _description_
76
+ default_model : DomainModel
77
+ _description_
78
+ """
79
+ self.session = session
80
+ self._default_model = default_model
81
+
82
+ def add(
83
+ self,
84
+ obj: DomainModel,
85
+ return_obj: bool = True,
86
+ raise_if_exists: bool = False,
87
+ ) -> DomainModel | None:
88
+ """_summary_
89
+
90
+ Parameters
91
+ ----------
92
+ obj : DomainModel
93
+ _description_
94
+ return_obj : bool, optional
95
+ _description_, by default True
96
+ raise_if_exists : bool, optional
97
+ _description_, by default False
98
+
99
+ Returns
100
+ -------
101
+ DomainModel | None
102
+ _description_
103
+
104
+ Raises
105
+ ------
106
+ exceptions.AlreadyExistsException
107
+ _description_
108
+ """
109
+ try:
110
+ self.session.add(obj)
111
+ self.session.flush()
112
+ if return_obj:
113
+ self.session.refresh(obj)
114
+ if llc("debug"):
115
+ logging.debug(
116
+ "added object to database session: %s",
117
+ json.dumps(obj, cls=JSONEncoder),
118
+ )
119
+ logging.debug("flushed pending transaction to session")
120
+ if return_obj:
121
+ if llc("debug"):
122
+ logging.debug(
123
+ "refreshed object: %s",
124
+ json.dumps(obj, cls=JSONEncoder),
125
+ )
126
+ return obj
127
+ except IntegrityError as exc:
128
+ self.session.rollback()
129
+ if llc("debug"):
130
+ logging.debug("rolled back pending transaction from session")
131
+ if raise_if_exists:
132
+ raise exceptions.AlreadyExistsException(exc)
133
+ return None
134
+
135
+ def add_all(
136
+ self,
137
+ objs: list[DomainModel],
138
+ return_obj: bool = False,
139
+ raise_if_exists: bool = False,
140
+ ) -> list[DomainModel] | None:
141
+ """_summary_
142
+
143
+ Parameters
144
+ ----------
145
+ objs : list[DomainModel]
146
+ _description_
147
+ return_obj : bool, optional
148
+ _description_, by default False
149
+ raise_if_exists : bool, optional
150
+ _description_, by default False
151
+
152
+ Returns
153
+ -------
154
+ list[DomainModel] | None
155
+ _description_
156
+
157
+ Raises
158
+ ------
159
+ exceptions.AlreadyExistsException
160
+ _description_
161
+ """
162
+ if return_obj:
163
+ objects: list[DomainModel] | None = []
164
+ for obj in objs:
165
+ object_ = self.add(
166
+ obj=obj,
167
+ return_obj=return_obj,
168
+ raise_if_exists=raise_if_exists,
169
+ )
170
+ objects.append(object_) # type: ignore
171
+ return objects
172
+ try:
173
+ self.session.bulk_save_objects(objs)
174
+ if llc("debug"):
175
+ logging.debug(
176
+ "bulk added objects to database session: %s",
177
+ json.dumps(objs, cls=JSONEncoder),
178
+ )
179
+ self.session.flush()
180
+ if llc("debug"):
181
+ logging.debug("flushed pending transactions to session")
182
+ except IntegrityError as exc:
183
+ self.session.rollback()
184
+ if llc("debug"):
185
+ logging.debug("rolled back pending transaction from session")
186
+ if raise_if_exists:
187
+ raise exceptions.AlreadyExistsException(exc)
188
+ for obj in objs:
189
+ self.add(obj)
190
+ return None
191
+
192
+ def count(
193
+ self,
194
+ model: DomainModel | None = None,
195
+ **kwargs: Any,
196
+ ) -> int:
197
+ """_summary_
198
+
199
+ Parameters
200
+ ----------
201
+ model : DomainModel | None, optional
202
+ _description_, by default None
203
+
204
+ Returns
205
+ -------
206
+ int
207
+ _description_
208
+ """
209
+ return self._query(cursor_result="count", model=model, **kwargs) # type: ignore
210
+
211
+ def get(
212
+ self,
213
+ attr: str | InstrumentedAttribute[Any],
214
+ value: str | int | float | Enum | UUID,
215
+ cursor_result: str = "first",
216
+ model: DomainModel | None = None,
217
+ **kwargs: Any,
218
+ ) -> DomainModel:
219
+ """_summary_
220
+
221
+ Parameters
222
+ ----------
223
+ attr : str | InstrumentedAttribute
224
+ _description_
225
+ value : str | int | float | Enum | UUID
226
+ _description_
227
+ cursor_result : str, optional
228
+ _description_, by default "first"
229
+ model : DomainModel | None, optional
230
+ _description_, by default None
231
+
232
+ Returns
233
+ -------
234
+ DomainModel
235
+ _description_
236
+ """
237
+
238
+ if isinstance(attr, InstrumentedAttribute):
239
+ attr = attr.key
240
+ return self._query(
241
+ cursor_result=cursor_result,
242
+ filter_by={attr: value},
243
+ model=model,
244
+ **kwargs, # type: ignore
245
+ )
246
+
247
+ def get_all(
248
+ self,
249
+ attr: str | InstrumentedAttribute[Any],
250
+ value: str | int | float | Enum | UUID,
251
+ cursor_result: str = "all",
252
+ model: DomainModel | None = None,
253
+ **kwargs: Any,
254
+ ) -> list[DomainModel]:
255
+ """_summary_
256
+
257
+ Parameters
258
+ ----------
259
+ attr : str | InstrumentedAttribute
260
+ _description_
261
+ value : str | int | float | Enum | UUID
262
+ _description_
263
+ cursor_result : str, optional
264
+ _description_, by default "all"
265
+ model : DomainModel | None, optional
266
+ _description_, by default None
267
+
268
+ Returns
269
+ -------
270
+ list[DomainModel]
271
+ _description_
272
+ """
273
+ objs = self.get(
274
+ attr=attr,
275
+ value=value,
276
+ cursor_result=cursor_result,
277
+ model=model,
278
+ **kwargs,
279
+ )
280
+ return objs # type: ignore
281
+
282
+ def get_one(
283
+ self,
284
+ attr: str | InstrumentedAttribute[Any],
285
+ value: str | int | float | Enum | UUID,
286
+ cursor_result: str = "one",
287
+ model: DomainModel | None = None,
288
+ **kwargs: Any,
289
+ ) -> DomainModel:
290
+ """_summary_
291
+
292
+ Parameters
293
+ ----------
294
+ attr : str | InstrumentedAttribute
295
+ _description_
296
+ value : str | int | float | Enum | UUID
297
+ _description_
298
+ cursor_result : str, optional
299
+ _description_, by default "one"
300
+ model : DomainModel | None, optional
301
+ _description_, by default None
302
+
303
+ Returns
304
+ -------
305
+ DomainModel
306
+ _description_
307
+ """
308
+ return self.get(
309
+ attr=attr,
310
+ value=value,
311
+ cursor_result=cursor_result,
312
+ model=model,
313
+ **kwargs,
314
+ )
315
+
316
+ def get_one_or_none(
317
+ self,
318
+ attr: str | InstrumentedAttribute[Any],
319
+ value: str | int | float | Enum | UUID,
320
+ cursor_result: str = "one_or_none",
321
+ model: DomainModel | None = None,
322
+ **kwargs: Any,
323
+ ) -> DomainModel | None:
324
+ """_summary_
325
+
326
+ Parameters
327
+ ----------
328
+ attr : str | InstrumentedAttribute
329
+ _description_
330
+ value : str | int | float | Enum | UUID
331
+ _description_
332
+ cursor_result : str, optional
333
+ _description_, by default "one_or_none"
334
+ model : DomainModel | None, optional
335
+ _description_, by default None
336
+
337
+ Returns
338
+ -------
339
+ DomainModel | None
340
+ _description_
341
+ """
342
+ return self.get(
343
+ attr=attr,
344
+ value=value,
345
+ cursor_result=cursor_result,
346
+ model=model,
347
+ **kwargs,
348
+ )
349
+
350
+ def get_by_id(
351
+ self,
352
+ value: str | int | UUID,
353
+ attr: str | InstrumentedAttribute[Any] = "id",
354
+ cursor_result: str = "one_or_none",
355
+ model: DomainModel | None = None,
356
+ **kwargs: Any,
357
+ ) -> DomainModel | None:
358
+ """_summary_
359
+
360
+ Parameters
361
+ ----------
362
+ value : str | int | UUID
363
+ _description_
364
+ attr : str | InstrumentedAttribute, optional
365
+ _description_, by default "id"
366
+ cursor_result : str, optional
367
+ _description_, by default "one_or_none"
368
+ model : DomainModel | None, optional
369
+ _description_, by default None
370
+
371
+ Returns
372
+ -------
373
+ DomainModel | None
374
+ _description_
375
+ """
376
+ return self.get(
377
+ attr=attr,
378
+ value=value,
379
+ cursor_result=cursor_result,
380
+ model=model,
381
+ **kwargs,
382
+ )
383
+
384
+ def patch(
385
+ self, obj: BaseDomainModel, patches: JsonPatch
386
+ ) -> BaseDomainModel:
387
+ """Patch a domain model object using a JSON patch document.
388
+
389
+ Parameters
390
+ ----------
391
+ obj
392
+ Patchable object to be patched.
393
+ patches
394
+ JSON patch document containing the changes to apply.
395
+
396
+ Returns
397
+ -------
398
+ DomainModel
399
+ Patched object.
400
+ """
401
+ if not hasattr(obj, "patch"):
402
+ raise TypeError("Object does not support patch operation")
403
+ patched = obj.patch(patches) # type: ignore[attr-defined]
404
+ self.session.flush()
405
+ return cast(BaseDomainModel, patched)
406
+
407
+ def remove(self, obj: DomainModel) -> None:
408
+ """_summary_
409
+
410
+ Parameters
411
+ ----------
412
+ obj : DomainModel
413
+ _description_
414
+ """
415
+ self.session.delete(obj)
416
+ self.session.flush()
417
+
418
+ def remove_all(
419
+ self,
420
+ objs: list[DomainModel] | None = None,
421
+ **kwargs: Any,
422
+ ) -> None:
423
+ """_summary_
424
+
425
+ Parameters
426
+ ----------
427
+ objs : list[DomainModel] | None, optional
428
+ _description_, by default None
429
+ """
430
+ if not objs:
431
+ objs = self.select(**kwargs) # type: ignore
432
+ for obj in objs:
433
+ self.remove(obj)
434
+
435
+ def select(
436
+ self,
437
+ model: DomainModel | None = None,
438
+ cursor_result: str = "all",
439
+ **kwargs: Any,
440
+ ) -> list[DomainModel]:
441
+ """_summary_
442
+
443
+ Parameters
444
+ ----------
445
+ model : DomainModel | None, optional
446
+ _description_, by default None
447
+ cursor_result : str, optional
448
+ _description_, by default "all"
449
+
450
+ Returns
451
+ -------
452
+ list[DomainModel]
453
+ _description_
454
+ """
455
+ return self._query(cursor_result=cursor_result, model=model, **kwargs) # type: ignore
456
+
457
+ def update(self, obj: Updateable, new: DomainModel) -> DomainModel:
458
+ """_summary_
459
+
460
+ Parameters
461
+ ----------
462
+ obj : DomainModel
463
+ _description_
464
+ new : DomainModel
465
+ _description_
466
+
467
+ Returns
468
+ -------
469
+ DomainModel
470
+ _description_
471
+ """
472
+ obj = obj.update(new)
473
+ self.session.flush()
474
+ self.session.refresh(obj)
475
+ return obj
476
+
477
+ def view(
478
+ self,
479
+ model: DomainModel,
480
+ cursor_result: str = "all",
481
+ **kwargs: Any,
482
+ ) -> list[DomainModel]:
483
+ """_summary_
484
+
485
+ Parameters
486
+ ----------
487
+ model : DomainModel
488
+ _description_
489
+ cursor_result : str, optional
490
+ _description_, by default "all"
491
+
492
+ Returns
493
+ -------
494
+ list[DomainModel]
495
+ _description_
496
+ """
497
+ return self._query(cursor_result=cursor_result, model=model, **kwargs) # type: ignore
498
+
499
+ def _query(
500
+ self,
501
+ cursor_result: str | None = None,
502
+ model: DomainModel | None = None,
503
+ filters: Iterable[SearchFilter | FilterOperator] | None = None,
504
+ query: Query[Any] | None = None,
505
+ order_by: list[
506
+ InstrumentedAttribute[Any]
507
+ | UnaryExpression[Any]
508
+ | OrderBy
509
+ | QueryClause
510
+ ] = list(),
511
+ **kwargs: Any,
512
+ ) -> Any:
513
+ """_summary_
514
+
515
+ Parameters
516
+ ----------
517
+ cursor_result : str | None, optional
518
+ _description_, by default None
519
+ model : DomainModel | None, optional
520
+ _description_, by default None
521
+ filters : list[SearchFilter | QueryClause | FilterOperator], optional
522
+ _description_, by default list()
523
+ query : Query[Any] | None, optional
524
+ _description_, by default None
525
+ order_by : list[ InstrumentedAttribute[Any] | UnaryExpression[Any] | OrderBy | QueryClause ], optional
526
+ _description_, by default list()
527
+
528
+ Returns
529
+ -------
530
+ Any
531
+ _description_
532
+ """
533
+ """
534
+ cursor_result:
535
+ all
536
+ first
537
+ one
538
+ one_or_none
539
+ count
540
+ None
541
+
542
+ **kwargs:
543
+ limit=n
544
+ order_by=User.id
545
+ order_by=[User.username, User.birthday]
546
+ distinct=User.username
547
+
548
+ """
549
+ if not model:
550
+ model = self._default_model
551
+
552
+ subquery: Query[Any]
553
+
554
+ if query:
555
+ subquery = query
556
+ else:
557
+ subquery = self.session.query(model) # type: ignore
558
+
559
+ normalized_filters = list(filters) if filters else []
560
+ if normalized_filters:
561
+ filter_statements = self._process_filters(
562
+ filters=normalized_filters, model=model
563
+ )
564
+ subquery = subquery.filter(*filter_statements) # type: ignore
565
+
566
+ for k, value in kwargs.items():
567
+ if not value:
568
+ break
569
+
570
+ if isinstance(value, QueryClause):
571
+ subquery = self._query_clause(
572
+ clause=value, query=subquery, model=model # type: ignore
573
+ )
574
+ elif isinstance(value, dict): # type: ignore
575
+ subquery = getattr(subquery, k)(**value) # type: ignore
576
+ elif isinstance(value, list):
577
+ for item in value: # type: ignore
578
+ if isinstance(item, QueryClause):
579
+ subquery = self._query_clause(
580
+ clause=item, query=subquery, model=model # type: ignore
581
+ )
582
+ else:
583
+ subquery = getattr(subquery, k)(item) # type: ignore
584
+ else:
585
+ subquery = getattr(subquery, k)(value) # type: ignore
586
+
587
+ for order in order_by:
588
+ if isinstance(order, QueryClause):
589
+ subquery = self._query_clause(
590
+ clause=order, query=subquery, model=model # type: ignore
591
+ )
592
+ elif isinstance(
593
+ order, InstrumentedAttribute | UnaryExpression
594
+ ): # type: ignore
595
+ subquery = getattr(subquery, "order_by")(order) # type: ignore
596
+
597
+ # Process cursor_result parameter
598
+ if cursor_result:
599
+ return getattr(subquery, cursor_result)() # type: ignore
600
+
601
+ return subquery # type: ignore
602
+
603
+ def _query_clause(
604
+ self,
605
+ clause: QueryClause,
606
+ query: Query[Any],
607
+ model: DomainModel,
608
+ ) -> Query[Any]:
609
+ if not clause._domain_model: # type: ignore
610
+ clause.set_domain_model(model)
611
+ return clause.query_clause(query)
612
+
613
+ def _process_filters(
614
+ self,
615
+ filters: Iterable[SearchFilter | FilterOperator],
616
+ model: BaseDomainModel,
617
+ ) -> list[ColumnElement[Any] | BinaryExpression[Any] | ColumnOperators]:
618
+ """Process query filters and apply them to the query object
619
+
620
+ Parameters
621
+ ----------
622
+ filters
623
+ Filters which can be SearchFilter or FilerOperator objects
624
+ model
625
+ The domain model which will be used to set the `_domain_model`
626
+ attribute of SearchFilter objects
627
+
628
+ Returns
629
+ -------
630
+ Query object to which the filters have been applied
631
+ """
632
+ filter_expressions = [
633
+ self._process_filter_item(filter_=f, model=model) for f in filters
634
+ ]
635
+ return filter_expressions
636
+
637
+ def _process_filter_item(
638
+ self,
639
+ filter_: SearchFilter | FilterOperator,
640
+ model: BaseDomainModel,
641
+ ) -> ColumnElement[Any] | BinaryExpression[Any] | ColumnOperators:
642
+ """Process a filter item. When the item is a SeachFilter object
643
+ the domain model will be set and the filter statement will be returned.
644
+ When the item is a FilterOperator object, all the filters will be
645
+ processed recursively by this method and they are supplied to the
646
+ filter operator.
647
+
648
+ Parameters
649
+ ----------
650
+ filter_
651
+ A filter object
652
+ model
653
+ Domain model type
654
+
655
+ Returns
656
+ -------
657
+ Returns a filter statement or a filter operator containing
658
+ filter statements
659
+
660
+ Raises
661
+ ------
662
+ TypeError
663
+ When an unsupported filter type is being used
664
+ """
665
+ if isinstance(filter_, FilterOperator):
666
+ filters = [
667
+ self._process_filter_item(filter_=filter_item, model=model)
668
+ for filter_item in filter_.search_filters
669
+ ]
670
+ return filter_.filter_operator(*filters) # type: ignore
671
+ elif isinstance(filter_, SearchFilter): # type: ignore
672
+ if not filter_._domain_model: # type: ignore
673
+ filter_.set_domain_model(model) # type: ignore
674
+ return filter_.filter_statement
675
+ else:
676
+ raise TypeError(
677
+ "Only QueryClause and FilterOperator types are allowed "
678
+ "as values for the 'filters' argument"
679
+ )
File without changes
@@ -0,0 +1,16 @@
1
+ """_summary_"""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from alpha.domain.models.base_model import BaseDomainModel
6
+ from alpha.interfaces.sql_repository import SqlRepository
7
+
8
+
9
+ @dataclass
10
+ class RepositoryModel:
11
+ """_summary_"""
12
+
13
+ name: str
14
+ repository: type[SqlRepository[BaseDomainModel]]
15
+ default_model: type[BaseDomainModel]
16
+ interface: object | None
File without changes