fastapi-rtk 1.0.8__py3-none-any.whl → 1.0.10__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.
@@ -3,6 +3,8 @@ import typing
3
3
  from datetime import datetime
4
4
 
5
5
  import fastapi_users.exceptions
6
+ import sqlalchemy
7
+ import sqlalchemy.orm
6
8
  from pydantic import BaseModel, Field
7
9
  from sqlalchemy import select
8
10
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -11,7 +13,7 @@ from sqlalchemy.orm import Session
11
13
  from ...const import logger
12
14
  from ...globals import g
13
15
  from ...setting import Setting
14
- from ...utils import merge_schema, safe_call
16
+ from ...utils import T, lazy, merge_schema, safe_call
15
17
  from .models import Api, Permission, PermissionApi, Role, User
16
18
 
17
19
  __all__ = ["SecurityManager"]
@@ -28,6 +30,21 @@ class SecurityManager:
28
30
  """
29
31
 
30
32
  toolkit: typing.Optional["FastAPIReactToolkit"]
33
+ builtin_roles = lazy(lambda: Setting.ROLES)
34
+ """
35
+ The built-in roles defined in the settings.
36
+
37
+ Format:
38
+ ```python
39
+ {
40
+ "role_name": [
41
+ (api_name1|api_name2|..., permission_name1|permission_name2|...),
42
+ ...
43
+ ],
44
+ ...
45
+ }
46
+ ```
47
+ """
31
48
 
32
49
  def __init__(self, toolkit: typing.Optional["FastAPIReactToolkit"] = None) -> None:
33
50
  self.toolkit = toolkit
@@ -385,6 +402,273 @@ class SecurityManager:
385
402
  -----------------------------------------
386
403
  """
387
404
 
