julee 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 (161) hide show
  1. julee/__init__.py +3 -0
  2. julee/api/__init__.py +20 -0
  3. julee/api/app.py +180 -0
  4. julee/api/dependencies.py +257 -0
  5. julee/api/requests.py +175 -0
  6. julee/api/responses.py +43 -0
  7. julee/api/routers/__init__.py +43 -0
  8. julee/api/routers/assembly_specifications.py +212 -0
  9. julee/api/routers/documents.py +182 -0
  10. julee/api/routers/knowledge_service_configs.py +79 -0
  11. julee/api/routers/knowledge_service_queries.py +293 -0
  12. julee/api/routers/system.py +137 -0
  13. julee/api/routers/workflows.py +234 -0
  14. julee/api/services/__init__.py +20 -0
  15. julee/api/services/system_initialization.py +214 -0
  16. julee/api/tests/__init__.py +14 -0
  17. julee/api/tests/routers/__init__.py +17 -0
  18. julee/api/tests/routers/test_assembly_specifications.py +749 -0
  19. julee/api/tests/routers/test_documents.py +301 -0
  20. julee/api/tests/routers/test_knowledge_service_configs.py +234 -0
  21. julee/api/tests/routers/test_knowledge_service_queries.py +738 -0
  22. julee/api/tests/routers/test_system.py +179 -0
  23. julee/api/tests/routers/test_workflows.py +393 -0
  24. julee/api/tests/test_app.py +285 -0
  25. julee/api/tests/test_dependencies.py +245 -0
  26. julee/api/tests/test_requests.py +250 -0
  27. julee/domain/__init__.py +22 -0
  28. julee/domain/models/__init__.py +49 -0
  29. julee/domain/models/assembly/__init__.py +17 -0
  30. julee/domain/models/assembly/assembly.py +103 -0
  31. julee/domain/models/assembly/tests/__init__.py +0 -0
  32. julee/domain/models/assembly/tests/factories.py +37 -0
  33. julee/domain/models/assembly/tests/test_assembly.py +430 -0
  34. julee/domain/models/assembly_specification/__init__.py +24 -0
  35. julee/domain/models/assembly_specification/assembly_specification.py +172 -0
  36. julee/domain/models/assembly_specification/knowledge_service_query.py +123 -0
  37. julee/domain/models/assembly_specification/tests/__init__.py +0 -0
  38. julee/domain/models/assembly_specification/tests/factories.py +78 -0
  39. julee/domain/models/assembly_specification/tests/test_assembly_specification.py +490 -0
  40. julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +310 -0
  41. julee/domain/models/custom_fields/__init__.py +0 -0
  42. julee/domain/models/custom_fields/content_stream.py +68 -0
  43. julee/domain/models/custom_fields/tests/__init__.py +0 -0
  44. julee/domain/models/custom_fields/tests/test_custom_fields.py +53 -0
  45. julee/domain/models/document/__init__.py +17 -0
  46. julee/domain/models/document/document.py +150 -0
  47. julee/domain/models/document/tests/__init__.py +0 -0
  48. julee/domain/models/document/tests/factories.py +76 -0
  49. julee/domain/models/document/tests/test_document.py +297 -0
  50. julee/domain/models/knowledge_service_config/__init__.py +17 -0
  51. julee/domain/models/knowledge_service_config/knowledge_service_config.py +86 -0
  52. julee/domain/models/policy/__init__.py +15 -0
  53. julee/domain/models/policy/document_policy_validation.py +220 -0
  54. julee/domain/models/policy/policy.py +203 -0
  55. julee/domain/models/policy/tests/__init__.py +0 -0
  56. julee/domain/models/policy/tests/factories.py +47 -0
  57. julee/domain/models/policy/tests/test_document_policy_validation.py +420 -0
  58. julee/domain/models/policy/tests/test_policy.py +546 -0
  59. julee/domain/repositories/__init__.py +27 -0
  60. julee/domain/repositories/assembly.py +45 -0
  61. julee/domain/repositories/assembly_specification.py +52 -0
  62. julee/domain/repositories/base.py +146 -0
  63. julee/domain/repositories/document.py +49 -0
  64. julee/domain/repositories/document_policy_validation.py +52 -0
  65. julee/domain/repositories/knowledge_service_config.py +54 -0
  66. julee/domain/repositories/knowledge_service_query.py +44 -0
  67. julee/domain/repositories/policy.py +49 -0
  68. julee/domain/use_cases/__init__.py +17 -0
  69. julee/domain/use_cases/decorators.py +107 -0
  70. julee/domain/use_cases/extract_assemble_data.py +649 -0
  71. julee/domain/use_cases/initialize_system_data.py +842 -0
  72. julee/domain/use_cases/tests/__init__.py +7 -0
  73. julee/domain/use_cases/tests/test_extract_assemble_data.py +548 -0
  74. julee/domain/use_cases/tests/test_initialize_system_data.py +455 -0
  75. julee/domain/use_cases/tests/test_validate_document.py +1228 -0
  76. julee/domain/use_cases/validate_document.py +736 -0
  77. julee/fixtures/assembly_specifications.yaml +70 -0
  78. julee/fixtures/documents.yaml +178 -0
  79. julee/fixtures/knowledge_service_configs.yaml +37 -0
  80. julee/fixtures/knowledge_service_queries.yaml +27 -0
  81. julee/repositories/__init__.py +17 -0
  82. julee/repositories/memory/__init__.py +31 -0
  83. julee/repositories/memory/assembly.py +84 -0
  84. julee/repositories/memory/assembly_specification.py +125 -0
  85. julee/repositories/memory/base.py +227 -0
  86. julee/repositories/memory/document.py +149 -0
  87. julee/repositories/memory/document_policy_validation.py +104 -0
  88. julee/repositories/memory/knowledge_service_config.py +123 -0
  89. julee/repositories/memory/knowledge_service_query.py +120 -0
  90. julee/repositories/memory/policy.py +87 -0
  91. julee/repositories/memory/tests/__init__.py +0 -0
  92. julee/repositories/memory/tests/test_document.py +212 -0
  93. julee/repositories/memory/tests/test_document_policy_validation.py +161 -0
  94. julee/repositories/memory/tests/test_policy.py +443 -0
  95. julee/repositories/minio/__init__.py +31 -0
  96. julee/repositories/minio/assembly.py +103 -0
  97. julee/repositories/minio/assembly_specification.py +170 -0
  98. julee/repositories/minio/client.py +570 -0
  99. julee/repositories/minio/document.py +530 -0
  100. julee/repositories/minio/document_policy_validation.py +120 -0
  101. julee/repositories/minio/knowledge_service_config.py +187 -0
  102. julee/repositories/minio/knowledge_service_query.py +211 -0
  103. julee/repositories/minio/policy.py +106 -0
  104. julee/repositories/minio/tests/__init__.py +0 -0
  105. julee/repositories/minio/tests/fake_client.py +213 -0
  106. julee/repositories/minio/tests/test_assembly.py +374 -0
  107. julee/repositories/minio/tests/test_assembly_specification.py +391 -0
  108. julee/repositories/minio/tests/test_client_protocol.py +57 -0
  109. julee/repositories/minio/tests/test_document.py +591 -0
  110. julee/repositories/minio/tests/test_document_policy_validation.py +192 -0
  111. julee/repositories/minio/tests/test_knowledge_service_config.py +374 -0
  112. julee/repositories/minio/tests/test_knowledge_service_query.py +438 -0
  113. julee/repositories/minio/tests/test_policy.py +559 -0
  114. julee/repositories/temporal/__init__.py +38 -0
  115. julee/repositories/temporal/activities.py +114 -0
  116. julee/repositories/temporal/activity_names.py +34 -0
  117. julee/repositories/temporal/proxies.py +159 -0
  118. julee/services/__init__.py +18 -0
  119. julee/services/knowledge_service/__init__.py +48 -0
  120. julee/services/knowledge_service/anthropic/__init__.py +12 -0
  121. julee/services/knowledge_service/anthropic/knowledge_service.py +331 -0
  122. julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +318 -0
  123. julee/services/knowledge_service/factory.py +138 -0
  124. julee/services/knowledge_service/knowledge_service.py +160 -0
  125. julee/services/knowledge_service/memory/__init__.py +13 -0
  126. julee/services/knowledge_service/memory/knowledge_service.py +278 -0
  127. julee/services/knowledge_service/memory/test_knowledge_service.py +345 -0
  128. julee/services/knowledge_service/test_factory.py +112 -0
  129. julee/services/temporal/__init__.py +38 -0
  130. julee/services/temporal/activities.py +86 -0
  131. julee/services/temporal/activity_names.py +22 -0
  132. julee/services/temporal/proxies.py +41 -0
  133. julee/util/__init__.py +0 -0
  134. julee/util/domain.py +119 -0
  135. julee/util/repos/__init__.py +0 -0
  136. julee/util/repos/minio/__init__.py +0 -0
  137. julee/util/repos/minio/file_storage.py +213 -0
  138. julee/util/repos/temporal/__init__.py +11 -0
  139. julee/util/repos/temporal/client_proxies/file_storage.py +68 -0
  140. julee/util/repos/temporal/data_converter.py +123 -0
  141. julee/util/repos/temporal/minio_file_storage.py +12 -0
  142. julee/util/repos/temporal/proxies/__init__.py +0 -0
  143. julee/util/repos/temporal/proxies/file_storage.py +58 -0
  144. julee/util/repositories.py +55 -0
  145. julee/util/temporal/__init__.py +22 -0
  146. julee/util/temporal/activities.py +123 -0
  147. julee/util/temporal/decorators.py +473 -0
  148. julee/util/tests/__init__.py +1 -0
  149. julee/util/tests/test_decorators.py +770 -0
  150. julee/util/validation/__init__.py +29 -0
  151. julee/util/validation/repository.py +100 -0
  152. julee/util/validation/type_guards.py +369 -0
  153. julee/worker.py +211 -0
  154. julee/workflows/__init__.py +26 -0
  155. julee/workflows/extract_assemble.py +215 -0
  156. julee/workflows/validate_document.py +228 -0
  157. julee-0.1.0.dist-info/METADATA +195 -0
  158. julee-0.1.0.dist-info/RECORD +161 -0
  159. julee-0.1.0.dist-info/WHEEL +5 -0
  160. julee-0.1.0.dist-info/licenses/LICENSE +674 -0
  161. julee-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,187 @@
