maleo-metadata-client 0.1.75__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 maleo-metadata-client might be problematic. Click here for more details.

@@ -0,0 +1,315 @@
1
+ import json
2
+ from copy import deepcopy
3
+ from datetime import datetime, timezone
4
+ from typing import Literal, overload
5
+ from uuid import UUID
6
+ from nexo.client.service import ClientService
7
+ from nexo.database.enums import Connection
8
+ from nexo.database.utils import build_cache_key
9
+ from nexo.enums.cardinality import Cardinality
10
+ from nexo.enums.connection import Header
11
+ from nexo.logging.enums import LogLevel
12
+ from maleo.metadata.constants.organization_type import ORGANIZATION_TYPE_RESOURCE
13
+ from maleo.metadata.enums.organization_type import Granularity
14
+ from maleo.metadata.mixins.organization_type import is_id_identifier
15
+ from maleo.metadata.schemas.organization_type import (
16
+ ReadMultipleParameter,
17
+ ReadSingleParameter,
18
+ StandardOrganizationTypeSchema,
19
+ FullOrganizationTypeSchema,
20
+ )
21
+ from maleo.metadata.utils.organization_type import get_schema_model
22
+ from nexo.schemas.connection import ConnectionContext
23
+ from nexo.schemas.exception.factory import MaleoExceptionFactory
24
+ from nexo.schemas.operation.action.resource import ReadResourceOperationAction
25
+ from nexo.schemas.operation.enums import OperationType, Target
26
+ from nexo.schemas.operation.mixins import Timestamp
27
+ from nexo.schemas.operation.resource import (
28
+ ReadMultipleResourceOperation,
29
+ ReadSingleResourceOperation,
30
+ )
31
+ from nexo.schemas.pagination import StrictPagination
32
+ from nexo.schemas.resource import AggregateField
33
+ from nexo.schemas.response import (
34
+ MultipleDataResponse,
35
+ ReadMultipleDataResponse,
36
+ SingleDataResponse,
37
+ ReadSingleDataResponse,
38
+ )
39
+ from nexo.schemas.security.authorization import (
40
+ OptAnyAuthorization,
41
+ AnyAuthorization,
42
+ AuthorizationFactory,
43
+ )
44
+ from nexo.schemas.security.impersonation import OptImpersonation
45
+ from nexo.types.dict import OptStrToStrDict
46
+ from nexo.utils.merger import merge_dicts
47
+ from ..config import ClientConfig
48
+
49
+
50
+ class OrganizationTypeClientService(ClientService[ClientConfig]):
51
+ resource = ORGANIZATION_TYPE_RESOURCE
52
+
53
+ @overload
54
+ async def read(
55
+ self,
56
+ cardinality: Literal[Cardinality.MULTIPLE],
57
+ granularity: Literal[Granularity.STANDARD],
58
+ *,
59
+ operation_id: UUID,
60
+ connection_context: ConnectionContext,
61
+ authorization: AnyAuthorization,
62
+ impersonation: OptImpersonation = None,
63
+ parameters: ReadMultipleParameter,
64
+ headers: OptStrToStrDict = None,
65
+ ) -> ReadMultipleDataResponse[
66
+ StandardOrganizationTypeSchema, StrictPagination, None
67
+ ]: ...
68
+ @overload
69
+ async def read(
70
+ self,
71
+ cardinality: Literal[Cardinality.MULTIPLE],
72
+ granularity: Literal[Granularity.FULL],
73
+ *,
74
+ operation_id: UUID,
75
+ connection_context: ConnectionContext,
76
+ authorization: AnyAuthorization,
77
+ impersonation: OptImpersonation = None,
78
+ parameters: ReadMultipleParameter,
79
+ headers: OptStrToStrDict = None,
80
+ ) -> ReadMultipleDataResponse[
81
+ FullOrganizationTypeSchema, StrictPagination, None
82
+ ]: ...
83
+ @overload
84
+ async def read(
85
+ self,
86
+ cardinality: Literal[Cardinality.SINGLE],
87
+ granularity: Literal[Granularity.STANDARD],
88
+ *,
89
+ operation_id: UUID,
90
+ connection_context: ConnectionContext,
91
+ authorization: AnyAuthorization,
92
+ impersonation: OptImpersonation = None,
93
+ parameters: ReadSingleParameter,
94
+ headers: OptStrToStrDict = None,
95
+ ) -> ReadSingleDataResponse[StandardOrganizationTypeSchema, None]: ...
96
+ @overload
97
+ async def read(
98
+ self,
99
+ cardinality: Literal[Cardinality.SINGLE],
100
+ granularity: Literal[Granularity.FULL],
101
+ *,
102
+ operation_id: UUID,
103
+ connection_context: ConnectionContext,
104
+ authorization: AnyAuthorization,
105
+ impersonation: OptImpersonation = None,
106
+ parameters: ReadSingleParameter,
107
+ headers: OptStrToStrDict = None,
108
+ ) -> ReadSingleDataResponse[FullOrganizationTypeSchema, None]: ...
109
+ async def read(
110
+ self,
111
+ cardinality: Cardinality,
112
+ granularity: Granularity,
113
+ *,
114
+ operation_id: UUID,
115
+ connection_context: ConnectionContext,
116
+ authorization: OptAnyAuthorization = None,
117
+ impersonation: OptImpersonation = None,
118
+ parameters: ReadMultipleParameter | ReadSingleParameter,
119
+ headers: OptStrToStrDict = None,
120
+ ) -> (
121
+ ReadMultipleDataResponse[StandardOrganizationTypeSchema, StrictPagination, None]
122
+ | ReadMultipleDataResponse[FullOrganizationTypeSchema, StrictPagination, None]
123
+ | ReadSingleDataResponse[StandardOrganizationTypeSchema, None]
124
+ | ReadSingleDataResponse[FullOrganizationTypeSchema, None]
125
+ ):
126
+ redis_client = self._redis.manager.client.get(Connection.ASYNC)
127
+ data_model_cls = get_schema_model(granularity)
128
+
129
+ executed_at = datetime.now(tz=timezone.utc)
130
+
131
+ # Define arguments being used in this function
132
+ positional_arguments = [cardinality, granularity]
133
+ keyword_arguments = {
134
+ "authorization": (
135
+ authorization.model_dump(mode="json")
136
+ if authorization is not None
137
+ else None
138
+ ),
139
+ "parameters": parameters.model_dump(mode="json"),
140
+ }
141
+
142
+ # Define full function string
143
+ ext = f"({json.dumps(positional_arguments)}|{json.dumps(keyword_arguments)})"
144
+
145
+ # Define full cache_key
146
+ cache_key = build_cache_key(ext, namespace=self._namespace)
147
+
148
+ if parameters.use_cache:
149
+ # Initialize cache operation context
150
+ operation_context = deepcopy(self._operation_context)
151
+ operation_context.target.type = Target.CACHE
152
+
153
+ redis_response_str = await redis_client.get(cache_key)
154
+
155
+ if redis_response_str is not None:
156
+ operation_timestamp = Timestamp.completed_now(executed_at)
157
+ if cardinality is Cardinality.MULTIPLE:
158
+ response = ReadMultipleDataResponse[
159
+ data_model_cls, StrictPagination, None
160
+ ].model_validate_json(redis_response_str)
161
+ operation = ReadMultipleResourceOperation[
162
+ data_model_cls, StrictPagination, None
163
+ ](
164
+ application_context=self._application_context,
165
+ id=operation_id,
166
+ context=operation_context,
167
+ resource=self.resource,
168
+ timestamp=operation_timestamp,
169
+ summary=f"Successfully read multiple {granularity} {self.resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
170
+ connection_context=connection_context,
171
+ authentication=None,
172
+ authorization=authorization,
173
+ impersonation=impersonation,
174
+ response=response,
175
+ )
176
+ operation.log(self._logger, LogLevel.INFO)
177
+ operation.publish(self._logger, self._publishers)
178
+ elif cardinality is Cardinality.SINGLE:
179
+ response = ReadSingleDataResponse[
180
+ data_model_cls, None
181
+ ].model_validate_json(redis_response_str)
182
+ operation = ReadSingleResourceOperation[data_model_cls, None](
183
+ application_context=self._application_context,
184
+ id=operation_id,
185
+ context=operation_context,
186
+ resource=self.resource,
187
+ timestamp=operation_timestamp,
188
+ summary=f"Successfully read single {granularity} {self.resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
189
+ connection_context=connection_context,
190
+ authentication=None,
191
+ authorization=authorization,
192
+ impersonation=impersonation,
193
+ response=response,
194
+ )
195
+ operation.log(self._logger, LogLevel.INFO)
196
+ operation.publish(self._logger, self._publishers)
197
+
198
+ return response # type: ignore
199
+
200
+ operation_context = deepcopy(self._operation_context)
201
+ operation_context.target.type = Target.MICROSERVICE
202
+
203
+ async with self._http_client_manager.get() as http_client:
204
+ base_headers = {
205
+ Header.CONTENT_TYPE.value: "application/json",
206
+ Header.X_OPERATION_ID.value: str(operation_id),
207
+ }
208
+ if impersonation is not None:
209
+ base_headers[Header.X_USER_ID.value] = str(impersonation.user_id)
210
+ if impersonation.organization_id is not None:
211
+ base_headers[Header.X_ORGANIZATION_ID.value] = str(
212
+ impersonation.organization_id
213
+ )
214
+
215
+ if headers is not None:
216
+ headers = merge_dicts(base_headers, headers)
217
+ else:
218
+ headers = base_headers
219
+
220
+ if authorization is not None:
221
+ auth = AuthorizationFactory.httpx_auth(
222
+ scheme=authorization.scheme, authorization=authorization.credentials
223
+ )
224
+ else:
225
+ auth = None
226
+
227
+ base_url = f"{self._config.url}/v1/{self.resource.identifiers[-1].slug}/"
228
+ if isinstance(parameters, ReadMultipleParameter):
229
+ url = base_url
230
+ elif isinstance(parameters, ReadSingleParameter):
231
+ if is_id_identifier(parameters.identifier):
232
+ url = base_url + str(parameters.identifier.value)
233
+ else:
234
+ url = (
235
+ base_url
236
+ + f"{parameters.identifier.type}/{parameters.identifier.value}"
237
+ )
238
+
239
+ params = parameters.to_query_params()
240
+
241
+ response = await http_client.get(
242
+ url, params=params, headers=headers, auth=auth
243
+ )
244
+
245
+ operation_timestamp = Timestamp.completed_now(executed_at)
246
+
247
+ if response.is_error:
248
+ exc = MaleoExceptionFactory.from_httpx(
249
+ response,
250
+ operation_type=OperationType.REQUEST,
251
+ application_context=self._application_context,
252
+ operation_id=operation_id,
253
+ operation_context=operation_context,
254
+ operation_action=ReadResourceOperationAction(),
255
+ operation_timestamp=operation_timestamp,
256
+ connection_context=connection_context,
257
+ authentication=None,
258
+ authorization=authorization,
259
+ impersonation=impersonation,
260
+ logger=self._logger,
261
+ )
262
+ exc.log_and_publish_operation(self._logger, self._publishers)
263
+ raise exc
264
+
265
+ if isinstance(parameters, ReadMultipleParameter):
266
+ validated_response = MultipleDataResponse[
267
+ data_model_cls, StrictPagination, None
268
+ ].model_validate(response.json())
269
+ service_response = ReadMultipleDataResponse[
270
+ data_model_cls, StrictPagination, None
271
+ ].new(
272
+ data=validated_response.data,
273
+ pagination=validated_response.pagination,
274
+ )
275
+ operation = ReadMultipleResourceOperation[
276
+ data_model_cls, StrictPagination, None
277
+ ](
278
+ application_context=self._application_context,
279
+ id=operation_id,
280
+ context=operation_context,
281
+ resource=self.resource,
282
+ timestamp=operation_timestamp,
283
+ summary=f"Successfully read multiple {granularity} {self.resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
284
+ connection_context=connection_context,
285
+ authentication=None,
286
+ authorization=authorization,
287
+ impersonation=impersonation,
288
+ response=service_response,
289
+ )
290
+ operation.log(self._logger, LogLevel.INFO)
291
+ operation.publish(self._logger, self._publishers)
292
+ elif isinstance(parameters, ReadSingleParameter):
293
+ validated_response = SingleDataResponse[
294
+ data_model_cls, None
295
+ ].model_validate(response.json())
296
+ service_response = ReadSingleDataResponse[data_model_cls, None].new(
297
+ data=validated_response.data,
298
+ )
299
+ operation = ReadSingleResourceOperation[data_model_cls, None](
300
+ application_context=self._application_context,
301
+ id=operation_id,
302
+ context=operation_context,
303
+ resource=self.resource,
304
+ timestamp=operation_timestamp,
305
+ summary=f"Successfully read single {granularity} {self.resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
306
+ connection_context=connection_context,
307
+ authentication=None,
308
+ authorization=authorization,
309
+ impersonation=impersonation,
310
+ response=service_response,
311
+ )
312
+ operation.log(self._logger, LogLevel.INFO)
313
+ operation.publish(self._logger, self._publishers)
314
+
315
+ return service_response # type: ignore
@@ -0,0 +1,311 @@
1
+ import json
2
+ from copy import deepcopy
3
+ from datetime import datetime, timezone
4
+ from typing import Literal, overload
5
+ from uuid import UUID
6
+ from nexo.client.service import ClientService
7
+ from nexo.database.enums import Connection
8
+ from nexo.database.utils import build_cache_key
9
+ from nexo.enums.cardinality import Cardinality
10
+ from nexo.enums.connection import Header
11
+ from nexo.logging.enums import LogLevel
12
+ from maleo.metadata.constants.service import SERVICE_RESOURCE
13
+ from maleo.metadata.enums.service import Granularity
14
+ from maleo.metadata.mixins.service import is_id_identifier
15
+ from maleo.metadata.schemas.service import (
16
+ ReadMultipleParameter,
17
+ ReadSingleParameter,
18
+ StandardServiceSchema,
19
+ FullServiceSchema,
20
+ )
21
+ from maleo.metadata.utils.service import get_schema_model
22
+ from nexo.schemas.connection import ConnectionContext
23
+ from nexo.schemas.exception.factory import MaleoExceptionFactory
24
+ from nexo.schemas.operation.action.resource import ReadResourceOperationAction
25
+ from nexo.schemas.operation.enums import OperationType, Target
26
+ from nexo.schemas.operation.mixins import Timestamp
27
+ from nexo.schemas.operation.resource import (
28
+ ReadMultipleResourceOperation,
29
+ ReadSingleResourceOperation,
30
+ )
31
+ from nexo.schemas.pagination import StrictPagination
32
+ from nexo.schemas.resource import AggregateField
33
+ from nexo.schemas.response import (
34
+ MultipleDataResponse,
35
+ ReadMultipleDataResponse,
36
+ SingleDataResponse,
37
+ ReadSingleDataResponse,
38
+ )
39
+ from nexo.schemas.security.authorization import (
40
+ OptAnyAuthorization,
41
+ AnyAuthorization,
42
+ AuthorizationFactory,
43
+ )
44
+ from nexo.schemas.security.impersonation import OptImpersonation
45
+ from nexo.types.dict import OptStrToStrDict
46
+ from nexo.utils.merger import merge_dicts
47
+ from ..config import ClientConfig
48
+
49
+
50
+ class ServiceClientService(ClientService[ClientConfig]):
51
+ resource = SERVICE_RESOURCE
52
+
53
+ @overload
54
+ async def read(
55
+ self,
56
+ cardinality: Literal[Cardinality.MULTIPLE],
57
+ granularity: Literal[Granularity.STANDARD],
58
+ *,
59
+ operation_id: UUID,
60
+ connection_context: ConnectionContext,
61
+ authorization: AnyAuthorization,
62
+ impersonation: OptImpersonation = None,
63
+ parameters: ReadMultipleParameter,
64
+ headers: OptStrToStrDict = None,
65
+ ) -> ReadMultipleDataResponse[StandardServiceSchema, StrictPagination, None]: ...
66
+ @overload
67
+ async def read(
68
+ self,
69
+ cardinality: Literal[Cardinality.MULTIPLE],
70
+ granularity: Literal[Granularity.FULL],
71
+ *,
72
+ operation_id: UUID,
73
+ connection_context: ConnectionContext,
74
+ authorization: AnyAuthorization,
75
+ impersonation: OptImpersonation = None,
76
+ parameters: ReadMultipleParameter,
77
+ headers: OptStrToStrDict = None,
78
+ ) -> ReadMultipleDataResponse[FullServiceSchema, StrictPagination, None]: ...
79
+ @overload
80
+ async def read(
81
+ self,
82
+ cardinality: Literal[Cardinality.SINGLE],
83
+ granularity: Literal[Granularity.STANDARD],
84
+ *,
85
+ operation_id: UUID,
86
+ connection_context: ConnectionContext,
87
+ authorization: AnyAuthorization,
88
+ impersonation: OptImpersonation = None,
89
+ parameters: ReadSingleParameter,
90
+ headers: OptStrToStrDict = None,
91
+ ) -> ReadSingleDataResponse[StandardServiceSchema, None]: ...
92
+ @overload
93
+ async def read(
94
+ self,
95
+ cardinality: Literal[Cardinality.SINGLE],
96
+ granularity: Literal[Granularity.FULL],
97
+ *,
98
+ operation_id: UUID,
99
+ connection_context: ConnectionContext,
100
+ authorization: AnyAuthorization,
101
+ impersonation: OptImpersonation = None,
102
+ parameters: ReadSingleParameter,
103
+ headers: OptStrToStrDict = None,
104
+ ) -> ReadSingleDataResponse[FullServiceSchema, None]: ...
105
+ async def read(
106
+ self,
107
+ cardinality: Cardinality,
108
+ granularity: Granularity,
109
+ *,
110
+ operation_id: UUID,
111
+ connection_context: ConnectionContext,
112
+ authorization: OptAnyAuthorization = None,
113
+ impersonation: OptImpersonation = None,
114
+ parameters: ReadMultipleParameter | ReadSingleParameter,
115
+ headers: OptStrToStrDict = None,
116
+ ) -> (
117
+ ReadMultipleDataResponse[StandardServiceSchema, StrictPagination, None]
118
+ | ReadMultipleDataResponse[FullServiceSchema, StrictPagination, None]
119
+ | ReadSingleDataResponse[StandardServiceSchema, None]
120
+ | ReadSingleDataResponse[FullServiceSchema, None]
121
+ ):
122
+ redis_client = self._redis.manager.client.get(Connection.ASYNC)
123
+ data_model_cls = get_schema_model(granularity)
124
+
125
+ executed_at = datetime.now(tz=timezone.utc)
126
+
127
+ # Define arguments being used in this function
128
+ positional_arguments = [cardinality, granularity]
129
+ keyword_arguments = {
130
+ "authorization": (
131
+ authorization.model_dump(mode="json")
132
+ if authorization is not None
133
+ else None
134
+ ),
135
+ "parameters": parameters.model_dump(mode="json"),
136
+ }
137
+
138
+ # Define full function string
139
+ ext = f"({json.dumps(positional_arguments)}|{json.dumps(keyword_arguments)})"
140
+
141
+ # Define full cache_key
142
+ cache_key = build_cache_key(ext, namespace=self._namespace)
143
+
144
+ if parameters.use_cache:
145
+ # Initialize cache operation context
146
+ operation_context = deepcopy(self._operation_context)
147
+ operation_context.target.type = Target.CACHE
148
+
149
+ redis_response_str = await redis_client.get(cache_key)
150
+
151
+ if redis_response_str is not None:
152
+ operation_timestamp = Timestamp.completed_now(executed_at)
153
+ if cardinality is Cardinality.MULTIPLE:
154
+ response = ReadMultipleDataResponse[
155
+ data_model_cls, StrictPagination, None
156
+ ].model_validate_json(redis_response_str)
157
+ operation = ReadMultipleResourceOperation[
158
+ data_model_cls, StrictPagination, None
159
+ ](
160
+ application_context=self._application_context,
161
+ id=operation_id,
162
+ context=operation_context,
163
+ resource=self.resource,
164
+ timestamp=operation_timestamp,
165
+ summary=f"Successfully read multiple {granularity} {self.resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
166
+ connection_context=connection_context,
167
+ authentication=None,
168
+ authorization=authorization,
169
+ impersonation=impersonation,
170
+ response=response,
171
+ )
172
+ operation.log(self._logger, LogLevel.INFO)
173
+ operation.publish(self._logger, self._publishers)
174
+ elif cardinality is Cardinality.SINGLE:
175
+ response = ReadSingleDataResponse[
176
+ data_model_cls, None
177
+ ].model_validate_json(redis_response_str)
178
+ operation = ReadSingleResourceOperation[data_model_cls, None](
179
+ application_context=self._application_context,
180
+ id=operation_id,
181
+ context=operation_context,
182
+ resource=self.resource,
183
+ timestamp=operation_timestamp,
184
+ summary=f"Successfully read single {granularity} {self.resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
185
+ connection_context=connection_context,
186
+ authentication=None,
187
+ authorization=authorization,
188
+ impersonation=impersonation,
189
+ response=response,
190
+ )
191
+ operation.log(self._logger, LogLevel.INFO)
192
+ operation.publish(self._logger, self._publishers)
193
+
194
+ return response # type: ignore
195
+
196
+ operation_context = deepcopy(self._operation_context)
197
+ operation_context.target.type = Target.MICROSERVICE
198
+
199
+ async with self._http_client_manager.get() as http_client:
200
+ base_headers = {
201
+ Header.CONTENT_TYPE.value: "application/json",
202
+ Header.X_OPERATION_ID.value: str(operation_id),
203
+ }
204
+ if impersonation is not None:
205
+ base_headers[Header.X_USER_ID.value] = str(impersonation.user_id)
206
+ if impersonation.organization_id is not None:
207
+ base_headers[Header.X_ORGANIZATION_ID.value] = str(
208
+ impersonation.organization_id
209
+ )
210
+
211
+ if headers is not None:
212
+ headers = merge_dicts(base_headers, headers)
213
+ else:
214
+ headers = base_headers
215
+
216
+ if authorization is not None:
217
+ auth = AuthorizationFactory.httpx_auth(
218
+ scheme=authorization.scheme, authorization=authorization.credentials
219
+ )
220
+ else:
221
+ auth = None
222
+
223
+ base_url = f"{self._config.url}/v1/{self.resource.identifiers[-1].slug}/"
224
+ if isinstance(parameters, ReadMultipleParameter):
225
+ url = base_url
226
+ elif isinstance(parameters, ReadSingleParameter):
227
+ if is_id_identifier(parameters.identifier):
228
+ url = base_url + str(parameters.identifier.value)
229
+ else:
230
+ url = (
231
+ base_url
232
+ + f"{parameters.identifier.type}/{parameters.identifier.value}"
233
+ )
234
+
235
+ params = parameters.to_query_params()
236
+
237
+ response = await http_client.get(
238
+ url, params=params, headers=headers, auth=auth
239
+ )
240
+
241
+ operation_timestamp = Timestamp.completed_now(executed_at)
242
+
243
+ if response.is_error:
244
+ exc = MaleoExceptionFactory.from_httpx(
245
+ response,
246
+ operation_type=OperationType.REQUEST,
247
+ application_context=self._application_context,
248
+ operation_id=operation_id,
249
+ operation_context=operation_context,
250
+ operation_action=ReadResourceOperationAction(),
251
+ operation_timestamp=operation_timestamp,
252
+ connection_context=connection_context,
253
+ authentication=None,
254
+ authorization=authorization,
255
+ impersonation=impersonation,
256
+ logger=self._logger,
257
+ )
258
+ exc.log_and_publish_operation(self._logger, self._publishers)
259
+ raise exc
260
+
261
+ if isinstance(parameters, ReadMultipleParameter):
262
+ validated_response = MultipleDataResponse[
263
+ data_model_cls, StrictPagination, None
264
+ ].model_validate(response.json())
265
+ service_response = ReadMultipleDataResponse[
266
+ data_model_cls, StrictPagination, None
267
+ ].new(
268
+ data=validated_response.data,
269
+ pagination=validated_response.pagination,
270
+ )
271
+ operation = ReadMultipleResourceOperation[
272
+ data_model_cls, StrictPagination, None
273
+ ](
274
+ application_context=self._application_context,
275
+ id=operation_id,
276
+ context=operation_context,
277
+ resource=self.resource,
278
+ timestamp=operation_timestamp,
279
+ summary=f"Successfully read multiple {granularity} {self.resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
280
+ connection_context=connection_context,
281
+ authentication=None,
282
+ authorization=authorization,
283
+ impersonation=impersonation,
284
+ response=service_response,
285
+ )
286
+ operation.log(self._logger, LogLevel.INFO)
287
+ operation.publish(self._logger, self._publishers)
288
+ elif isinstance(parameters, ReadSingleParameter):
289
+ validated_response = SingleDataResponse[
290
+ data_model_cls, None
291
+ ].model_validate(response.json())
292
+ service_response = ReadSingleDataResponse[data_model_cls, None].new(
293
+ data=validated_response.data,
294
+ )
295
+ operation = ReadSingleResourceOperation[data_model_cls, None](
296
+ application_context=self._application_context,
297
+ id=operation_id,
298
+ context=operation_context,
299
+ resource=self.resource,
300
+ timestamp=operation_timestamp,
301
+ summary=f"Successfully read single {granularity} {self.resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
302
+ connection_context=connection_context,
303
+ authentication=None,
304
+ authorization=authorization,
305
+ impersonation=impersonation,
306
+ response=service_response,
307
+ )
308
+ operation.log(self._logger, LogLevel.INFO)
309
+ operation.publish(self._logger, self._publishers)
310
+
311
+ return service_response # type: ignore