405
+ def has_access_in_builtin_roles(
406
+ self, role_name: str, api_name: str, permission_name: str
407
+ ):
408
+ """
409
+ Checks if the given role has access to the specified API and permission in the built-in roles.
410
+
411
+ Args:
412
+ role_name (str): The name of the role to check.
413
+ api_name (str): The name of the API to check.
414
+ permission_name (str): The name of the permission to check.
415
+
416
+ Returns:
417
+ bool: True if the role has access, False otherwise.
418
+ """
419
+ if role_name not in self.builtin_roles:
420
+ return False
421
+
422
+ for api, perm in self.builtin_roles[role_name]:
423
+ api_names = api.split("|")
424
+ perm_names = perm.split("|")
425
+ if api_name in api_names and permission_name in perm_names:
426
+ return True
427
+ return False
428
+
429
+ def get_roles_from_builtin_roles(self):
430
+ """
431
+ Retrieves the names of the built-in roles.
432
+
433
+ Returns:
434
+ list[str]: The list of built-in role names.
435
+ """
436
+ return list(self.builtin_roles.keys())
437
+
438
+ def get_api_permission_tuples_from_builtin_roles(self, role: str | None = None):
439
+ """
440
+ Retrieves the API-permission tuples from the built-in roles.
441
+
442
+ Args:
443
+ role (str | None, optional): The name of the role to filter by. If None, retrieves from all roles. Defaults to None.
444
+
445
+ Returns:
446
+ list[tuple[str, str]]: The list of API-permission tuples.
447
+ """
448
+ api_permission_tuples = list[tuple[str, str]]()
449
+ for role_name, role_api_permission_list in self.builtin_roles.items():
450
+ if role is not None and role != role_name:
451
+ continue
452
+
453
+ for api, perm in role_api_permission_list:
454
+ api_permission_tuples.append((api, perm))
455
+ return api_permission_tuples
456
+
457
+ def get_role_and_api_permission_tuples_from_builtin_roles(self):
458
+ """
459
+ Retrieves the role and API-permission tuples from the built-in roles.
460
+
461
+ Returns:
462
+ list[tuple[str, list[tuple[str, str]]]]: The list of role and API-permission tuples.
463
+ """
464
+ role_api_permission_tuples = list[tuple[str, list[tuple[str, str]]]]()
465
+ for role_name, role_api_permission_list in self.builtin_roles.items():
466
+ api_permission_list = list[tuple[str, str]]()
467
+ for api, perm in role_api_permission_list:
468
+ api_permission_list.append((api, perm))
469
+ role_api_permission_tuples.append((role_name, api_permission_list))
470
+ return role_api_permission_tuples
471
+
472
+ async def create_roles(
473
+ self,
474
+ roles: list[str],
475
+ *,
476
+ session: AsyncSession | Session = None,
477
+ raise_exception: bool = True,
478
+ ):
479
+ """
480
+ Creates new roles with the given names. Existing roles are not duplicated.
481
+
482
+ Args:
483
+ roles (list[str]): The names of the roles to create.
484
+ session (AsyncSession | Session, optional): The database session to use. If not given, a new session will be created. Defaults to None.
485
+ raise_exception (bool, optional): When set to True, raises an exception if an error occurs, otherwise returns None. Defaults to True.
486
+
487
+ Returns:
488
+ list[Role] | None: The created role objects if successful, else None.
489
+
490
+ Raises:
491
+ SomeException: Description of the exception raised, if any.
492
+ """
493
+ return await self._create_entities(
494
+ Role,
495
+ roles,
496
+ session=session,
497
+ raise_exception=raise_exception,
498
+ on_after_create=lambda role: logger.info(f"ADDING ROLE {role}"),
499
+ )
500
+
501
+ async def create_permissions(
502
+ self,
503
+ permissions: list[str],
504
+ *,
505
+ session: AsyncSession | Session = None,
506
+ raise_exception: bool = True,
507
+ ):
508
+ """
509
+ Creates new permissions with the given names. Existing permissions are not duplicated.
510
+
511
+ Args:
512
+ permissions (list[str]): The names of the permissions to create.
513
+ session (AsyncSession | Session, optional): The database session to use. If not given, a new session will be created. Defaults to None.
514
+ raise_exception (bool, optional): When set to True, raises an exception if an error occurs, otherwise returns None. Defaults to True.
515
+
516
+ Returns:
517
+ list[Permission] | None: The created permission objects if successful, else None.
518
+
519
+ Raises:
520
+ SomeException: Description of the exception raised, if any.
521
+ """
522
+ return await self._create_entities(
523
+ Permission,
524
+ permissions,
525
+ session=session,
526
+ raise_exception=raise_exception,
527
+ on_after_create=lambda permission: logger.info(
528
+ f"ADDING PERMISSION {permission}"
529
+ ),
530
+ )
531
+
532
+ async def create_apis(
533
+ self,
534
+ apis: list[str],
535
+ *,
536
+ session: AsyncSession | Session = None,
537
+ raise_exception: bool = True,
538
+ ):
539
+ """
540
+ Creates new APIs with the given names. Existing APIs are not duplicated.
541
+
542
+ Args:
543
+ apis (list[str]): The names of the APIs to create.
544
+ session (AsyncSession | Session, optional): The database session to use. If not given, a new session will be created. Defaults to None.
545
+ raise_exception (bool, optional): When set to True, raises an exception if an error occurs, otherwise returns None. Defaults to True.
546
+
547
+ Returns:
548
+ list[Api] | None: The created API objects if successful, else None.
549
+
550
+ Raises:
551
+ SomeException: Description of the exception raised, if any.
552
+ """
553
+ return await self._create_entities(
554
+ Api,
555
+ apis,
556
+ session=session,
557
+ raise_exception=raise_exception,
558
+ on_after_create=lambda api: logger.info(f"ADDING API {api}"),
559
+ )
560
+
561
+ async def associate_list_of_permission_with_api(
562
+ self,
563
+ permission_api_tuples: list[tuple[Permission, Api]],
564
+ *,
565
+ session: AsyncSession | Session = None,
566
+ raise_exception: bool = True,
567
+ ):
568
+ """
569
+ Associates a list of permissions with APIs. Existing associations are not duplicated.
570
+
571
+ Args:
572
+ permission_api_tuples (list[tuple[Permission, Api]]): A list of tuples containing Permission and Api objects to associate.
573
+ session (AsyncSession | Session, optional): The database session to use. If not given, a new session will be created. Defaults to None.
574
+ raise_exception (bool, optional): When set to True, raises an exception if an error occurs, otherwise returns None. Defaults to True.
575
+
576
+ Raises:
577
+ SomeException: Description of the exception raised, if any.
578
+ """
579
+ try:
580
+ if not session:
581
+ async with db.session() as session:
582
+ await self.associate_list_of_permission_with_api(
583
+ permission_api_tuples,
584
+ session=session,
585
+ raise_exception=raise_exception,
586
+ )
587
+ return
588
+
589
+ conditions = []
590
+ query = select(PermissionApi).options(
591
+ sqlalchemy.orm.joinedload(PermissionApi.api),
592
+ sqlalchemy.orm.joinedload(PermissionApi.permission),
593
+ sqlalchemy.orm.selectinload(PermissionApi.roles),
594
+ )
595
+ for permission, api in permission_api_tuples:
596
+ conditions.append(
597
+ sqlalchemy.and_(
598
+ PermissionApi.permission_id == permission.id,
599
+ PermissionApi.api_id == api.id,
600
+ )
601
+ )
602
+ query = query.where(sqlalchemy.or_(*conditions))
603
+ result = await safe_call(session.scalars(query))
604
+ existing_permission_apis = list(result.all())
605
+
606
+ new_tuples = list[tuple[Permission, Api]]()
607
+ for permission, api in permission_api_tuples:
608
+ exists = False
609
+ for permission_api in existing_permission_apis:
610
+ if (
611
+ permission_api.permission.id == permission.id
612
+ and permission_api.api.id == api.id
613
+ ):
614
+ exists = True
615
+ break
616
+ if not exists:
617
+ new_tuples.append((permission, api))
618
+
619
+ new_permission_apis = list[PermissionApi]()
620
+ for permission, api in new_tuples:
621
+ permission_api = PermissionApi(permission=permission, api=api, roles=[])
622
+ session.add(permission_api)
623
+ new_permission_apis.append(permission_api)
624
+ logger.info(f"ASSOCIATING PERMISSION {permission} WITH API {api}")
625
+ await safe_call(session.commit())
626
+ return existing_permission_apis + new_permission_apis
627
+ except Exception as e:
628
+ if not raise_exception:
629
+ return
630
+ raise e
631
+
632
+ async def associate_list_of_role_with_permission_api(
633
+ self,
634
+ role_permission_api_tuples: list[tuple[Role, PermissionApi]],
635
+ *,
636
+ session: AsyncSession | Session = None,
637
+ raise_exception: bool = True,
638
+ ):
639
+ """
640
+ Associates a list of roles with permission APIs. Existing associations are not duplicated.
641
+
642
+ Args:
643
+ role_permission_api_tuples (list[tuple[Role, PermissionApi]]): A list of tuples containing Role and PermissionApi objects to associate.
644
+ session (AsyncSession | Session, optional): The database session to use. If not given, a new session will be created. Defaults to None.
645
+ raise_exception (bool, optional): When set to True, raises an exception if an error occurs, otherwise returns None. Defaults to True.
646
+
647
+ Raises:
648
+ SomeException: Description of the exception raised, if any.
649
+ """
650
+ try:
651
+ if not session:
652
+ async with db.session() as session:
653
+ await self.associate_list_of_role_with_permission_api(
654
+ role_permission_api_tuples,
655
+ session=session,
656
+ raise_exception=raise_exception,
657
+ )
658
+ return
659
+
660
+ for role, permission_api in role_permission_api_tuples:
661
+ if role not in permission_api.roles:
662
+ permission_api.roles.append(role)
663
+ logger.info(
664
+ f"ASSOCIATING ROLE {role} WITH PERMISSION API {permission_api}"
665
+ )
666
+ await safe_call(session.commit())
667
+ except Exception as e:
668
+ if not raise_exception:
669
+ return
670
+ raise e
671
+
388
672
  async def cleanup(self, *, session: AsyncSession | Session = None):
