fastapi-factory-utilities 0.1.0__py3-none-any.whl → 0.2.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.

Files changed (54) hide show
  1. fastapi_factory_utilities/core/api/v1/sys/health.py +56 -12
  2. fastapi_factory_utilities/core/api/v1/sys/readiness.py +19 -12
  3. fastapi_factory_utilities/core/app/__init__.py +7 -12
  4. fastapi_factory_utilities/core/app/application.py +133 -0
  5. fastapi_factory_utilities/core/app/builder.py +123 -0
  6. fastapi_factory_utilities/core/app/config.py +164 -0
  7. fastapi_factory_utilities/core/app/exceptions.py +20 -0
  8. fastapi_factory_utilities/core/app/fastapi_builder.py +85 -0
  9. fastapi_factory_utilities/core/app/plugin_manager/__init__.py +15 -0
  10. fastapi_factory_utilities/core/app/plugin_manager/exceptions.py +33 -0
  11. fastapi_factory_utilities/core/app/plugin_manager/plugin_manager.py +190 -0
  12. fastapi_factory_utilities/core/exceptions.py +43 -0
  13. fastapi_factory_utilities/core/plugins/__init__.py +21 -0
  14. fastapi_factory_utilities/core/plugins/example/__init__.py +31 -0
  15. fastapi_factory_utilities/core/plugins/httpx_plugin/__init__.py +31 -0
  16. fastapi_factory_utilities/core/plugins/odm_plugin/__init__.py +74 -17
  17. fastapi_factory_utilities/core/plugins/odm_plugin/builder.py +27 -35
  18. fastapi_factory_utilities/core/plugins/odm_plugin/configs.py +1 -3
  19. fastapi_factory_utilities/core/plugins/odm_plugin/depends.py +30 -0
  20. fastapi_factory_utilities/core/plugins/opentelemetry_plugin/__init__.py +5 -5
  21. fastapi_factory_utilities/core/plugins/opentelemetry_plugin/builder.py +7 -7
  22. fastapi_factory_utilities/core/protocols.py +19 -16
  23. fastapi_factory_utilities/core/services/status/__init__.py +14 -0
  24. fastapi_factory_utilities/core/services/status/enums.py +30 -0
  25. fastapi_factory_utilities/core/services/status/exceptions.py +27 -0
  26. fastapi_factory_utilities/core/services/status/health_calculator_strategies.py +48 -0
  27. fastapi_factory_utilities/core/services/status/readiness_calculator_strategies.py +41 -0
  28. fastapi_factory_utilities/core/services/status/services.py +218 -0
  29. fastapi_factory_utilities/core/services/status/types.py +128 -0
  30. fastapi_factory_utilities/core/utils/configs.py +1 -1
  31. fastapi_factory_utilities/core/utils/status.py +71 -0
  32. fastapi_factory_utilities/core/utils/uvicorn.py +7 -8
  33. fastapi_factory_utilities/example/__init__.py +3 -3
  34. fastapi_factory_utilities/example/api/books/routes.py +7 -10
  35. fastapi_factory_utilities/example/app.py +50 -0
  36. fastapi_factory_utilities/example/application.yaml +5 -9
  37. fastapi_factory_utilities/example/services/books/__init__.py +2 -2
  38. fastapi_factory_utilities/example/services/books/services.py +9 -0
  39. fastapi_factory_utilities/py.typed +0 -0
  40. {fastapi_factory_utilities-0.1.0.dist-info → fastapi_factory_utilities-0.2.1.dist-info}/METADATA +6 -4
  41. fastapi_factory_utilities-0.2.1.dist-info/RECORD +71 -0
  42. {fastapi_factory_utilities-0.1.0.dist-info → fastapi_factory_utilities-0.2.1.dist-info}/WHEEL +1 -1
  43. fastapi_factory_utilities/core/app/base/__init__.py +0 -17
  44. fastapi_factory_utilities/core/app/base/application.py +0 -123
  45. fastapi_factory_utilities/core/app/base/config_abstract.py +0 -78
  46. fastapi_factory_utilities/core/app/base/exceptions.py +0 -25
  47. fastapi_factory_utilities/core/app/base/fastapi_application_abstract.py +0 -88
  48. fastapi_factory_utilities/core/app/base/plugins_manager_abstract.py +0 -136
  49. fastapi_factory_utilities/example/app/__init__.py +0 -6
  50. fastapi_factory_utilities/example/app/app.py +0 -37
  51. fastapi_factory_utilities/example/app/config.py +0 -12
  52. fastapi_factory_utilities-0.1.0.dist-info/RECORD +0 -58
  53. {fastapi_factory_utilities-0.1.0.dist-info → fastapi_factory_utilities-0.2.1.dist-info}/LICENSE +0 -0
  54. {fastapi_factory_utilities-0.1.0.dist-info → fastapi_factory_utilities-0.2.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,218 @@
1
+ """Provides status services."""
2
+
3
+ import datetime
4
+
5
+ from fastapi import Request
6
+ from reactivex import Subject
7
+ from structlog.stdlib import BoundLogger, get_logger
8
+
9
+ from .enums import ComponentTypeEnum, HealthStatusEnum, ReadinessStatusEnum
10
+ from .exceptions import ComponentRegistrationError
11
+ from .health_calculator_strategies import (
12
+ HealthCalculatorStrategy,
13
+ HealthSimpleCalculatorStrategy,
14
+ )
15
+ from .readiness_calculator_strategies import (
16
+ ReadinessCalculatorStrategy,
17
+ ReadinessSimpleCalculatorStrategy,
18
+ )
19
+ from .types import (
20
+ ComponentInstanceKey,
21
+ ComponentInstanceType,
22
+ Status,
23
+ StatusUpdateEvent,
24
+ )
25
+
26
+ logger: BoundLogger = get_logger(__package__)
27
+
28
+
29
+ class StatusService:
30
+ """Status service.
31
+
32
+ It's responsible for managing the status of the components and determine the health and readiness status of the
33
+ application.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ health_calculator_strategy: type[HealthCalculatorStrategy] = HealthSimpleCalculatorStrategy,
39
+ readiness_calculator_strategy: type[ReadinessCalculatorStrategy] = ReadinessSimpleCalculatorStrategy,
40
+ ) -> None:
41
+ """Initialize the status service."""
42
+ # Status
43
+ self._health_status: HealthStatusEnum = HealthStatusEnum.HEALTHY
44
+ self._health_calculator_strategy: type[HealthCalculatorStrategy] = health_calculator_strategy
45
+ self._readiness_status: ReadinessStatusEnum = ReadinessStatusEnum.NOT_READY
46
+ self._readiness_calculator_strategy: type[ReadinessCalculatorStrategy] = readiness_calculator_strategy
47
+ # Components
48
+ self._components: dict[str, ComponentInstanceType] = {}
49
+ self._components_status: dict[ComponentInstanceKey, Status] = {}
50
+ self._components_subjects: dict[ComponentInstanceKey, Subject[Status]] = {}
51
+ # Observers
52
+ self._status_subject: Subject[StatusUpdateEvent] = Subject()
53
+
54
+ def _compute_status(self) -> None:
55
+ """Compute the status."""
56
+ # Health
57
+ previous_health: HealthStatusEnum = self._health_status
58
+ new_health: HealthStatusEnum = self._health_calculator_strategy(
59
+ components_status=self._components_status
60
+ ).calculate()
61
+ # Readiness
62
+ previous_readiness: ReadinessStatusEnum = self._readiness_status
63
+ new_readiness: ReadinessStatusEnum = self._readiness_calculator_strategy(
64
+ components_status=self._components_status
65
+ ).calculate()
66
+ # Update the status if needed
67
+ if previous_health != new_health or previous_readiness != new_readiness:
68
+ logger.info(
69
+ "Status updated: health=%s, readiness=%s",
70
+ new_health,
71
+ new_readiness,
72
+ )
73
+ self._health_status = new_health
74
+ self._readiness_status = new_readiness
75
+ self._status_subject.on_next(
76
+ StatusUpdateEvent(
77
+ health_status=new_health,
78
+ previous_health_status=previous_health,
79
+ readiness_status=new_readiness,
80
+ previous_readiness_status=previous_readiness,
81
+ triggered_at=datetime.datetime.now(),
82
+ )
83
+ )
84
+
85
+ def get_status(self) -> Status:
86
+ """Get the status.
87
+
88
+ Returns:
89
+ Status: The status.
90
+ """
91
+ return Status(
92
+ health=self._health_status,
93
+ readiness=self._readiness_status,
94
+ )
95
+
96
+ def get_components_status_by_type(self) -> dict[ComponentTypeEnum, dict[ComponentInstanceKey, Status]]:
97
+ """Get the components status.
98
+
99
+ Returns:
100
+ dict[str, dict[str, Status]]: The components status.
101
+ """
102
+ result: dict[ComponentTypeEnum, dict[ComponentInstanceKey, Status]] = {}
103
+ for component_instance in self._components.values():
104
+ component_type: ComponentTypeEnum = component_instance.component_type
105
+ if component_type not in result:
106
+ result[component_type] = {}
107
+ result[component_type][component_instance.key] = self._components_status[component_instance.key]
108
+ return result
109
+
110
+ def _on_next_for_component_instance(self, component_instance: ComponentInstanceType, event: Status) -> None:
111
+ """On next subscribe for all component instances updates.
112
+
113
+ Args:
114
+ component_instance (ComponentInstanceType): The component instance.
115
+ event (Status): The status event.
116
+
117
+ Raises:
118
+ ComponentRegistrationError: If the component instance is not registered.
119
+ """
120
+ # Check if the component instance is registered
121
+ if component_instance.key not in self._components_status:
122
+ raise ComponentRegistrationError(component_instance=component_instance)
123
+
124
+ # Update the component instance status
125
+ self._components_status[component_instance.key] = Status(
126
+ health=event["health"],
127
+ readiness=event["readiness"],
128
+ )
129
+
130
+ # Compute the status
131
+ self._compute_status()
132
+
133
+ def _register_component_instance_internaly(self, component_instance: ComponentInstanceType) -> None:
134
+ """Register the component instance internally.
135
+
136
+ Args:
137
+ component_instance (ComponentInstanceType): The component instance.
138
+
139
+ Raises:
140
+ ComponentRegistrationError: If the component instance is already registered.
141
+ """
142
+ # Check if the component instance is already registered
143
+ if component_instance.key in self._components:
144
+ raise ComponentRegistrationError(component_instance=component_instance)
145
+ # Register the component instance
146
+ self._components[component_instance.key] = component_instance
147
+ # Register the component instance status
148
+ self._components_status[component_instance.key] = Status(
149
+ health=HealthStatusEnum.HEALTHY,
150
+ readiness=ReadinessStatusEnum.NOT_READY,
151
+ )
152
+
153
+ def _create_and_subscribe_to_component_instance_subject(
154
+ self, component_instance: ComponentInstanceType
155
+ ) -> Subject[Status]:
156
+ """Create and subscribe to the component instance subject.
157
+
158
+ Args:
159
+ component_instance (ComponentInstanceType): The component instance.
160
+
161
+ Returns:
162
+ Subject[Status]: The observer.
163
+
164
+ Raises:
165
+ ComponentRegistrationError: If the component instance is already registered.
166
+ """
167
+
168
+ # Setup the subject and subscribe to the subject
169
+ def on_next(event: Status) -> None:
170
+ """Currying the on_next method for the component instance and delegate to the service method."""
171
+ self._on_next_for_component_instance(
172
+ component_instance=component_instance,
173
+ event=event,
174
+ )
175
+
176
+ subject: Subject[Status] = Subject()
177
+ # Check if the component instance is already registered
178
+ if component_instance.key in self._components_subjects:
179
+ raise ComponentRegistrationError(component_instance=component_instance)
180
+ # Register the component instance subject
181
+ self._components_subjects[component_instance.key] = subject
182
+ subject.subscribe(on_next=on_next)
183
+
184
+ return subject
185
+
186
+ def register_component_instance(
187
+ self,
188
+ component_instance: ComponentInstanceType,
189
+ ) -> Subject[Status]:
190
+ """Register the component instance to the status service.
191
+
192
+ It will create a subject for the component instance to share status update and
193
+ the status service will subscribe automatically to it.
194
+
195
+ Args:
196
+ component_instance (ComponentInstanceType): The component instance.
197
+
198
+ Returns:
199
+ Subject[Status]: The observer.
200
+
201
+ Raises:
202
+ ComponentRegistrationError: If the component instance is already registered.
203
+ """
204
+ self._register_component_instance_internaly(component_instance=component_instance)
205
+ subject: Subject[Status] = self._create_and_subscribe_to_component_instance_subject(
206
+ component_instance=component_instance
207
+ )
208
+ logger.debug(
209
+ "Component instance registered to the status service key=%s",
210
+ component_instance.key,
211
+ component_instance=component_instance,
212
+ )
213
+ return subject
214
+
215
+
216
+ def depends_status_service(request: Request) -> StatusService:
217
+ """Get the status service, through fastapi depends."""
218
+ return request.app.state.status_service
@@ -0,0 +1,128 @@
1
+ """Provides the status types for the service."""
2
+
3
+ import datetime
4
+ from typing import ClassVar, NewType, TypedDict, cast
5
+
6
+ from pydantic import BaseModel, ConfigDict
7
+
8
+ from .enums import ComponentTypeEnum, HealthStatusEnum, ReadinessStatusEnum
9
+
10
+ ComponentInstanceKey = NewType("ComponentInstanceKey", str)
11
+
12
+
13
+ class ComponentInstanceType:
14
+ """Component instance type.
15
+
16
+ Attributes:
17
+ component_type (ComponentTypeEnum): The component type.
18
+ identifier (str | None): The identifier.
19
+ key (ComponentInstanceKey): The key based on the component type and identifier.
20
+ """
21
+
22
+ def _generate_key(self) -> ComponentInstanceKey:
23
+ """Generate the key identifier for the component instance.
24
+
25
+ It is based on the component type and identifier.
26
+
27
+ Returns:
28
+ str: The key.
29
+
30
+ """
31
+ key: str = (
32
+ f"{self._component_type.value}:{self._identifier}" if self._identifier else self._component_type.value
33
+ )
34
+ return cast(ComponentInstanceKey, key)
35
+
36
+ def __init__(self, component_type: ComponentTypeEnum, identifier: str | None = None) -> None:
37
+ """Initialize the component instance type.
38
+
39
+ Args:
40
+ component_type (ComponentTypeEnum): The component type.
41
+ identifier (str, optional): The identifier. Defaults to None.
42
+
43
+ """
44
+ self._component_type: ComponentTypeEnum = component_type
45
+ self._identifier: str | None = identifier
46
+ self._key: ComponentInstanceKey = self._generate_key()
47
+
48
+ @property
49
+ def component_type(self) -> ComponentTypeEnum:
50
+ """Get the component type.
51
+
52
+ Returns:
53
+ ComponentTypeEnum: The component type.
54
+
55
+ """
56
+ return self._component_type
57
+
58
+ @property
59
+ def identifier(self) -> str | None:
60
+ """Get the identifier.
61
+
62
+ Returns:
63
+ str | None: The identifier.
64
+
65
+ """
66
+ return self._identifier
67
+
68
+ @property
69
+ def key(self) -> ComponentInstanceKey:
70
+ """Get the key.
71
+
72
+ Returns:
73
+ str: The key.
74
+
75
+ """
76
+ return self._key
77
+
78
+
79
+ class Status(TypedDict):
80
+ """Status type.
81
+
82
+ Attributes:
83
+ health (HealthStatusEnum): The health status.
84
+ readiness (ReadinessStatusEnum): The readiness status.
85
+ """
86
+
87
+ health: HealthStatusEnum
88
+ readiness: ReadinessStatusEnum
89
+
90
+
91
+ class StatusUpdateEvent(BaseModel):
92
+ """Status update event."""
93
+
94
+ # Pydantic config
95
+ model_config: ClassVar[ConfigDict] = {"frozen": True}
96
+
97
+ # Health status
98
+ health_status: HealthStatusEnum
99
+ previous_health_status: HealthStatusEnum
100
+
101
+ # Readiness status
102
+ readiness_status: ReadinessStatusEnum
103
+ previous_readiness_status: ReadinessStatusEnum
104
+
105
+ # Timestamp
106
+ triggered_at: datetime.datetime
107
+
108
+
109
+ class ComponentInstanceStatusUpdateEvent(BaseModel):
110
+ """Component instance status update event."""
111
+
112
+ # Pydantic config
113
+ model_config: ClassVar[ConfigDict] = {
114
+ "frozen": True,
115
+ "arbitrary_types_allowed": True, # Needed for the ComponentInstanceType
116
+ }
117
+
118
+ # Component instance
119
+ component_instance: ComponentInstanceType
120
+
121
+ # Health status
122
+ health_status: HealthStatusEnum
123
+
124
+ # Readiness status
125
+ readiness_status: ReadinessStatusEnum
126
+
127
+ # Timestamp
128
+ triggered_at: datetime.datetime
@@ -41,7 +41,7 @@ def build_config_from_file_in_package(
41
41
  package_name: str,
42
42
  filename: str,
43
43
  config_class: type[GenericConfigBaseModelType],
44
- yaml_base_key: str,
44
+ yaml_base_key: str | None = None,
45
45
  ) -> GenericConfigBaseModelType:
46
46
  """Build a configuration object from a file in a package.
47
47
 
@@ -0,0 +1,71 @@
1
+ """Provides the monitored abstract class for monitoring the status of the application.
2
+
3
+ ```python
4
+ # Example of using the MonitoredAbstract
5
+
6
+ class MyMonitored(MonitoredAbstract):
7
+ def __init__(self, status_service: StatusService) -> None:
8
+ super().__init__(
9
+ component_instance=ComponentInstanceType(
10
+ component_type=ComponentTypeEnum.APPLICATION,
11
+ component_name="MyMonitored",
12
+ ),
13
+ status_service=status_service,
14
+ )
15
+
16
+ def my_method(self) -> None:
17
+ self.update_monitoring_status(
18
+ Status(
19
+ status_type=StatusTypeEnum.INFO,
20
+ status_message="My method is running.",
21
+ )
22
+ )
23
+ ```
24
+
25
+ """
26
+
27
+ from abc import ABC
28
+
29
+ from reactivex import Subject
30
+
31
+ from fastapi_factory_utilities.core.services.status import (
32
+ ComponentInstanceType,
33
+ ComponentTypeEnum,
34
+ Status,
35
+ StatusService,
36
+ )
37
+
38
+
39
+ class MonitoredAbstract(ABC):
40
+ """Monitored abstract class."""
41
+
42
+ def __init__(self, component_instance: ComponentInstanceType, status_service: StatusService) -> None:
43
+ """Initialize the monitored.
44
+
45
+ Args:
46
+ component_instance (ComponentInstanceType): The component instance.
47
+ status_service (StatusService): The status service.
48
+
49
+ """
50
+ self._monit_component_instance: ComponentInstanceType = component_instance
51
+ self._monit_status_service_subject: Subject[Status] = status_service.register_component_instance(
52
+ component_instance=component_instance
53
+ )
54
+
55
+ def update_monitoring_status(self, status: Status) -> None:
56
+ """Update the monitoring status.
57
+
58
+ Args:
59
+ status (Status): The status.
60
+
61
+ """
62
+ self._monit_status_service_subject.on_next(status)
63
+
64
+
65
+ __all__: list[str] = [
66
+ "MonitoredAbstract",
67
+ "ComponentInstanceType",
68
+ "ComponentTypeEnum",
69
+ "Status",
70
+ "StatusService",
71
+ ]
@@ -3,24 +3,23 @@
3
3
  import uvicorn
4
4
  import uvicorn.server
5
5
 
6
- from fastapi_factory_utilities.core.protocols import BaseApplicationProtocol
6
+ from fastapi_factory_utilities.core.protocols import ApplicationAbstractProtocol
7
7
  from fastapi_factory_utilities.core.utils.log import clean_uvicorn_logger
8
8
 
9
9
 
10
10
  class UvicornUtils:
11
11
  """Provides utilities for Uvicorn."""
12
12
 
13
- def __init__(self, app: BaseApplicationProtocol) -> None:
13
+ def __init__(self, app: ApplicationAbstractProtocol) -> None:
14
14
  """Instantiate the factory.
15
15
 
16
16
  Args:
17
17
  app (BaseApplication): The application.
18
- config (AppConfigAbstract): The application configuration.
19
18
 
20
19
  Returns:
21
20
  None
22
21
  """
23
- self._app: BaseApplicationProtocol = app
22
+ self._app: ApplicationAbstractProtocol = app
24
23
 
25
24
  def build_uvicorn_config(self) -> uvicorn.Config:
26
25
  """Build the Uvicorn configuration.
@@ -30,10 +29,10 @@ class UvicornUtils:
30
29
  """
31
30
  config = uvicorn.Config(
32
31
  app=self._app.get_asgi_app(),
33
- host=self._app.get_config().host,
34
- port=self._app.get_config().port,
35
- reload=self._app.get_config().reload,
36
- workers=self._app.get_config().workers,
32
+ host=self._app.get_config().server.host,
33
+ port=self._app.get_config().server.port,
34
+ reload=self._app.get_config().development.reload,
35
+ workers=self._app.get_config().server.workers,
37
36
  )
38
37
  clean_uvicorn_logger()
39
38
  return config
@@ -1,11 +1,11 @@
1
1
  """Python Factory Example."""
2
2
 
3
- from fastapi_factory_utilities.example.app import App
3
+ from fastapi_factory_utilities.example.app import AppBuilder
4
4
 
5
5
 
6
6
  def main() -> None:
7
7
  """Main function."""
8
- App.main()
8
+ AppBuilder().build_and_serve()
9
9
 
10
10
 
11
- __all__: list[str] = ["App", "main"]
11
+ __all__: list[str] = ["main"]
@@ -3,11 +3,13 @@
3
3
  from typing import cast
4
4
  from uuid import UUID
5
5
 
6
- from fastapi import APIRouter, Depends, Request
6
+ from fastapi import APIRouter, Depends
7
7
 
8
8
  from fastapi_factory_utilities.example.entities.books import BookEntity
9
- from fastapi_factory_utilities.example.models.books.repository import BookRepository
10
- from fastapi_factory_utilities.example.services.books import BookService
9
+ from fastapi_factory_utilities.example.services.books import (
10
+ BookService,
11
+ depends_book_service,
12
+ )
11
13
 
12
14
  from .responses import BookListReponse, BookResponseModel
13
15
 
@@ -15,14 +17,9 @@ api_v1_books_router: APIRouter = APIRouter(prefix="/books")
15
17
  api_v2_books_router: APIRouter = APIRouter(prefix="/books")
16
18
 
17
19
 
18
- def get_book_service(request: Request) -> BookService:
19
- """Provide Book Service."""
20
- return BookService(book_repository=BookRepository(request.app.state.odm_client))
21
-
22
-
23
20
  @api_v1_books_router.get(path="", response_model=BookListReponse)
24
21
  def get_books(
25
- books_service: BookService = Depends(get_book_service),
22
+ books_service: BookService = Depends(depends_book_service),
26
23
  ) -> BookListReponse:
27
24
  """Get all books.
28
25
 
@@ -46,7 +43,7 @@ def get_books(
46
43
  @api_v1_books_router.get(path="/{book_id}", response_model=BookResponseModel)
47
44
  def get_book(
48
45
  book_id: UUID,
49
- books_service: BookService = Depends(get_book_service),
46
+ books_service: BookService = Depends(depends_book_service),
50
47
  ) -> BookResponseModel:
51
48
  """Get a book.
52
49
 
@@ -0,0 +1,50 @@
1
+ """Provides the concrete application class."""
2
+
3
+ from typing import ClassVar
4
+
5
+ from beanie import Document
6
+
7
+ from fastapi_factory_utilities.core.app.application import ApplicationAbstract
8
+ from fastapi_factory_utilities.core.app.builder import ApplicationGenericBuilder
9
+ from fastapi_factory_utilities.core.app.config import RootConfig
10
+ from fastapi_factory_utilities.core.plugins import PluginsEnum
11
+ from fastapi_factory_utilities.example.models.books.document import BookDocument
12
+
13
+
14
+ class AppRootConfig(RootConfig):
15
+ """Application configuration class."""
16
+
17
+ pass
18
+
19
+
20
+ class App(ApplicationAbstract):
21
+ """Concrete application class."""
22
+
23
+ CONFIG_CLASS: ClassVar[type[RootConfig]] = AppRootConfig
24
+
25
+ PACKAGE_NAME: ClassVar[str] = "fastapi_factory_utilities.example"
26
+
27
+ ODM_DOCUMENT_MODELS: ClassVar[list[type[Document]]] = [BookDocument]
28
+
29
+ DEFAULT_PLUGINS_ACTIVATED: ClassVar[list[PluginsEnum]] = [PluginsEnum.OPENTELEMETRY_PLUGIN, PluginsEnum.ODM_PLUGIN]
30
+
31
+ def configure(self) -> None:
32
+ """Configure the application."""
33
+ # Prevent circular import
34
+ from .api import api_router # pylint: disable=import-outside-toplevel
35
+
36
+ self.get_asgi_app().include_router(router=api_router)
37
+
38
+ async def on_startup(self) -> None:
39
+ """Actions to perform on application startup."""
40
+ pass
41
+
42
+ async def on_shutdown(self) -> None:
43
+ """Actions to perform on application shutdown."""
44
+ pass
45
+
46
+
47
+ class AppBuilder(ApplicationGenericBuilder[App]):
48
+ """Application builder for the App application."""
49
+
50
+ pass
@@ -1,19 +1,15 @@
1
1
  ---
2
2
  application:
3
- service_name: fastapi_factory_utilities_example
4
- service_namespace: fastapi_factory_utilities_example
5
- title: Python Factory Example
6
- description: An example application for Python Factory
3
+ service_name: Example Application
4
+ service_namespace: fastapi_factory_utilities
5
+ description: An example application using fastapi-factory-utilities
7
6
  version: 0.1.0
8
7
  environment: ${ENVIRONMENT:development}
8
+
9
+ development:
9
10
  debug: ${APPLICATION_DEBUG:false}
10
11
  reload: ${APPLICATION_RELOAD:false}
11
12
 
12
- plugins:
13
- activate:
14
- - opentelemetry_plugin
15
- - odm_plugin
16
-
17
13
  opentelemetry:
18
14
  activate: "${OTEL_ACTIVE:false}"
19
15
 
@@ -1,5 +1,5 @@
1
1
  """Provide Books Service."""
2
2
 
3
- from .services import BookService
3
+ from .services import BookService, depends_book_service
4
4
 
5
- __all__: list[str] = ["BookService"]
5
+ __all__: list[str] = ["BookService", "depends_book_service"]
@@ -3,8 +3,12 @@
3
3
  from typing import ClassVar
4
4
  from uuid import UUID
5
5
 
6
+ from fastapi import Request
6
7
  from opentelemetry import metrics
7
8
 
9
+ from fastapi_factory_utilities.core.plugins.odm_plugin.depends import (
10
+ depends_odm_database,
11
+ )
8
12
  from fastapi_factory_utilities.core.plugins.opentelemetry_plugin.helpers import (
9
13
  trace_span,
10
14
  )
@@ -165,3 +169,8 @@ class BookService:
165
169
  self.book_store[book.id] = book
166
170
 
167
171
  self.METER_COUNTER_BOOK_UPDATE.add(amount=1)
172
+
173
+
174
+ def depends_book_service(request: Request) -> BookService:
175
+ """Provide Book Service."""
176
+ return BookService(book_repository=BookRepository(database=depends_odm_database(request=request)))
File without changes
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: fastapi_factory_utilities
3
- Version: 0.1.0
3
+ Version: 0.2.1
4
4
  Summary: Consolidate libraries and utilities to create microservices in Python with FastAPI, Beanie, Httpx, AioPika and OpenTelemetry.
5
- Home-page: https://github.com/miragecentury/fastapi_factory_utilities
6
5
  License: MIT
7
6
  Keywords: python,fastapi,beanie,httpx,opentelemetry,microservices
8
7
  Author: miragecentury
@@ -27,11 +26,14 @@ Requires-Dist: opentelemetry-instrumentation-fastapi (>=0.49b1,<0.50)
27
26
  Requires-Dist: opentelemetry-instrumentation-pymongo (>=0.49b2,<0.50)
28
27
  Requires-Dist: opentelemetry-propagator-b3 (>=1.26.0,<2.0.0)
29
28
  Requires-Dist: opentelemetry-sdk (>=1.26.0,<2.0.0)
29
+ Requires-Dist: pyaml (>=25.1.0,<26.0.0)
30
30
  Requires-Dist: pydantic (>=2.8.2,<3.0.0)
31
31
  Requires-Dist: pymongo (>=4.9.2,<4.10.0)
32
- Requires-Dist: structlog (>=24.1.0,<25.0.0)
32
+ Requires-Dist: reactivex (>=4.0.4,<5.0.0)
33
+ Requires-Dist: structlog (>=24.1,<26.0)
33
34
  Requires-Dist: typer (>=0.15.1,<0.16.0)
34
35
  Requires-Dist: uvicorn (>=0.32.0,<0.33.0)
36
+ Project-URL: Homepage, https://github.com/miragecentury/fastapi_factory_utilities
35
37
  Project-URL: Repository, https://github.com/miragecentury/fastapi_factory_utilities
36
38
  Description-Content-Type: text/markdown
37
39