1
+ """
2
+ Minio implementation of KnowledgeServiceConfigRepository.
3
+
4
+ This module provides a Minio-based implementation of the
5
+ KnowledgeServiceConfigRepository
6
+ protocol that follows the Clean Architecture patterns defined in the
7
+ Fun-Police Framework. It handles knowledge service configuration storage
8
+ as JSON objects in Minio, ensuring idempotency and proper error handling.
9
+
10
+ The implementation stores knowledge service configurations as JSON objects
11
+ in Minio, following the large payload handling pattern from the architectural
12
+ guidelines. Each configuration is stored with its knowledge_service_id as the
13
+ key.
14
+ """
15
+
16
+ import logging
17
+ from typing import Optional, List, Dict
18
+
19
+ from julee.domain.models.knowledge_service_config import (
20
+ KnowledgeServiceConfig,
21
+ )
22
+ from julee.domain.repositories.knowledge_service_config import (
23
+ KnowledgeServiceConfigRepository,
24
+ )
25
+ from .client import MinioClient, MinioRepositoryMixin
26
+
27
+
28
+ class MinioKnowledgeServiceConfigRepository(
29
+ KnowledgeServiceConfigRepository, MinioRepositoryMixin
30
+ ):
31
+ """
32
+ Minio implementation of KnowledgeServiceConfigRepository using Minio for
33
+ persistence.
34
+
35
+ This implementation stores knowledge service configurations as JSON
36
+ objects:
37
+ - Knowledge Service Configs: JSON objects in the
38
+ "knowledge-service-configs" bucket
39
+
40
+ Each configuration is stored with its knowledge_service_id as the object
41
+ name
42
+ for efficient retrieval and updates.
43
+ """
44
+
45
+ def __init__(self, client: MinioClient) -> None:
46
+ """Initialize repository with Minio client.
47
+
48
+ Args:
49
+ client: MinioClient protocol implementation (real or fake)
50
+ """
51
+ self.client = client
52
+ self.logger = logging.getLogger("MinioKnowledgeServiceConfigRepository")
53
+ self.bucket_name = "knowledge-service-configs"
54
+ self.ensure_buckets_exist(self.bucket_name)
55
+
56
+ async def get(self, knowledge_service_id: str) -> Optional[KnowledgeServiceConfig]:
57
+ """Retrieve a knowledge service configuration by ID.
58
+
59
+ Args:
60
+ knowledge_service_id: Unique knowledge service identifier
61
+
62
+ Returns:
63
+ KnowledgeServiceConfig object if found, None otherwise
64
+ """
65
+ object_name = f"config/{knowledge_service_id}"
66
+
67
+ return self.get_json_object(
68
+ bucket_name=self.bucket_name,
69
+ object_name=object_name,
70
+ model_class=KnowledgeServiceConfig,
71
+ not_found_log_message="Knowledge service config not found",
72
+ error_log_message="Error retrieving knowledge service config",
73
+ extra_log_data={"knowledge_service_id": knowledge_service_id},
74
+ )
75
+
76
+ async def save(self, knowledge_service: KnowledgeServiceConfig) -> None:
77
+ """Save a knowledge service configuration.
78
+
79
+ Args:
80
+ knowledge_service: Complete KnowledgeServiceConfig to save
81
+ """
82
+ # Update timestamps
83
+ self.update_timestamps(knowledge_service)
84
+
85
+ object_name = f"config/{knowledge_service.knowledge_service_id}"
86
+
87
+ self.put_json_object(
88
+ bucket_name=self.bucket_name,
89
+ object_name=object_name,
90
+ model=knowledge_service,
91
+ success_log_message="Knowledge service config saved successfully",
92
+ error_log_message="Error saving knowledge service config",
93
+ extra_log_data={
94
+ "knowledge_service_id": (knowledge_service.knowledge_service_id),
95
+ "service_name": knowledge_service.name,
96
+ "service_api": knowledge_service.service_api.value,
97
+ },
98
+ )
99
+
100
+ async def get_many(
101
+ self, knowledge_service_ids: List[str]
102
+ ) -> Dict[str, Optional[KnowledgeServiceConfig]]:
103
+ """Retrieve multiple knowledge service configs by ID.
104
+
105
+ Args:
106
+ knowledge_service_ids: List of unique knowledge service
107
+ identifiers
108
+
109
+ Returns:
110
+ Dict mapping knowledge_service_id to KnowledgeServiceConfig (or
111
+ None if not found)
112
+ """
113
+ # Convert knowledge service IDs to object names
114
+ object_names = [f"config/{service_id}" for service_id in knowledge_service_ids]
115
+
116
+ # Get objects from Minio using batch method
117
+ object_results = self.get_many_json_objects(
118
+ bucket_name=self.bucket_name,
119
+ object_names=object_names,
120
+ model_class=KnowledgeServiceConfig,
121
+ not_found_log_message="Knowledge service config not found",
122
+ error_log_message="Error retrieving knowledge service config",
123
+ extra_log_data={"knowledge_service_ids": knowledge_service_ids},
124
+ )
125
+
126
+ # Convert object names back to knowledge service IDs for the result
127
+ result: Dict[str, Optional[KnowledgeServiceConfig]] = {}
128
+ for i, service_id in enumerate(knowledge_service_ids):
129
+ object_name = object_names[i]
130
+ result[service_id] = object_results[object_name]
131
+
132
+ return result
133
+
134
+ async def generate_id(self) -> str:
135
+ """Generate a unique knowledge service identifier.
136
+
137
+ Returns:
138
+ Unique knowledge service ID string
139
+ """
140
+ return self.generate_id_with_prefix("ks")
141
+
142
+ async def list_all(self) -> List[KnowledgeServiceConfig]:
143
+ """List all knowledge service configurations.
144
+
145
+ Returns:
146
+ List of all knowledge service configurations, sorted by
147
+ knowledge_service_id
148
+ """
149
+ try:
150
+ # Extract knowledge service IDs from objects with config/ prefix
151
+ service_ids = self.list_objects_with_prefix_extract_ids(
152
+ bucket_name=self.bucket_name,
153
+ prefix="config/",
154
+ entity_type_name="configs",
155
+ )
156
+
157
+ if not service_ids:
158
+ return []
159
+
160
+ # Get all configurations using the existing get_many method
161
+ config_results = await self.get_many(service_ids)
162
+
163
+ # Filter out None results and sort by knowledge_service_id
164
+ configs = [
165
+ config for config in config_results.values() if config is not None
166
+ ]
167
+ configs.sort(key=lambda x: x.knowledge_service_id)
168
+
169
+ self.logger.debug(
170
+ "Retrieved configs",
171
+ extra={"count": len(configs)},
172
+ )
173
+
174
+ return configs
175
+
176
+ except Exception as e:
177
+ self.logger.error(
178
+ "Error listing configs",
179
+ exc_info=True,
180
+ extra={
181
+ "error_type": type(e).__name__,
182
+ "error_message": str(e),
183
+ "bucket": self.bucket_name,
184
+ },
185
+ )
186
+ # Return empty list on error to avoid breaking the API
187
+ return []
@@ -0,0 +1,211 @@
1
+ """
2
+ Minio implementation of KnowledgeServiceQueryRepository.
3
+
4
+ This module provides a Minio-based implementation of the
5
+ KnowledgeServiceQueryRepository protocol that follows the Clean Architecture
6
+ patterns defined in the Fun-Police Framework. It handles knowledge service
7
+ query storage as JSON objects in Minio, ensuring idempotency and proper
8
+ error handling.
9
+
10
+ The implementation stores knowledge service queries as JSON objects in Minio,
11
+ following the large payload handling pattern from the architectural
12
+ guidelines.
13
+ Each query is stored as a separate object with the query ID as the key.
14
+ """
15
+
16
+ import logging
17
+ import uuid
18
+
19
+ from typing import Optional, List, Dict
20
+
21
+
22
+ from julee.domain.models.assembly_specification import (
23
+ KnowledgeServiceQuery,
24
+ )
25
+ from .client import MinioClient, MinioRepositoryMixin
26
+ from julee.domain.repositories.knowledge_service_query import (
27
+ KnowledgeServiceQueryRepository,
28
+ )
29
+
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ class MinioKnowledgeServiceQueryRepository(
35
+ KnowledgeServiceQueryRepository, MinioRepositoryMixin
36
+ ):
37
+ """
38
+ Minio implementation of KnowledgeServiceQueryRepository.
39
+
40
+ This implementation stores knowledge service queries as JSON objects in
41
+ Minio buckets, following the established patterns for Minio repositories
42
+ in this system. Each query is stored as a separate object with
43
+ deterministic naming.
44
+ """
45
+
46
+ def __init__(self, client: MinioClient) -> None:
47
+ """Initialize repository with Minio client.
48
+
49
+ Args:
50
+ client: MinioClient protocol implementation (real or fake)
51
+ """
52
+ self.client = client
53
+ self.logger = logging.getLogger("MinioKnowledgeServiceQueryRepository")
54
+ self.bucket_name = "knowledge-service-queries"
55
+ self.ensure_buckets_exist(self.bucket_name)
56
+
57
+ async def get(self, query_id: str) -> Optional[KnowledgeServiceQuery]:
58
+ """Retrieve a knowledge service query by ID.
59
+
60
+ Args:
61
+ query_id: Unique query identifier
62
+
63
+ Returns:
64
+ KnowledgeServiceQuery object if found, None otherwise
65
+ """
66
+ logger.debug(
67
+ "MinioKnowledgeServiceQueryRepository: Attempting to retrieve " "query",
68
+ extra={"query_id": query_id, "bucket": self.bucket_name},
69
+ )
70
+
71
+ object_name = f"query/{query_id}"
72
+
73
+ # Get object from Minio
74
+ query_data = self.get_json_object(
75
+ bucket_name=self.bucket_name,
76
+ object_name=object_name,
77
+ model_class=KnowledgeServiceQuery,
78
+ not_found_log_message="Knowledge service query not found",
79
+ error_log_message="Error retrieving knowledge service query",
80
+ extra_log_data={"query_id": query_id},
81
+ )
82
+
83
+ return query_data
84
+
85
+ async def save(self, query: KnowledgeServiceQuery) -> None:
86
+ """Store or update a knowledge service query.
87
+
88
+ Args:
89
+ query: KnowledgeServiceQuery object to store
90
+ """
91
+ logger.debug(
92
+ "MinioKnowledgeServiceQueryRepository: Saving query",
93
+ extra={"query_id": query.query_id, "bucket": self.bucket_name},
94
+ )
95
+
96
+ # Update the updated_at timestamp
97
+ self.update_timestamps(query)
98
+
99
+ object_name = f"query/{query.query_id}"
100
+
101
+ # Store in Minio
102
+ self.put_json_object(
103
+ bucket_name=self.bucket_name,
104
+ object_name=object_name,
105
+ model=query,
106
+ success_log_message="Knowledge service query saved successfully",
107
+ error_log_message="Failed to save knowledge service query",
108
+ extra_log_data={"query_id": query.query_id},
109
+ )
110
+
111
+ async def generate_id(self) -> str:
112
+ """Generate a unique query identifier.
113
+
114
+ Returns:
115
+ Unique string identifier for a new query
116
+ """
117
+ query_id = f"query-{uuid.uuid4().hex[:12]}"
118
+
119
+ logger.debug(
120
+ "MinioKnowledgeServiceQueryRepository: Generated query ID",
121
+ extra={"query_id": query_id},
122
+ )
123
+
124
+ return query_id
125
+
126
+ async def get_many(
127
+ self, query_ids: List[str]
128
+ ) -> Dict[str, Optional[KnowledgeServiceQuery]]:
129
+ """Retrieve multiple knowledge service queries by ID.
130
+
131
+ Args:
132
+ query_ids: List of unique query identifiers
133
+
134
+ Returns:
135
+ Dict mapping query_id to KnowledgeServiceQuery (or None if not
136
+ found)
137
+ """
138
+ logger.debug(
139
+ "MinioKnowledgeServiceQueryRepository: Attempting to retrieve "
140
+ "multiple queries",
141
+ extra={
142
+ "query_ids": query_ids,
143
+ "count": len(query_ids),
144
+ "bucket": self.bucket_name,
145
+ },
146
+ )
147
+
148
+ # Convert query IDs to object names
149
+ object_names = [f"query/{query_id}" for query_id in query_ids]
150
+
151
+ # Get objects from Minio using batch method
152
+ object_results = self.get_many_json_objects(
153
+ bucket_name=self.bucket_name,
154
+ object_names=object_names,
155
+ model_class=KnowledgeServiceQuery,
156
+ not_found_log_message="Knowledge service query not found",
157
+ error_log_message="Error retrieving knowledge service query",
158
+ extra_log_data={"query_ids": query_ids},
159
+ )
160
+
161
+ # Convert object names back to query IDs for the result
162
+ result: Dict[str, Optional[KnowledgeServiceQuery]] = {}
163
+ for i, query_id in enumerate(query_ids):
164
+ object_name = object_names[i]
165
+ result[query_id] = object_results[object_name]
166
+
167
+ return result
168
+
169
+ async def list_all(self) -> List[KnowledgeServiceQuery]:
170
+ """List all knowledge service queries.
171
+
172
+ Returns:
173
+ List of all knowledge service queries, sorted by query_id
174
+ """
175
+ try:
176
+ # Extract query IDs from objects with the query/ prefix
177
+ query_ids = self.list_objects_with_prefix_extract_ids(
178
+ bucket_name=self.bucket_name,
179
+ prefix="query/",
180
+ entity_type_name="queries",
181
+ )
182
+
183
+ if not query_ids:
184
+ return []
185
+
186
+ # Get all queries using the existing get_many method
187
+ query_results = await self.get_many(query_ids)
188
+
189
+ # Filter out None results and sort by query_id
190
+ queries = [query for query in query_results.values() if query is not None]
191
+ queries.sort(key=lambda x: x.query_id)
192
+
193
+ logger.debug(
194
+ "Retrieved queries",
195
+ extra={"count": len(queries)},
196
+ )
197
+
198
+ return queries
199
+
200
+ except Exception as e:
201
+ logger.error(
202
+ "Error listing queries",
203
+ exc_info=True,
204
+ extra={
205
+ "error_type": type(e).__name__,
206
+ "error_message": str(e),
207
+ "bucket": self.bucket_name,
208
+ },
209
+ )
210
+ # Return empty list on error to avoid breaking the API
211
+ return []
@@ -0,0 +1,106 @@
1
+ """
2
+ Minio implementation of PolicyRepository.
3
+
4
+ This module provides a Minio-based implementation of the PolicyRepository
5
+ protocol that follows the Clean Architecture patterns defined in the
6
+ Fun-Police Framework. It handles policy storage as JSON objects in Minio,
7
+ ensuring idempotency and proper error handling.
8
+
9
+ The implementation stores policies as JSON objects in Minio, following the
10
+ large payload handling pattern from the architectural guidelines. Each
11
+ policy is stored as a complete JSON document with its validation scores
12
+ and transformation queries.
13
+ """
14
+
15
+ import logging
16
+ from typing import Optional, List, Dict
17
+
18
+ from julee.domain.models.policy import Policy
19
+ from julee.domain.repositories.policy import PolicyRepository
20
+ from .client import MinioClient, MinioRepositoryMixin
21
+
22
+
23
+ class MinioPolicyRepository(PolicyRepository, MinioRepositoryMixin):
24
+ """
25
+ Minio implementation of PolicyRepository using Minio for persistence.
26
+
27
+ This implementation stores policies as JSON objects in the "policies"
28
+ bucket. Each policy includes its complete validation scores and optional
29
+ transformation queries.
30
+ """
31
+
32
+ def __init__(self, client: MinioClient) -> None:
33
+ """Initialize repository with Minio client.
34
+
35
+ Args:
36
+ client: MinioClient protocol implementation (real or fake)
37
+ """
38
+ self.client = client
39
+ self.logger = logging.getLogger("MinioPolicyRepository")
40
+ self.policies_bucket = "policies"
41
+ self.ensure_buckets_exist(self.policies_bucket)
42
+
43
+ async def get(self, policy_id: str) -> Optional[Policy]:
44
+ """Retrieve a policy by ID."""
45
+ return self.get_json_object(
46
+ bucket_name=self.policies_bucket,
47
+ object_name=policy_id,
48
+ model_class=Policy,
49
+ not_found_log_message="Policy not found",
50
+ error_log_message="Error retrieving policy",
51
+ extra_log_data={"policy_id": policy_id},
52
+ )
53
+
54
+ async def save(self, policy: Policy) -> None:
55
+ """Save a policy to Minio."""
56
+ # Update timestamps
57
+ self.update_timestamps(policy)
58
+
59
+ self.put_json_object(
60
+ bucket_name=self.policies_bucket,
61
+ object_name=policy.policy_id,
62
+ model=policy,
63
+ success_log_message="Policy saved successfully",
64
+ error_log_message="Error saving policy",
65
+ extra_log_data={
66
+ "policy_id": policy.policy_id,
67
+ "title": policy.title,
68
+ "status": policy.status.value,
69
+ "validation_scores_count": len(policy.validation_scores),
70
+ "has_transformations": policy.has_transformations,
71
+ "version": policy.version,
72
+ },
73
+ )
74
+
75
+ async def get_many(self, policy_ids: List[str]) -> Dict[str, Optional[Policy]]:
76
+ """Retrieve multiple policies by ID.
77
+
78
+ Args:
79
+ policy_ids: List of unique policy identifiers
80
+
81
+ Returns:
82
+ Dict mapping policy_id to Policy (or None if not found)
83
+ """
84
+ # Convert policy IDs to object names (direct mapping in this case)
85
+ object_names = policy_ids
86
+
87
+ # Get objects from Minio using batch method
88
+ object_results = self.get_many_json_objects(
89
+ bucket_name=self.policies_bucket,
90
+ object_names=object_names,
91
+ model_class=Policy,
92
+ not_found_log_message="Policy not found",
93
+ error_log_message="Error retrieving policy",
94
+ extra_log_data={"policy_ids": policy_ids},
95
+ )
96
+
97
+ # Convert object names back to policy IDs for the result
98
+ result: Dict[str, Optional[Policy]] = {}
99
+ for policy_id in policy_ids:
100
+ result[policy_id] = object_results[policy_id]
101
+
102
+ return result
103
+
104
+ async def generate_id(self) -> str:
105
+ """Generate a unique policy identifier."""
106
+ return self.generate_id_with_prefix("policy")
File without changes