389
673
  """
390
674
  Cleanup unused permissions from apis and roles.
@@ -404,13 +688,12 @@ class SecurityManager:
404
688
  "FastAPIReactToolkit instance not provided, you must provide it to use this function."
405
689
  )
406
690
 
407
- api_permission_tuples = (Setting.ROLES).values()
691
+ api_permission_tuples = self.get_api_permission_tuples_from_builtin_roles()
408
692
  apis = [api.__class__.__name__ for api in self.toolkit.apis]
409
693
  permissions = self.toolkit.total_permissions()
410
- for api_permission_tuple in api_permission_tuples:
411
- for api, permission in api_permission_tuple:
412
- apis.append(api)
413
- permissions.append(permission)
694
+ for api, permission in api_permission_tuples:
695
+ apis.append(api)
696
+ permissions.append(permission)
414
697
 
415
698
  # Clean up unused permissions
416
699
  unused_permissions = await safe_call(
@@ -428,14 +711,20 @@ class SecurityManager:
428
711
  logger.info(f"DELETING API {api} AND ITS ASSOCIATIONS")
429
712
  await safe_call(session.delete(api))
430
713
 
431
- roles = list(Setting.ROLES.keys())
714
+ roles = self.get_roles_from_builtin_roles()
432
715
  if g.admin_role is not None:
433
716
  roles.append(g.admin_role)
434
717
 
435
718
  # Clean up existing permission-apis, that are no longer connected to any roles
436
- unused_permission_apis = await safe_call(session.scalars(select(PermissionApi)))
437
- for permission_api in unused_permission_apis:
438
- for role in list(permission_api.roles) or []:
719
+ permission_apis_in_db = await safe_call(
720
+ session.scalars(
721
+ select(PermissionApi).options(
722
+ sqlalchemy.orm.selectinload(PermissionApi.roles)
723
+ )
724
+ )
725
+ )
726
+ for permission_api in permission_apis_in_db:
727
+ for role in permission_api.roles:
439
728
  if role.name not in roles:
440
729
  permission_api.roles.remove(role)
441
730
  logger.info(
@@ -443,3 +732,71 @@ class SecurityManager:
443
732
  )
444
733
 
445
734
  await safe_call(session.commit())
735
+
736
+ """
737
+ -----------------------------------------
738
+ HELPER FUNCTIONS
739
+ -----------------------------------------
740
+ """
741
+
742
+ async def _create_entities(
743
+ self,
744
+ entity_class: typing.Type[T],
745
+ names: list[str],
746
+ *,
747
+ session: AsyncSession | Session = None,
748
+ raise_exception: bool = True,
749
+ on_after_create: typing.Optional[
750
+ typing.Callable[[T], typing.Awaitable[None] | None]
751
+ ] = None,
752
+ ):
753
+ """
754
+ Helper function to create new entities with the given names.
755
+ Existing entities are not duplicated.
756
+
757
+ Args:
758
+ entity_class (typing.Type[T]): The entity class to create.
759
+ names (list[str]): The names of the entities to create.
760
+ session (AsyncSession | Session, optional): The database session to use. If not given, a new session will be created. Defaults to None.
761
+ raise_exception (bool, optional): When set to True, raises an exception if an error occurs, otherwise returns None. Defaults to True.
762
+ on_after_create (Optional[Callable[[T], Awaitable[None] | None]], optional): An optional callback function to be called after each entity is created. Defaults to None.
763
+
764
+ Returns:
765
+ list[T] | None: The created entity objects if successful, else None.
766
+
767
+ Raises:
768
+ Exception: If an error occurs and raise_exception is True.
769
+ """
770
+ try:
771
+ if not session:
772
+ async with db.session() as session:
773
+ return await self._create_entities(
774
+ entity_class,
775
+ names,
776
+ session=session,
777
+ raise_exception=raise_exception,
778
+ )
779
+
780
+ # Check for existing entities
781
+ result = await safe_call(
782
+ session.scalars(
783
+ select(entity_class).where(entity_class.name.in_(names))
784
+ )
785
+ )
786
+ existing_entities = list(result.all())
787
+ existing_names = [entity.name for entity in existing_entities]
788
+
789
+ new_names = [name for name in names if name not in existing_names]
790
+ new_entities = list[T]()
791
+ for name in new_names:
792
+ entity = entity_class(name=name)
793
+ session.add(entity)
794
+ new_entities.append(entity)
795
+ if on_after_create:
796
+ await safe_call(on_after_create(entity))
797
+ await safe_call(session.commit())
798
+ return existing_entities + new_entities
799
+ except Exception as e:
800
+ if not raise_exception:
801
+ return
802
+ raise e
@@ -4,16 +4,19 @@ import typing
4
4
  __all__ = ["hooks"]
5
5
 
6
6
 
7
- def hooks(pre: typing.Callable = None, post: typing.Callable = None):
7
+ def hooks(
8
+ pre: typing.Callable[..., None] | None = None,
9
+ post: typing.Callable[..., None] | None = None,
10
+ ):
8
11
  """
