fastapi-factory-utilities 0.2.12__py3-none-any.whl → 0.3.1__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.

Potentially problematic release.


This version of fastapi-factory-utilities might be problematic. Click here for more details.

@@ -2,7 +2,13 @@
2
2
 
3
3
  from .application import ApplicationAbstract
4
4
  from .builder import ApplicationGenericBuilder
5
- from .config import BaseApplicationConfig, RootConfig
5
+ from .config import (
6
+ BaseApplicationConfig,
7
+ DependencyConfig,
8
+ HttpServiceDependencyConfig,
9
+ RootConfig,
10
+ depends_dependency_config,
11
+ )
6
12
  from .enums import EnvironmentEnum
7
13
 
8
14
  __all__: list[str] = [
@@ -11,4 +17,7 @@ __all__: list[str] = [
11
17
  "ApplicationAbstract",
12
18
  "ApplicationGenericBuilder",
13
19
  "RootConfig",
20
+ "HttpServiceDependencyConfig",
21
+ "DependencyConfig",
22
+ "depends_dependency_config",
14
23
  ]
@@ -2,7 +2,8 @@
2
2
 
3
3
  from typing import Any, ClassVar, Generic, TypeVar, get_args
4
4
 
5
- from pydantic import BaseModel, ConfigDict, Field
5
+ from fastapi import Request
6
+ from pydantic import BaseModel, ConfigDict, Field, HttpUrl
6
7
 
7
8
  from fastapi_factory_utilities.core.app.exceptions import ConfigBuilderError
8
9
  from fastapi_factory_utilities.core.utils.configs import (
@@ -71,6 +72,18 @@ class BaseApplicationConfig(BaseModel):
71
72
  root_path: str = Field(default="", description="Root path")
72
73
 
73
74
 
75
+ class HttpServiceDependencyConfig(BaseModel):
76
+ """Http service dependency config."""
77
+
78
+ url: HttpUrl
79
+
80
+
81
+ class DependencyConfig(BaseModel):
82
+ """Dependency config."""
83
+
84
+ kratos: HttpServiceDependencyConfig | None = Field(default=None, description="Kratos dependency config")
85
+
86
+
74
87
  class RootConfig(BaseModel):
75
88
  """Root configuration."""
76
89
 
@@ -84,6 +97,7 @@ class RootConfig(BaseModel):
84
97
  cors: CorsConfig = Field(description="CORS configuration", default_factory=CorsConfig)
85
98
  development: DevelopmentConfig = Field(description="Development configuration", default_factory=DevelopmentConfig)
86
99
  logging: list[LoggingConfig] = Field(description="Logging configuration", default_factory=list)
100
+ dependencies: DependencyConfig = Field(description="Dependencies configuration", default_factory=DependencyConfig)
87
101
 
88
102
 
89
103
  GenericConfig = TypeVar("GenericConfig", bound=BaseModel)
@@ -162,3 +176,8 @@ class GenericConfigBuilder(Generic[GenericConfig]):
162
176
  ) from exception
163
177
 
164
178
  return config
179
+
180
+
181
+ def depends_dependency_config(request: Request) -> DependencyConfig:
182
+ """Get the dependency config."""
183
+ return request.app.state.config.dependencies
@@ -4,9 +4,22 @@ import datetime
4
4
  from abc import ABC
5
5
  from collections.abc import AsyncGenerator, Callable
6
6
  from contextlib import asynccontextmanager
7
- from typing import Any, Generic, TypeVar, get_args
7
+ from typing import (
8
+ Any,
9
+ Dict,
10
+ Generic,
11
+ List,
12
+ Mapping,
13
+ Optional,
14
+ Tuple,
15
+ TypeVar,
16
+ Union,
17
+ get_args,
18
+ )
8
19
  from uuid import UUID
9
20
 
21
+ from beanie import SortDirection
22
+ from beanie.odm.queries.find import FindMany
10
23
  from motor.motor_asyncio import AsyncIOMotorClientSession, AsyncIOMotorDatabase
11
24
  from pydantic import BaseModel
12
25
  from pymongo.errors import DuplicateKeyError, PyMongoError
@@ -214,3 +227,66 @@ class AbstractRepository(ABC, Generic[DocumentGenericType, EntityGenericType]):
214
227
  return
215
228
 
216
229
  raise OperationError("Failed to delete document.")
230
+
231
+ @managed_session()
232
+ async def find(
233
+ self,
234
+ *args: Union[Mapping[str, Any], bool],
235
+ projection_model: None = None,
236
+ skip: Optional[int] = None,
237
+ limit: Optional[int] = None,
238
+ sort: Union[None, str, List[Tuple[str, SortDirection]]] = None,
239
+ session: Optional[AsyncIOMotorClientSession] = None,
240
+ ignore_cache: bool = False,
241
+ fetch_links: bool = False,
242
+ lazy_parse: bool = False,
243
+ nesting_depth: Optional[int] = None,
244
+ nesting_depths_per_field: Optional[Dict[str, int]] = None,
245
+ **pymongo_kwargs: Any,
246
+ ) -> list[EntityGenericType]:
247
+ """Find documents in the database.
248
+
249
+ Args:
250
+ *args: The arguments to pass to the find method.
251
+ projection_model: The projection model to use.
252
+ skip: The number of documents to skip.
253
+ limit: The number of documents to return.
254
+ sort: The sort order.
255
+ session: The session to use.
256
+ ignore_cache: Whether to ignore the cache.
257
+ fetch_links: Whether to fetch links.
258
+ lazy_parse: Whether to lazy parse the documents.
259
+ nesting_depth: The nesting depth.
260
+ nesting_depths_per_field: The nesting depths per field.
261
+ **pymongo_kwargs: Additional keyword arguments to pass to the find method.
262
+
263
+ Returns:
264
+ list[EntityGenericType]: The list of entities.
265
+
266
+ Raises:
267
+ OperationError: If the operation fails.
268
+ ValueError: If the entity cannot be created from the document.
269
+ """
270
+ try:
271
+ documents: list[DocumentGenericType] = await self._document_type.find(
272
+ *args,
273
+ projection_model=projection_model,
274
+ skip=skip,
275
+ limit=limit,
276
+ sort=sort,
277
+ session=session,
278
+ ignore_cache=ignore_cache,
279
+ fetch_links=fetch_links,
280
+ lazy_parse=lazy_parse,
281
+ nesting_depth=nesting_depth,
282
+ nesting_depths_per_field=nesting_depths_per_field,
283
+ ).to_list()
284
+ except PyMongoError as error:
285
+ raise OperationError(f"Failed to find documents: {error}") from error
286
+
287
+ try:
288
+ entities: list[EntityGenericType] = [self._entity_type(**document.model_dump()) for document in documents]
289
+ except ValueError as error:
290
+ raise ValueError(f"Failed to create entity from document: {error}") from error
291
+
292
+ return entities
@@ -0,0 +1,158 @@
1
+ """Provides security-related functions for the API."""
2
+
3
+ from asyncio import Task, TaskGroup
4
+ from http import HTTPStatus
5
+ from typing import Any, ClassVar, NewType, cast
6
+
7
+ import jwt
8
+ import pydantic
9
+ from fastapi import Request
10
+ from fastapi.exceptions import HTTPException
11
+ from pydantic import BaseModel
12
+
13
+ Scope = NewType("Scope", str)
14
+
15
+
16
+ class JWTBearerDecoded(BaseModel):
17
+ """JWT bearer token decoded."""
18
+
19
+ model_config: ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
20
+ arbitrary_types_allowed=True,
21
+ extra="forbid",
22
+ frozen=True,
23
+ )
24
+
25
+ scopes: list[str] | None = None
26
+
27
+
28
+ class JWTBearerAuthentication:
29
+ """JWT Bearer Authentication.
30
+
31
+ This class is used to authenticate users using JWT tokens.
32
+ It extracts the token from the request, decodes it, and verifies its validity.
33
+ """
34
+
35
+ def __init__(self, scopes: list[Scope] | None = None, jwt_raw: str | None = None) -> None:
36
+ """Initialize the OAuth2 class.
37
+
38
+ Args:
39
+ scopes (SecurityScopes): Security scopes for the OAuth2.
40
+ jwt_raw (str): JWT token to be used for authentication.
41
+ """
42
+ self.jwt_raw: str | None = jwt_raw
43
+ self.scopes: list[Scope] | None = scopes
44
+
45
+ def _extract_raw_token(self, request: Request) -> str:
46
+ """Extract the raw token from the request.
47
+
48
+ Args:
49
+ request (Request): FastAPI request object.
50
+
51
+ Returns:
52
+ str: Raw token.
53
+
54
+ Raises:
55
+ HTTPException: If the token is missing or invalid.
56
+ """
57
+ try:
58
+ authorization_header: str | None = request.headers.get("Authorization")
59
+ except (AttributeError, KeyError) as e:
60
+ raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="Missing Credentials") from e
61
+
62
+ if not authorization_header:
63
+ raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="Missing Credentials")
64
+
65
+ if not authorization_header.startswith("Bearer "):
66
+ raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="Invalid Credentials")
67
+
68
+ return authorization_header.split(sep=" ")[1]
69
+
70
+ async def _decode_jwt(self, jwt_raw: str) -> JWTBearerDecoded:
71
+ """Decode the JWT token.
72
+
73
+ Args:
74
+ jwt_raw (str): Raw JWT token.
75
+
76
+ Returns:
77
+ JWTBearerDecoded: Decoded JWT token.
78
+
79
+ Raises:
80
+ HTTPException: If the token is invalid or expired.
81
+ """
82
+ try:
83
+ jwt_decoded: dict[str, Any] = cast(
84
+ dict[str, Any],
85
+ jwt.decode(
86
+ jwt=jwt_raw,
87
+ algorithms=["HS256", "RS256"],
88
+ options={"verify_signature": True},
89
+ ),
90
+ )
91
+ return JWTBearerDecoded(**jwt_decoded)
92
+ except jwt.ExpiredSignatureError as e:
93
+ raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="Token expired") from e
94
+ except jwt.InvalidTokenError as e:
95
+ raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="Invalid token") from e
96
+ except pydantic.ValidationError as e:
97
+ raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail=f"Invalid token: {e.json()}") from e
98
+
99
+ async def _verify(self, jwt_raw: str) -> None:
100
+ """Verify the JWT token.
101
+
102
+ Args:
103
+ jwt_raw (str): Raw JWT token.
104
+ """
105
+ pass
106
+
107
+ def _has_scope(self, jwt_decoded: JWTBearerDecoded) -> None:
108
+ """Check if the JWT token has the required scope.
109
+
110
+ Args:
111
+ jwt_decoded (JWTBearerDecoded): Decoded JWT token.
112
+
113
+ """
114
+ # Just Authentication (no scopes, no authorization)
115
+ if not self.scopes:
116
+ return
117
+ # JWT without scopes (no authorization)
118
+ if not jwt_decoded.scopes:
119
+ raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Unauthorized")
120
+ # Check if all required scopes are present
121
+ if not all(scope in jwt_decoded.scopes for scope in (self.scopes or [])):
122
+ raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Unauthorized")
123
+
124
+ # All scopes are valid (authorization)
125
+ return
126
+
127
+ async def __call__(self, request: Request | None = None) -> JWTBearerDecoded:
128
+ """Call the JWT bearer authentication.
129
+
130
+ Args:
131
+ request (Request): FastAPI request object.
132
+
133
+ Returns:
134
+ JWTBearerDecoded: Decoded JWT token.
135
+
136
+ Raises:
137
+ HTTPException: If the token is missing or invalid.
138
+ """
139
+ # Ensure that the jwt will be provided
140
+ # by the request or by the jwt parameter
141
+ if self.jwt_raw is None and request is None:
142
+ raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="Missing Credentials")
143
+ jwt_raw: str = (
144
+ self.jwt_raw
145
+ if self.jwt_raw is not None
146
+ else self._extract_raw_token(request=request) # type: ignore[arg-type]
147
+ )
148
+ # Execute the io bound and cpu bound tasks in parallel
149
+ async with TaskGroup() as tg:
150
+ # TODO: Can be disabled by configuration (for operation purposes)
151
+ # Ensure that the jwt is not revoked or expired
152
+ tg.create_task(self._verify(jwt_raw=jwt_raw), name="verify_jwt")
153
+ # Ensure that the jwt is not altered or expired
154
+ task_decode: Task[Any] = tg.create_task(self._decode_jwt(jwt_raw=jwt_raw), name="decode_jwt")
155
+ # Scope Validation
156
+ jwt_decoded: JWTBearerDecoded = task_decode.result()
157
+ self._has_scope(jwt_decoded=jwt_decoded)
158
+ return jwt_decoded
@@ -0,0 +1,78 @@
1
+ """Provide Kratos Session and Identity classes."""
2
+
3
+ from typing import Annotated
4
+
5
+ from fastapi import Depends, HTTPException, Request
6
+
7
+ from fastapi_factory_utilities.core.services.kratos import (
8
+ KratosOperationError,
9
+ KratosService,
10
+ KratosSessionInvalidError,
11
+ KratosSessionObject,
12
+ depends_kratos_service,
13
+ )
14
+
15
+
16
+ class KratosSessionAuthentication:
17
+ """Kratos Session class."""
18
+
19
+ DEFAULT_COOKIE_NAME: str = "ory_kratos_session"
20
+
21
+ def __init__(self, cookie_name: str = DEFAULT_COOKIE_NAME) -> None:
22
+ """Initialize the KratosSessionAuthentication class.
23
+
24
+ Args:
25
+ cookie_name (str): Name of the cookie to extract the session
26
+ """
27
+ self._cookie_name: str = cookie_name
28
+
29
+ def _extract_cookie(self, request: Request) -> str:
30
+ """Extract the cookie from the request.
31
+
32
+ Args:
33
+ request (Request): FastAPI request object.
34
+
35
+ Returns:
36
+ str | None: Cookie value or None if not found.
37
+
38
+ Raises:
39
+ HTTPException: If the cookie is missing.
40
+ """
41
+ cookie: str | None = request.cookies.get(self._cookie_name)
42
+ if not cookie:
43
+ raise HTTPException(
44
+ status_code=401,
45
+ detail="Missing Credentials",
46
+ )
47
+ return cookie
48
+
49
+ async def __call__(
50
+ self, request: Request, kratos_service: Annotated[KratosService, Depends(depends_kratos_service)]
51
+ ) -> KratosSessionObject:
52
+ """Extract the Kratos session from the request.
53
+
54
+ Args:
55
+ request (Request): FastAPI request object.
56
+ kratos_service (KratosService): Kratos service object.
57
+
58
+ Returns:
59
+ KratosSessionObject: Kratos session object.
60
+
61
+ Raises:
62
+ HTTPException: If the session is invalid.
63
+ """
64
+ cookie: str = self._extract_cookie(request)
65
+ try:
66
+ session: KratosSessionObject = await kratos_service.whoami(cookie_value=cookie)
67
+ except KratosSessionInvalidError as e:
68
+ raise HTTPException(
69
+ status_code=401,
70
+ detail="Invalid Credentials",
71
+ ) from e
72
+ except KratosOperationError as e:
73
+ raise HTTPException(
74
+ status_code=500,
75
+ detail="Internal Server Error",
76
+ ) from e
77
+
78
+ return session
@@ -0,0 +1,13 @@
1
+ """Kratos service module."""
2
+
3
+ from .exceptions import KratosOperationError, KratosSessionInvalidError
4
+ from .objects import KratosSessionObject
5
+ from .services import KratosService, depends_kratos_service
6
+
7
+ __all__: list[str] = [
8
+ "KratosOperationError",
9
+ "KratosService",
10
+ "KratosSessionInvalidError",
11
+ "KratosSessionObject",
12
+ "depends_kratos_service",
13
+ ]
@@ -0,0 +1,11 @@
1
+ """Provides enums for Kratos."""
2
+
3
+ from enum import StrEnum
4
+
5
+
6
+ class AuthenticatorAssuranceLevelEnum(StrEnum):
7
+ """Enum for Authenticator Assurance Level (AAL)."""
8
+
9
+ AAL1 = "aal1"
10
+ AAL2 = "aal2"
11
+ AAL3 = "aal3"
@@ -0,0 +1,15 @@
1
+ """Python exceptions for the Kratos service."""
2
+
3
+ from fastapi_factory_utilities.core.exceptions import FastAPIFactoryUtilitiesError
4
+
5
+
6
+ class KratosError(FastAPIFactoryUtilitiesError):
7
+ """Base class for all exceptions raised by the Kratos service."""
8
+
9
+
10
+ class KratosOperationError(KratosError):
11
+ """Exception raised when a Kratos operation fails."""
12
+
13
+
14
+ class KratosSessionInvalidError(KratosOperationError):
15
+ """Exception raised when a Kratos session is invalid."""
@@ -0,0 +1,42 @@
1
+ """Provides the Kratos Objects."""
2
+
3
+ import datetime
4
+ import uuid
5
+ from typing import ClassVar
6
+
7
+ from pydantic import BaseModel, ConfigDict
8
+
9
+ from .enums import AuthenticatorAssuranceLevelEnum
10
+
11
+
12
+ class KratosTraitsObject(BaseModel):
13
+ """Traits for Kratos."""
14
+
15
+ model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
16
+
17
+ email: str
18
+ username: str
19
+
20
+
21
+ class KratosIdentityObject(BaseModel):
22
+ """Identity for Kratos."""
23
+
24
+ model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
25
+
26
+ id: uuid.UUID
27
+ state: str
28
+ traits: KratosTraitsObject
29
+
30
+
31
+ class KratosSessionObject(BaseModel):
32
+ """Session object for Kratos."""
33
+
34
+ model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
35
+
36
+ id: uuid.UUID
37
+ active: bool
38
+ issued_at: datetime.datetime
39
+ expires_at: datetime.datetime
40
+ authenticated_at: datetime.datetime
41
+ authenticator_assurance_level: AuthenticatorAssuranceLevelEnum
42
+ identity: KratosIdentityObject
@@ -0,0 +1,86 @@
1
+ """Provides the KratosService class for handling Kratos operations."""
2
+
3
+ from http import HTTPStatus
4
+ from typing import Annotated
5
+
6
+ import aiohttp
7
+ from fastapi import Depends
8
+ from pydantic import ValidationError
9
+
10
+ from fastapi_factory_utilities.core.app import (
11
+ DependencyConfig,
12
+ HttpServiceDependencyConfig,
13
+ depends_dependency_config,
14
+ )
15
+
16
+ from .exceptions import KratosOperationError, KratosSessionInvalidError
17
+ from .objects import KratosSessionObject
18
+
19
+
20
+ class KratosService:
21
+ """Service class for handling Kratos operations."""
22
+
23
+ COOKIE_NAME: str = "ory_kratos_session"
24
+
25
+ def __init__(self, kratos_http_config: HttpServiceDependencyConfig) -> None:
26
+ """Initialize the KratosService class.
27
+
28
+ Args:
29
+ kratos_http_config (HttpServiceDependencyConfig): Kratos HTTP configuration.
30
+ """
31
+ self._http_config: HttpServiceDependencyConfig = kratos_http_config
32
+
33
+ async def whoami(self, cookie_value: str) -> KratosSessionObject:
34
+ """Get the current user session.
35
+
36
+ Args:
37
+ cookie_value (str): Cookie value.
38
+
39
+ Returns:
40
+ KratosSessionObject: Kratos session object.
41
+
42
+ Raises:
43
+ KratosOperationError: If the Kratos service returns an error.
44
+ KratosSessionInvalidError: If the Kratos session is invalid.
45
+ """
46
+ cookies: dict[str, str] = {self.COOKIE_NAME: cookie_value}
47
+ async with aiohttp.ClientSession(base_url=str(self._http_config.url), cookies=cookies) as session:
48
+ async with session.get(
49
+ url="/sessions/whoami",
50
+ ) as response:
51
+ if response.status >= HTTPStatus.INTERNAL_SERVER_ERROR.value:
52
+ raise KratosOperationError(message=f"Kratos service error: {response.status} - {response.reason}")
53
+ if response.status == HTTPStatus.UNAUTHORIZED:
54
+ raise KratosSessionInvalidError(
55
+ message=f"Kratos session invalid: {response.status} - {response.reason}"
56
+ )
57
+ if response.status != HTTPStatus.OK:
58
+ raise KratosOperationError(message=f"Kratos service error: {response.status} - {response.reason}")
59
+
60
+ try:
61
+ kratos_session: KratosSessionObject = KratosSessionObject(**await response.json())
62
+ except ValidationError as e:
63
+ raise KratosOperationError(message=f"Kratos service error: {e}") from e
64
+
65
+ return kratos_session
66
+
67
+
68
+ def depends_kratos_service(
69
+ dependency_config: Annotated[DependencyConfig, Depends(depends_dependency_config)],
70
+ ) -> KratosService:
71
+ """Dependency function to get the Kratos service instance.
72
+
73
+ Args:
74
+ dependency_config (DependencyConfig): Dependency configuration.
75
+
76
+ Returns:
77
+ KratosService: Kratos service instance.
78
+
79
+ Raises:
80
+ KratosOperationError: If the Kratos dependency is not configured.
81
+ """
82
+ if dependency_config.kratos is None:
83
+ raise KratosOperationError(message="Kratos dependency not configured")
84
+ return KratosService(
85
+ kratos_http_config=dependency_config.kratos,
86
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fastapi_factory_utilities
3
- Version: 0.2.12
3
+ Version: 0.3.1
4
4
  Summary: Consolidate libraries and utilities to create microservices in Python with FastAPI, Beanie, Httpx, AioPika and OpenTelemetry.
5
5
  License: MIT
6
6
  Keywords: python,fastapi,beanie,httpx,opentelemetry,microservices
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Topic :: Software Development :: Libraries
19
19
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
20
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Dist: aiohttp[speedups] (>=3.11.16,<4.0.0)
21
22
  Requires-Dist: beanie (>=1.27.0,<2.0.0)
22
23
  Requires-Dist: fastapi (>=0.115.4,<0.116.0)
23
24
  Requires-Dist: httpx (>=0.28.1,<0.29.0)
@@ -29,6 +30,7 @@ Requires-Dist: opentelemetry-propagator-b3 (>=1.26.0,<2.0.0)
29
30
  Requires-Dist: opentelemetry-sdk (>=1.26.0,<2.0.0)
30
31
  Requires-Dist: pyaml (>=25.1.0,<26.0.0)
31
32
  Requires-Dist: pydantic (>=2.8.2,<3.0.0)
33
+ Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
32
34
  Requires-Dist: pymongo (>=4.9.2,<4.10.0)
33
35
  Requires-Dist: reactivex (>=4.0.4,<5.0.0)
34
36
  Requires-Dist: structlog (>=24.1,<26.0)
@@ -5,10 +5,10 @@ fastapi_factory_utilities/core/api/tags.py,sha256=3hQcTeW0FS78sPTJ2PB44dMDTSkoW-
5
5
  fastapi_factory_utilities/core/api/v1/sys/__init__.py,sha256=mTXhpn3_KgQ1snt0-0PFmGvFr4n5srQRRADEdRSGFJM,345
6
6
  fastapi_factory_utilities/core/api/v1/sys/health.py,sha256=IF51Z1seOFn91m3FC57U8uWfAA7c_EhhBpjbu_ly9WQ,2807
7
7
  fastapi_factory_utilities/core/api/v1/sys/readiness.py,sha256=xIY8pQLShU7KWRtlOUK5gTDyZ8aB1KBvLczC6boT-tg,1711
8
- fastapi_factory_utilities/core/app/__init__.py,sha256=I04abOkkWiY9ChgkUNeiwnuWq8kkBoRAtMLBUKB7J7Y,405
8
+ fastapi_factory_utilities/core/app/__init__.py,sha256=IHHkcu3t-fX8vxQk12A36cE-yTjGn_PInDpL_OlRSE8,596
9
9
  fastapi_factory_utilities/core/app/application.py,sha256=WrDXh00r_jzQTtZGeFO43lIzvDraplitejTaSJj_uFE,5091
10
10
  fastapi_factory_utilities/core/app/builder.py,sha256=VbThqoI1qWnADwPQ61D774oNZ5d6OMxW0tyXr_Yz5E4,4503
11
- fastapi_factory_utilities/core/app/config.py,sha256=7ELIoCy1AGCD4Zq7O-jZk6VJcqDU8H-00CSrvtwzdZE,6466
11
+ fastapi_factory_utilities/core/app/config.py,sha256=81KYoxB14TX05jMWujPNjLI1BXkvMnUyw2VgF7nJIE0,7063
12
12
  fastapi_factory_utilities/core/app/enums.py,sha256=X1upnaehYU0eHExXTde5xsH-pI9q7HZDNsOEF5PApdg,226
13
13
  fastapi_factory_utilities/core/app/exceptions.py,sha256=tQDf0_4j5xgCbku7TL7JaZGs3_bjsWG2YLBCydQJpPw,664
14
14
  fastapi_factory_utilities/core/app/fastapi_builder.py,sha256=DgIqiCnJK6cqsG-sg4H7Pi0lkhaxOhSLQt_ksHjpjW0,2835
@@ -25,13 +25,20 @@ fastapi_factory_utilities/core/plugins/odm_plugin/configs.py,sha256=zQoJC1wLNyq2
25
25
  fastapi_factory_utilities/core/plugins/odm_plugin/depends.py,sha256=OcLsfTLzMBk_xFV6qsMy_-qFkiphEbbEuaHUooagxg8,730
26
26
  fastapi_factory_utilities/core/plugins/odm_plugin/documents.py,sha256=BFQYHxHBmTacJRfhZi2OffvT_RAFvAAiDVQAa_d6Y7w,1141
27
27
  fastapi_factory_utilities/core/plugins/odm_plugin/exceptions.py,sha256=acnKJB0lGAzDs-7-LjBap8shjP3iV1a7dw7ouPVF27o,551
28
- fastapi_factory_utilities/core/plugins/odm_plugin/repositories.py,sha256=r880o--iVj3oK3yTenM5kgJwfFWKSUZa0bfnEwOukFE,8705
28
+ fastapi_factory_utilities/core/plugins/odm_plugin/repositories.py,sha256=56rZRcpyzFlClRbR4QO8cLJf6icsaUm3U10DzkOq2_E,11412
29
29
  fastapi_factory_utilities/core/plugins/opentelemetry_plugin/__init__.py,sha256=UsXPjiAASn5GIHW8vrF32mklxGNq8ajILV-ty4K1Tbs,4371
30
30
  fastapi_factory_utilities/core/plugins/opentelemetry_plugin/builder.py,sha256=9npQImifYAbEg0lFG7KwZ8V78SNrPoaINgd8vKitdMw,12509
31
31
  fastapi_factory_utilities/core/plugins/opentelemetry_plugin/configs.py,sha256=pMG9leMB7rtdkdGFLIxXflV7bf9epGrrYPt2N97KZcM,3750
32
32
  fastapi_factory_utilities/core/plugins/opentelemetry_plugin/exceptions.py,sha256=CpsHayfQpP0zghN8y5PP6TBy-cXhHoNxBR--I86gAdE,327
33
33
  fastapi_factory_utilities/core/plugins/opentelemetry_plugin/helpers.py,sha256=qpTIzX67orJz7vy6SBIwRs24omMBoToJkhpurZRjPuk,1533
34
34
  fastapi_factory_utilities/core/protocols.py,sha256=TzZKr_KfmTphk2LL-TD2XzxNlLbihbGM2DxWMhc5lEQ,2428
35
+ fastapi_factory_utilities/core/security/jwt.py,sha256=LuNVmTlONrmoKl7ghNv5JHV4qzMNOwuxJQlWgGSvBoo,5631
36
+ fastapi_factory_utilities/core/security/kratos.py,sha256=o6fkl2KfSFT7W_uDAwlLiVFl53r-neL3LQ45H3VqEIM,2350
37
+ fastapi_factory_utilities/core/services/kratos/__init__.py,sha256=DaC29-Ol0WR5vX56IHLGDXP9UrhISq0Juhg_sJTasw4,368
38
+ fastapi_factory_utilities/core/services/kratos/enums.py,sha256=ULJppowlZbOjdnUIXQyI4_nHmHZoNnv7-M1CYQBYXFY,220
39
+ fastapi_factory_utilities/core/services/kratos/exceptions.py,sha256=xAX01-lQvPpADgcwhB5YWSy1UqAxG38s2rlU9AJBJd8,472
40
+ fastapi_factory_utilities/core/services/kratos/objects.py,sha256=Dq9aEuwgwz21oLqJ4VWxrBB6ASJ7Xp_MEdyA3UbzWVE,956
41
+ fastapi_factory_utilities/core/services/kratos/services.py,sha256=RFKrME6M6omWiHORMBCSyvIG0BsimQaCfnmtsOdFfsg,3158
35
42
  fastapi_factory_utilities/core/services/status/__init__.py,sha256=N5H0cCN9ZFu_0YZar4RLdCDEjKMICrIhNtfKgB0LI78,370
36
43
  fastapi_factory_utilities/core/services/status/enums.py,sha256=IUxWAd0Ecknri4BqzaqoDRRhT_8LdcgtQcNqgNVDXGE,599
37
44
  fastapi_factory_utilities/core/services/status/exceptions.py,sha256=_fQFGqHKnG54Hs-ZtC4gs0xwzSH246_WwQOonraoGKw,856
@@ -64,8 +71,8 @@ fastapi_factory_utilities/example/models/books/repository.py,sha256=7K63uAsSEGZ2
64
71
  fastapi_factory_utilities/example/services/books/__init__.py,sha256=Z06yNRoA7Zg3TGN-Q9rrvJg6Bbx-qJw661MVwukV6vQ,148
65
72
  fastapi_factory_utilities/example/services/books/services.py,sha256=-x7d4hotUWLzWo5uImMjFmtNcSTHwWv2bfttIbYYKbA,5380
66
73
  fastapi_factory_utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
- fastapi_factory_utilities-0.2.12.dist-info/LICENSE,sha256=iO1nLzMMst6vEiqgSUrfrbetM7b0bvdzXhbed5tqG8o,1074
68
- fastapi_factory_utilities-0.2.12.dist-info/METADATA,sha256=1h_N321DsWiApHV0OZs9sRpo78nmxyD61x6-x80AJ00,3387
69
- fastapi_factory_utilities-0.2.12.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
70
- fastapi_factory_utilities-0.2.12.dist-info/entry_points.txt,sha256=IK0VcBexXo4uXQmTrbfhhnnfq4GmXPRn0GBB8hzlsq4,101
71
- fastapi_factory_utilities-0.2.12.dist-info/RECORD,,
74
+ fastapi_factory_utilities-0.3.1.dist-info/LICENSE,sha256=iO1nLzMMst6vEiqgSUrfrbetM7b0bvdzXhbed5tqG8o,1074
75
+ fastapi_factory_utilities-0.3.1.dist-info/METADATA,sha256=EF9C_1EW1EvdkTtdzsKjmOlW4o4BgBfi-RXXNtRERDc,3477
76
+ fastapi_factory_utilities-0.3.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
77
+ fastapi_factory_utilities-0.3.1.dist-info/entry_points.txt,sha256=IK0VcBexXo4uXQmTrbfhhnnfq4GmXPRn0GBB8hzlsq4,101
78
+ fastapi_factory_utilities-0.3.1.dist-info/RECORD,,