9
12
  Decorator to add optional pre and post hook methods to a function.
10
13
 
11
14
  Args:
12
- pre (Callable): Function to run before the main function.
13
- post (Callable): Function to run after the main function.
15
+ pre (typing.Callable[..., None] | None): Function to run before the main function. Defaults to None.
16
+ post (typing.Callable[..., None] | None): Function to run after the main function. Defaults to None.
14
17
 
15
18
  Returns:
16
- Callable: The decorated function.
19
+ typing.Callable[..., typing.Any]: The decorated function.
17
20
  """
18
21
 
19
22
  def decorator(func):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-rtk
3
- Version: 1.0.8
3
+ Version: 1.0.10
4
4
  Summary: A package that provides a set of tools to build a FastAPI application with a Class-Based CRUD API.
5
5
  Project-URL: Homepage, https://codeberg.org/datatactics/fastapi-rtk
6
6
  Project-URL: Issues, https://codeberg.org/datatactics/fastapi-rtk/issues
@@ -1,13 +1,13 @@
1
- fastapi_rtk/__init__.py,sha256=acLIihNMCZI3prFTq0cru1-k3kPjSZb2hhcqNrW7xJo,6203
2
- fastapi_rtk/_version.py,sha256=NND_6JDYnYnGzN3-RTpS5F7wzv62vDf7hAxiyTSBJfE,22
1
+ fastapi_rtk/__init__.py,sha256=Lg6Er5blOMcbtQSRJMNWbRQ1VX4PWEPMVFm2ie5r8mA,6183
2
+ fastapi_rtk/_version.py,sha256=5vGk-8GWz6jojEu9w36UP5aNA0LuiwgbNSJ8Umn2rLA,23
3
3
  fastapi_rtk/apis.py,sha256=6X_Lhl98m7lKrDRybg2Oe24pLFLJ29eCOQSwCAvpKhY,172
4
4
  fastapi_rtk/config.py,sha256=9PZF9E5i1gxmnsZEprZZKxVHSk0dFEklJSplX9NEqdo,14036
5
5
  fastapi_rtk/const.py,sha256=sEj_cYeerj9pVwbCu0k5Sy1EYpdr1EHzUjqqbnporgc,4905
6
- fastapi_rtk/db.py,sha256=nAPIibCCyaGm7Kw9updiVx5LWASkdfX01GxG5Gp12hM,24505
6
+ fastapi_rtk/db.py,sha256=gItbbRYYAtAF7_1YqaZGBeQY5_E0cKvomGEzcCjIhpg,24548
7
7
  fastapi_rtk/decorators.py,sha256=HqAFSiO0l5_M0idWs0IcY24FdzbAcDQDQoifM_WgZAQ,14515
8
- fastapi_rtk/dependencies.py,sha256=H31uMZRA0FdZZXmzIVS2j9gBAovJDYdb_5ZgmrKd9qc,6070
8
+ fastapi_rtk/dependencies.py,sha256=jlcsMrh83yrJsgXvpWJet_mjqwDP3nBZfPSg4Lq8KKE,7757
9
9
  fastapi_rtk/exceptions.py,sha256=P0qwd4VkeWFotgMVQHgmdT1NphFQaEznKLFIvJzW4Zs,2594
10
- fastapi_rtk/fastapi_react_toolkit.py,sha256=gDqHq7Bk90LTZm6muOlqBbiFtOMR8xMOxa9edm8ad8w,31891
10
+ fastapi_rtk/fastapi_react_toolkit.py,sha256=lvtieBLzCAlozKQU9dkAUVoW7Ofi1ncri_hDyeHazF4,29803
11
11
  fastapi_rtk/filters.py,sha256=weCH3suCxpGJQYmwhj9D1iAqMPiRnmbRiN7stK0rhoE,181
12
12
  fastapi_rtk/globals.py,sha256=TcoMHCueM7sFwZ8iYorUe4q-3KpVFfV04RAPuMTYFKY,8863
13
13
  fastapi_rtk/manager.py,sha256=J86sBByj6E1kZ3CRS0NayI0n5firrfMhAU2d58mz0JM,33965
@@ -23,7 +23,7 @@ fastapi_rtk/api/__init__.py,sha256=MwFR7HHppnhbjZGg3sOdQ6nqy9uxnHHXvicpswNFMNA,2
23
23
  fastapi_rtk/api/base_api.py,sha256=42I9v3b25lqxNAMDGEtajA5-btIDSyUWF0xMDgGkA8c,8078
24
24
  fastapi_rtk/api/model_rest_api.py,sha256=R-zdfDK6QiVb8u6EBTpZt7cqvH4kP86udNMxdJRiphQ,105254
25
25
  fastapi_rtk/auth/__init__.py,sha256=iX7O41NivBYDfdomEaqm4lUx9KD17wI4g3EFLF6kUTw,336
26
- fastapi_rtk/auth/auth.py,sha256=MZmuueioiMbSHjd_F3frKEqCA3yjtanRWyKOy6CnOd0,20994
26
+ fastapi_rtk/auth/auth.py,sha256=F2qZoR7go_7FnvVJrDxUCd6vtRz5XW8yyOv143MWPts,20664
27
27
  fastapi_rtk/auth/hashers/__init__.py,sha256=uBThFj2VPPSMSioxYTktNiM4-mVgtDAjTpKA3ZzWxxs,110
28
28
  fastapi_rtk/auth/hashers/pbkdf2.py,sha256=EeusoypVDHVuxneoPP2aXOPpyCwxx_5O22qMeDW51b0,919
29
29
  fastapi_rtk/auth/hashers/scrypt.py,sha256=CPGe6vzj7hlKTl_MKDGSgKQW927EpPkgpGM7mVO5MW0,902
@@ -60,7 +60,7 @@ fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py,sha256=L1WgO7UzWuAgs
60
60
  fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py,sha256=sckNoxPE8ApKCLgBZzE_2dokXrM6mIXvMguHZvyJzIM,3891
61
61
  fastapi_rtk/bases/__init__.py,sha256=v3tMVuX20UbvjI_mTWpDAePWAA38e3pjlYEiICgY4j8,440
62
62
  fastapi_rtk/bases/db.py,sha256=D27BhF89J0OaLHjALDCa85eNf35lBaTz6VV7EDa4wuM,18711
63
- fastapi_rtk/bases/file_manager.py,sha256=d1ZZaY-OgDxjSWQ49DNQNSDHKznzr-03wki_eR2iU2A,8230
63
+ fastapi_rtk/bases/file_manager.py,sha256=n6yRY0jh60itXZt0R3oaSN-2Q_CTRswX1BghE7GN4Sk,8791
64
64
  fastapi_rtk/bases/filter.py,sha256=XmWTcLaIcBj9pKF1PMAKdwSnZNpdT8Df3uLeUIOGUDE,1840
65
65
  fastapi_rtk/bases/interface.py,sha256=Cq9Duxa3w-tw342P424h88fc0_X1DoxCdTa3rAN-6jM,45380
66
66
  fastapi_rtk/bases/model.py,sha256=nUZf0AVs0Mzqh2u_ALiRNYN1bfOU9PzYLvEFHDQ57Y0,1692
@@ -73,7 +73,7 @@ fastapi_rtk/cli/types.py,sha256=ezJjljcofwSI4HlDz2JWe0oVY9IA6hVvgx7IEWBkyRQ,1364
73
73
  fastapi_rtk/cli/utils.py,sha256=v7sfL1-S3XYSQ40WTD-CYcH77EfJTcKhuD3jaAImYc8,6285
74
74
  fastapi_rtk/cli/commands/__init__.py,sha256=iFkU03MOUgHoAmmyF5FmUxA6wD1YN59JqqP4WD5n6Dk,524
75
75
  fastapi_rtk/cli/commands/export.py,sha256=4OuFyljUxSvdyZc0bYzzLfnahJvSYN38fxAUPopoH4M,17572
76
- fastapi_rtk/cli/commands/security.py,sha256=oP1sfQxhgrpNoRmlB-lvbt1dAoGlNXeBEQQXQihAF_k,8916
76
+ fastapi_rtk/cli/commands/security.py,sha256=tZOUVSbsgBFXjH7_vfH6GMKH5pKkRuhVl8TTh7V8plA,8880
77
77
  fastapi_rtk/cli/commands/translate.py,sha256=v6s0XentYi5wLzPEd7qc6QUjgdn6wt89dvTbWzUPeOY,10508
78
78
  fastapi_rtk/cli/commands/db/__init__.py,sha256=2clkGMUpGY0czSUaBql8fFSd8YrSJ0oGlzkpBZejjd0,14128
79
79
  fastapi_rtk/cli/commands/db/templates/fastapi/README,sha256=dNsmYPHI9RJkhAIE18JqVmXhdJ21OCeQs8GP5DyA8Yo,47
@@ -87,7 +87,7 @@ fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako,sha256=o02U
87
87
  fastapi_rtk/file_managers/__init__.py,sha256=RZzSSUDq_cIv4MeVePRBGR0mbEiJua7WKccfE6IAawk,198
88
88
  fastapi_rtk/file_managers/file_manager.py,sha256=YJzKWtwVOiIYS4-W0CfadYhhWLydNRJ7D_T9vdzcIdU,2612
89
89
  fastapi_rtk/file_managers/image_manager.py,sha256=S2mcBuXOzXWlPhmq1in76kRmNKGYrIeEqDMs-c5KXo8,573
90
- fastapi_rtk/file_managers/s3_file_manager.py,sha256=3brXewQ1d3Fx9EVTvc24EFdzqE1DIlhNARWjCWKMeR8,3635
90
+ fastapi_rtk/file_managers/s3_file_manager.py,sha256=wjSyAoNrpDa5-SS__hK6Grajm1ZLOQZX9mXptjPd4lY,5538
91
91
  fastapi_rtk/file_managers/s3_image_manager.py,sha256=YLPARdmffA_prMPmm5Mzupaz0IakxWgWL7hWaLSznm0,352
92
92
  fastapi_rtk/lang/__init__.py,sha256=zJejpbIXx1O-nSuWwrCYgr5f5EtwjYW6kh8jRy_Yo0U,68
93
93
  fastapi_rtk/lang/babel.cfg,sha256=ahkzg8wUKyl9G9UqkgAyWEtRlCnaWkjStM5c4FhKuUE,18
@@ -102,9 +102,9 @@ fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo,sha256=Oi0vH4JgE6QmBcbh
102
102
  fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po,sha256=GcgnXbNEQyhPM_E1urhOyhsZJxfSl2JMjMC7rBbMzas,6781
103
103
  fastapi_rtk/security/__init__.py,sha256=XkYZ_GO2opdyQS_LJwbCH1C6SUkNfLUvLkpFhcO7ZlI,86
104
104
  fastapi_rtk/security/sqla/__init__.py,sha256=qKh1mGQezDtMzvRzxzfHZRyKVaks-O7PXrOx16ekSZA,88
105
- fastapi_rtk/security/sqla/apis.py,sha256=J3BCqSnaX-w9raTA5LHa7muW24kA09nAm9kN6b3N5PU,10170
106
- fastapi_rtk/security/sqla/models.py,sha256=dwIZHNU-6b1r0M2swlYyRtSgI1e1hnvav6d6jdmffM8,8096
107
- fastapi_rtk/security/sqla/security_manager.py,sha256=eBXhDTjqQYySCKQXnD6wEeHBGw8oytwVvmKpi9btNYI,17371
105
+ fastapi_rtk/security/sqla/apis.py,sha256=8Qtd1G73i2TaA9Euo63naaB8d_I9r4EHL8DWtWNJEfs,10699
106
+ fastapi_rtk/security/sqla/models.py,sha256=rEXpbljTMlTkiDp0VIxRXant8xN-WTjpqgQkr-ElXeM,7865
107
+ fastapi_rtk/security/sqla/security_manager.py,sha256=ui0g2cH8Z-YjDuqszcstyj6Li8U4IpuVV4bAVaD69Lc,31395
108
108
  fastapi_rtk/utils/__init__.py,sha256=0X4BwrVDl4S3mB7DLyHaZVednefMjRIjBIDT3yv_CHM,1875
109
109
  fastapi_rtk/utils/async_task_runner.py,sha256=HzykQSdeAmNjZfVB5vUDVwrSCVFr8__67ACQk60pSsk,15545
110
110
  fastapi_rtk/utils/class_factory.py,sha256=jlVw8yCh-tYsMnR5Hm8fgxtL0kvXwnhe6DPJA1sUh7k,598
@@ -112,7 +112,7 @@ fastapi_rtk/utils/csv_json_converter.py,sha256=7szrPiB7DrK5S-s2GaHVCmEP9_OXk9RFw
112
112
  fastapi_rtk/utils/deep_merge.py,sha256=PHtKJgXfCngOBGVliX9aVlEFcwCxr-GlzU-w6vjgAIs,2426
113
113
  fastapi_rtk/utils/extender_mixin.py,sha256=eu22VAZJIf-r_uD-zVn_2IzvknfuUkmEHn9oo-0KU0k,1388
114
114
  fastapi_rtk/utils/flask_appbuilder_utils.py,sha256=nPLQIczDZgKElMtqBRSo_aPJZMOnPs7fRyjqKUtPDbo,2276
115
- fastapi_rtk/utils/hooks.py,sha256=9DO8Y6vj0Drjkh064bTf31nSgLiCqkA-XCUOgzLB_V4,744
115
+ fastapi_rtk/utils/hooks.py,sha256=iGE8HTfDDVfQm7yeD3WqPB8_I1FXFGpBdl3ngyjaRbY,901
116
116
  fastapi_rtk/utils/lazy.py,sha256=SlVYQ-RHRcp6pGmcslVNc5lKs5GOSjqLcRsQsSWIB0s,10352
117
117
  fastapi_rtk/utils/merge_schema.py,sha256=AStYAS_t2idD63_YQri8sBcRM1jaq0kapjYUpI27syg,1398
118
118
  fastapi_rtk/utils/multiple_async_contexts.py,sha256=-juAliUeG4XI1J-p31KknsJvzvM5vX0gXVXst_xZUIo,557
@@ -126,8 +126,8 @@ fastapi_rtk/utils/timezone.py,sha256=62S0pPWuDFFXxV1YTFCsc4uKiSP_Ba36Fv7S3gYjfhs
126
126
  fastapi_rtk/utils/update_signature.py,sha256=PIzZgNpGEwvDNgQ3G51Zi-QhVV3mbxvUvSwDwf_-yYs,2209
127
127
  fastapi_rtk/utils/use_default_when_none.py,sha256=H2HqhKy_8eYk3i1xijEjuaKak0oWkMIkrdz6T7DK9QU,469
128
128
  fastapi_rtk/utils/werkzeug.py,sha256=1Gv-oyqSmhVGrmNbB9fDqpqJzPpANOzWf4zMMrhW9UA,3245
129
- fastapi_rtk-1.0.8.dist-info/METADATA,sha256=BtLiGBfKiNshhahz5dAFunO6FJip5cxTRFkWZ4_zibI,1270
130
- fastapi_rtk-1.0.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
131
- fastapi_rtk-1.0.8.dist-info/entry_points.txt,sha256=UuTkxSVIokSlVN28TMhoxzRRUaPxlVRSH3Gsx6yip60,53
132
- fastapi_rtk-1.0.8.dist-info/licenses/LICENSE,sha256=NDrWi4Qwcxal3u1r2lBWGA6TVh3OeW7yMan098mQz98,1073
133
- fastapi_rtk-1.0.8.dist-info/RECORD,,
129
+ fastapi_rtk-1.0.10.dist-info/METADATA,sha256=FWjDKJQ40ntwOVswJo9zqpKqasIu4dugohhYo7bWaCU,1271
130
+ fastapi_rtk-1.0.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
131
+ fastapi_rtk-1.0.10.dist-info/entry_points.txt,sha256=UuTkxSVIokSlVN28TMhoxzRRUaPxlVRSH3Gsx6yip60,53
132
+ fastapi_rtk-1.0.10.dist-info/licenses/LICENSE,sha256=NDrWi4Qwcxal3u1r2lBWGA6TVh3OeW7yMan098mQz98,1073
133
+ fastapi_rtk-1.0.10.dist-info/RECORD,,