maleo-identity-client 0.6.2__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.
@@ -0,0 +1,381 @@
1
+ import json
2
+ from copy import deepcopy
3
+ from datetime import datetime, timezone
4
+ from typing import ClassVar, 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 nexo.schemas.connection import ConnectionContext
13
+ from nexo.schemas.exception.factory import MaleoExceptionFactory
14
+ from nexo.schemas.operation.action.resource import (
15
+ CreateResourceOperationAction,
16
+ ReadResourceOperationAction,
17
+ )
18
+ from nexo.schemas.operation.enums import OperationType, Target
19
+ from nexo.schemas.operation.mixins import Timestamp
20
+ from nexo.schemas.operation.resource import (
21
+ CreateSingleResourceOperation,
22
+ ReadMultipleResourceOperation,
23
+ ReadSingleResourceOperation,
24
+ )
25
+ from nexo.schemas.pagination import StrictPagination
26
+ from nexo.schemas.resource import Resource, AggregateField
27
+ from nexo.schemas.response import (
28
+ MultipleDataResponse,
29
+ ReadMultipleDataResponse,
30
+ SingleDataResponse,
31
+ CreateSingleDataResponse,
32
+ ReadSingleDataResponse,
33
+ )
34
+ from nexo.schemas.security.authorization import (
35
+ AnyAuthorization,
36
+ AuthorizationFactory,
37
+ )
38
+ from nexo.schemas.security.impersonation import OptImpersonation
39
+ from nexo.types.dict import OptStrToStrDict
40
+ from nexo.utils.merger import merge_dicts
41
+ from maleo.identity.constants.user import USER_RESOURCE
42
+ from maleo.identity.mixins.user import is_id_identifier
43
+ from maleo.identity.schemas.common import UserSchema, VerifyPasswordSchema
44
+ from maleo.identity.schemas.user import (
45
+ EncryptedVerifyPasswordData,
46
+ VerifyPasswordDataT,
47
+ VerifyPasswordParameter,
48
+ ReadMultipleParameter,
49
+ ReadSingleParameter,
50
+ )
51
+ from ..config import ClientConfig
52
+
53
+
54
+ class UserClientService(ClientService[ClientConfig]):
55
+ _resource: ClassVar[Resource] = USER_RESOURCE
56
+
57
+ @overload
58
+ async def read(
59
+ self,
60
+ cardinality: Literal[Cardinality.MULTIPLE],
61
+ *,
62
+ operation_id: UUID,
63
+ connection_context: ConnectionContext,
64
+ authorization: AnyAuthorization,
65
+ impersonation: OptImpersonation = None,
66
+ parameters: ReadMultipleParameter,
67
+ headers: OptStrToStrDict = None,
68
+ ) -> ReadMultipleDataResponse[UserSchema, StrictPagination, None]: ...
69
+ @overload
70
+ async def read(
71
+ self,
72
+ cardinality: Literal[Cardinality.SINGLE],
73
+ *,
74
+ operation_id: UUID,
75
+ connection_context: ConnectionContext,
76
+ authorization: AnyAuthorization,
77
+ impersonation: OptImpersonation = None,
78
+ parameters: ReadSingleParameter,
79
+ headers: OptStrToStrDict = None,
80
+ ) -> ReadSingleDataResponse[UserSchema, None]: ...
81
+ async def read(
82
+ self,
83
+ cardinality: Cardinality,
84
+ *,
85
+ operation_id: UUID,
86
+ connection_context: ConnectionContext,
87
+ authorization: AnyAuthorization,
88
+ impersonation: OptImpersonation = None,
89
+ parameters: ReadMultipleParameter | ReadSingleParameter,
90
+ headers: OptStrToStrDict = None,
91
+ ) -> (
92
+ ReadMultipleDataResponse[UserSchema, StrictPagination, None]
93
+ | ReadSingleDataResponse[UserSchema, None]
94
+ ):
95
+ redis_client = self._redis.manager.client.get(Connection.ASYNC)
96
+
97
+ executed_at = datetime.now(tz=timezone.utc)
98
+
99
+ # Define arguments being used in this function
100
+ positional_arguments = [cardinality]
101
+ keyword_arguments = {
102
+ "authorization": (
103
+ authorization.model_dump(mode="json")
104
+ if authorization is not None
105
+ else None
106
+ ),
107
+ "parameters": parameters.model_dump(mode="json"),
108
+ }
109
+
110
+ # Define full function string
111
+ ext = f"({json.dumps(positional_arguments)}|{json.dumps(keyword_arguments)})"
112
+
113
+ # Define full cache_key
114
+ cache_key = build_cache_key(ext, namespace=self._namespace)
115
+
116
+ if parameters.use_cache:
117
+ # Initialize cache operation context
118
+ operation_context = deepcopy(self._operation_context)
119
+ operation_context.target.type = Target.CACHE
120
+
121
+ redis_response_str = await redis_client.get(cache_key)
122
+
123
+ if redis_response_str is not None:
124
+ operation_timestamp = Timestamp.completed_now(executed_at)
125
+ if cardinality is Cardinality.MULTIPLE:
126
+ response = ReadMultipleDataResponse[
127
+ UserSchema, StrictPagination, None
128
+ ].model_validate_json(redis_response_str)
129
+ operation = ReadMultipleResourceOperation[
130
+ UserSchema, StrictPagination, None
131
+ ](
132
+ application_context=self._application_context,
133
+ id=operation_id,
134
+ context=operation_context,
135
+ resource=self._resource,
136
+ timestamp=operation_timestamp,
137
+ summary=f"Successfully read multiple {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
138
+ connection_context=connection_context,
139
+ authentication=None,
140
+ authorization=authorization,
141
+ impersonation=impersonation,
142
+ response=response,
143
+ )
144
+ operation.log(self._logger, LogLevel.INFO)
145
+ operation.publish(self._logger, self._publishers)
146
+ elif cardinality is Cardinality.SINGLE:
147
+ response = ReadSingleDataResponse[
148
+ UserSchema, None
149
+ ].model_validate_json(redis_response_str)
150
+ operation = ReadSingleResourceOperation[UserSchema, None](
151
+ application_context=self._application_context,
152
+ id=operation_id,
153
+ context=operation_context,
154
+ resource=self._resource,
155
+ timestamp=operation_timestamp,
156
+ summary=f"Successfully read single {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
157
+ connection_context=connection_context,
158
+ authentication=None,
159
+ authorization=authorization,
160
+ impersonation=impersonation,
161
+ response=response,
162
+ )
163
+ operation.log(self._logger, LogLevel.INFO)
164
+ operation.publish(self._logger, self._publishers)
165
+
166
+ return response # type: ignore
167
+
168
+ operation_context = deepcopy(self._operation_context)
169
+ operation_context.target.type = Target.MICROSERVICE
170
+
171
+ async with self._http_client_manager.get() as http_client:
172
+ base_headers = {
173
+ Header.CONTENT_TYPE.value: "application/json",
174
+ Header.X_OPERATION_ID.value: str(operation_id),
175
+ }
176
+ if impersonation is not None:
177
+ base_headers[Header.X_USER_ID.value] = str(impersonation.user_id)
178
+ if impersonation.organization_id is not None:
179
+ base_headers[Header.X_ORGANIZATION_ID.value] = str(
180
+ impersonation.organization_id
181
+ )
182
+
183
+ if headers is not None:
184
+ headers = merge_dicts(base_headers, headers)
185
+ else:
186
+ headers = base_headers
187
+
188
+ auth = AuthorizationFactory.httpx_auth(
189
+ scheme=authorization.scheme, authorization=authorization.credentials
190
+ )
191
+
192
+ base_url = f"{self._config.url}/v1/{self._resource.identifiers[-1].slug}"
193
+ if isinstance(parameters, ReadMultipleParameter):
194
+ url = base_url
195
+ elif isinstance(parameters, ReadSingleParameter):
196
+ if is_id_identifier(parameters.identifier):
197
+ url = base_url + f"/{parameters.identifier.value}"
198
+ else:
199
+ url = (
200
+ base_url
201
+ + f"/{parameters.identifier.type}/{parameters.identifier.value}"
202
+ )
203
+
204
+ params = parameters.to_query_params()
205
+
206
+ response = await http_client.get(
207
+ url, params=params, headers=headers, auth=auth
208
+ )
209
+
210
+ operation_timestamp = Timestamp.completed_now(executed_at)
211
+
212
+ if response.is_error:
213
+ exc = MaleoExceptionFactory.from_httpx(
214
+ response,
215
+ operation_type=OperationType.REQUEST,
216
+ application_context=self._application_context,
217
+ operation_id=operation_id,
218
+ operation_context=operation_context,
219
+ operation_action=ReadResourceOperationAction(),
220
+ operation_timestamp=operation_timestamp,
221
+ connection_context=connection_context,
222
+ authentication=None,
223
+ authorization=authorization,
224
+ impersonation=impersonation,
225
+ logger=self._logger,
226
+ )
227
+ exc.log_and_publish_operation(self._logger, self._publishers)
228
+ raise exc
229
+
230
+ if isinstance(parameters, ReadMultipleParameter):
231
+ validated_response = MultipleDataResponse[
232
+ UserSchema, StrictPagination, None
233
+ ].model_validate(response.json())
234
+ service_response = ReadMultipleDataResponse[
235
+ UserSchema, StrictPagination, None
236
+ ].new(
237
+ data=validated_response.data,
238
+ pagination=validated_response.pagination,
239
+ )
240
+ operation = ReadMultipleResourceOperation[
241
+ UserSchema, StrictPagination, None
242
+ ](
243
+ application_context=self._application_context,
244
+ id=operation_id,
245
+ context=operation_context,
246
+ resource=self._resource,
247
+ timestamp=operation_timestamp,
248
+ summary=f"Successfully read multiple {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
249
+ connection_context=connection_context,
250
+ authentication=None,
251
+ authorization=authorization,
252
+ impersonation=impersonation,
253
+ response=service_response,
254
+ )
255
+ operation.log(self._logger, LogLevel.INFO)
256
+ operation.publish(self._logger, self._publishers)
257
+ elif isinstance(parameters, ReadSingleParameter):
258
+ validated_response = SingleDataResponse[
259
+ UserSchema, None
260
+ ].model_validate(response.json())
261
+ service_response = ReadSingleDataResponse[UserSchema, None].new(
262
+ data=validated_response.data,
263
+ )
264
+ operation = ReadSingleResourceOperation[UserSchema, None](
265
+ application_context=self._application_context,
266
+ id=operation_id,
267
+ context=operation_context,
268
+ resource=self._resource,
269
+ timestamp=operation_timestamp,
270
+ summary=f"Successfully read single {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
271
+ connection_context=connection_context,
272
+ authentication=None,
273
+ authorization=authorization,
274
+ impersonation=impersonation,
275
+ response=service_response,
276
+ )
277
+ operation.log(self._logger, LogLevel.INFO)
278
+ operation.publish(self._logger, self._publishers)
279
+
280
+ return service_response # type: ignore
281
+
282
+ async def verify_password(
283
+ self,
284
+ *,
285
+ operation_id: UUID,
286
+ connection_context: ConnectionContext,
287
+ authorization: AnyAuthorization,
288
+ impersonation: OptImpersonation = None,
289
+ parameters: VerifyPasswordParameter[VerifyPasswordDataT],
290
+ headers: OptStrToStrDict = None,
291
+ ) -> CreateSingleDataResponse[VerifyPasswordSchema, None]:
292
+ executed_at = datetime.now(tz=timezone.utc)
293
+
294
+ operation_context = deepcopy(self._operation_context)
295
+ operation_context.target.type = Target.MICROSERVICE
296
+
297
+ async with self._http_client_manager.get() as http_client:
298
+ base_headers = {
299
+ Header.CONTENT_TYPE.value: "application/json",
300
+ Header.X_OPERATION_ID.value: str(operation_id),
301
+ }
302
+ if impersonation is not None:
303
+ base_headers[Header.X_USER_ID.value] = str(impersonation.user_id)
304
+ if impersonation.organization_id is not None:
305
+ base_headers[Header.X_ORGANIZATION_ID.value] = str(
306
+ impersonation.organization_id
307
+ )
308
+
309
+ if headers is not None:
310
+ headers = merge_dicts(base_headers, headers)
311
+ else:
312
+ headers = base_headers
313
+
314
+ auth = AuthorizationFactory.httpx_auth(
315
+ scheme=authorization.scheme, authorization=authorization.credentials
316
+ )
317
+
318
+ base_url = f"{self._config.url}/v1/{self._resource.identifiers[-1].slug}"
319
+ if is_id_identifier(parameters.identifier):
320
+ url = base_url + f"/{parameters.identifier.value}"
321
+ else:
322
+ url = (
323
+ base_url
324
+ + f"/{parameters.identifier.type}/{parameters.identifier.value}"
325
+ )
326
+
327
+ url += "/password/verify"
328
+
329
+ if isinstance(parameters.data, EncryptedVerifyPasswordData):
330
+ url += "/encrypted"
331
+
332
+ response = await http_client.post(
333
+ url,
334
+ json=parameters.data.model_dump(mode="json"),
335
+ headers=headers,
336
+ auth=auth,
337
+ )
338
+
339
+ operation_timestamp = Timestamp.completed_now(executed_at)
340
+
341
+ if response.is_error:
342
+ exc = MaleoExceptionFactory.from_httpx(
343
+ response,
344
+ operation_type=OperationType.REQUEST,
345
+ application_context=self._application_context,
346
+ operation_id=operation_id,
347
+ operation_context=operation_context,
348
+ operation_action=CreateResourceOperationAction(),
349
+ operation_timestamp=operation_timestamp,
350
+ connection_context=connection_context,
351
+ authentication=None,
352
+ authorization=authorization,
353
+ impersonation=impersonation,
354
+ logger=self._logger,
355
+ )
356
+ exc.log_and_publish_operation(self._logger, self._publishers)
357
+ raise exc
358
+
359
+ validated_response = SingleDataResponse[
360
+ VerifyPasswordSchema, None
361
+ ].model_validate(response.json())
362
+ service_response = CreateSingleDataResponse[VerifyPasswordSchema, None].new(
363
+ data=validated_response.data,
364
+ )
365
+ operation = CreateSingleResourceOperation[VerifyPasswordSchema, None](
366
+ application_context=self._application_context,
367
+ id=operation_id,
368
+ context=operation_context,
369
+ resource=self._resource,
370
+ timestamp=operation_timestamp,
371
+ summary=f"Successfully verified {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()}'s password in microservice",
372
+ connection_context=connection_context,
373
+ authentication=None,
374
+ authorization=authorization,
375
+ impersonation=impersonation,
376
+ response=service_response,
377
+ )
378
+ operation.log(self._logger, LogLevel.INFO)
379
+ operation.publish(self._logger, self._publishers)
380
+
381
+ return service_response
@@ -0,0 +1,282 @@
1
+ import json
2
+ from copy import deepcopy
3
+ from datetime import datetime, timezone
4
+ from typing import ClassVar, 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 nexo.schemas.connection import ConnectionContext
13
+ from nexo.schemas.exception.factory import MaleoExceptionFactory
14
+ from nexo.schemas.operation.action.resource import ReadResourceOperationAction
15
+ from nexo.schemas.operation.enums import OperationType, Target
16
+ from nexo.schemas.operation.mixins import Timestamp
17
+ from nexo.schemas.operation.resource import (
18
+ ReadMultipleResourceOperation,
19
+ ReadSingleResourceOperation,
20
+ )
21
+ from nexo.schemas.pagination import StrictPagination
22
+ from nexo.schemas.resource import Resource, AggregateField
23
+ from nexo.schemas.response import (
24
+ MultipleDataResponse,
25
+ ReadMultipleDataResponse,
26
+ SingleDataResponse,
27
+ ReadSingleDataResponse,
28
+ )
29
+ from nexo.schemas.security.authorization import (
30
+ AnyAuthorization,
31
+ AuthorizationFactory,
32
+ )
33
+ from nexo.schemas.security.impersonation import OptImpersonation
34
+ from nexo.types.dict import OptStrToStrDict
35
+ from nexo.utils.merger import merge_dicts
36
+ from maleo.identity.constants.user_medical_role import USER_MEDICAL_ROLE_RESOURCE
37
+ from maleo.identity.mixins.user_medical_role import (
38
+ is_id_identifier,
39
+ is_composite_identifier,
40
+ )
41
+ from maleo.identity.schemas.common import UserMedicalRoleSchema
42
+ from maleo.identity.schemas.user_medical_role import (
43
+ ReadMultipleParameter,
44
+ ReadSingleParameter,
45
+ )
46
+ from ..config import ClientConfig
47
+
48
+
49
+ class UserMedicalRoleClientService(ClientService[ClientConfig]):
50
+ _resource: ClassVar[Resource] = USER_MEDICAL_ROLE_RESOURCE
51
+
52
+ @overload
53
+ async def read(
54
+ self,
55
+ cardinality: Literal[Cardinality.MULTIPLE],
56
+ *,
57
+ operation_id: UUID,
58
+ connection_context: ConnectionContext,
59
+ authorization: AnyAuthorization,
60
+ impersonation: OptImpersonation = None,
61
+ parameters: ReadMultipleParameter,
62
+ headers: OptStrToStrDict = None,
63
+ ) -> ReadMultipleDataResponse[UserMedicalRoleSchema, StrictPagination, None]: ...
64
+ @overload
65
+ async def read(
66
+ self,
67
+ cardinality: Literal[Cardinality.SINGLE],
68
+ *,
69
+ operation_id: UUID,
70
+ connection_context: ConnectionContext,
71
+ authorization: AnyAuthorization,
72
+ impersonation: OptImpersonation = None,
73
+ parameters: ReadSingleParameter,
74
+ headers: OptStrToStrDict = None,
75
+ ) -> ReadSingleDataResponse[UserMedicalRoleSchema, None]: ...
76
+ async def read(
77
+ self,
78
+ cardinality: Cardinality,
79
+ *,
80
+ operation_id: UUID,
81
+ connection_context: ConnectionContext,
82
+ authorization: AnyAuthorization,
83
+ impersonation: OptImpersonation = None,
84
+ parameters: ReadMultipleParameter | ReadSingleParameter,
85
+ headers: OptStrToStrDict = None,
86
+ ) -> (
87
+ ReadMultipleDataResponse[UserMedicalRoleSchema, StrictPagination, None]
88
+ | ReadSingleDataResponse[UserMedicalRoleSchema, None]
89
+ ):
90
+ redis_client = self._redis.manager.client.get(Connection.ASYNC)
91
+
92
+ executed_at = datetime.now(tz=timezone.utc)
93
+
94
+ # Define arguments being used in this function
95
+ positional_arguments = [cardinality]
96
+ keyword_arguments = {
97
+ "authorization": (
98
+ authorization.model_dump(mode="json")
99
+ if authorization is not None
100
+ else None
101
+ ),
102
+ "parameters": parameters.model_dump(mode="json"),
103
+ }
104
+
105
+ # Define full function string
106
+ ext = f"({json.dumps(positional_arguments)}|{json.dumps(keyword_arguments)})"
107
+
108
+ # Define full cache_key
109
+ cache_key = build_cache_key(ext, namespace=self._namespace)
110
+
111
+ if parameters.use_cache:
112
+ # Initialize cache operation context
113
+ operation_context = deepcopy(self._operation_context)
114
+ operation_context.target.type = Target.CACHE
115
+
116
+ redis_response_str = await redis_client.get(cache_key)
117
+
118
+ if redis_response_str is not None:
119
+ operation_timestamp = Timestamp.completed_now(executed_at)
120
+ if cardinality is Cardinality.MULTIPLE:
121
+ response = ReadMultipleDataResponse[
122
+ UserMedicalRoleSchema, StrictPagination, None
123
+ ].model_validate_json(redis_response_str)
124
+ operation = ReadMultipleResourceOperation[
125
+ UserMedicalRoleSchema, StrictPagination, None
126
+ ](
127
+ application_context=self._application_context,
128
+ id=operation_id,
129
+ context=operation_context,
130
+ resource=self._resource,
131
+ timestamp=operation_timestamp,
132
+ summary=f"Successfully read multiple {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
133
+ connection_context=connection_context,
134
+ authentication=None,
135
+ authorization=authorization,
136
+ impersonation=impersonation,
137
+ response=response,
138
+ )
139
+ operation.log(self._logger, LogLevel.INFO)
140
+ operation.publish(self._logger, self._publishers)
141
+ elif cardinality is Cardinality.SINGLE:
142
+ response = ReadSingleDataResponse[
143
+ UserMedicalRoleSchema, None
144
+ ].model_validate_json(redis_response_str)
145
+ operation = ReadSingleResourceOperation[
146
+ UserMedicalRoleSchema, None
147
+ ](
148
+ application_context=self._application_context,
149
+ id=operation_id,
150
+ context=operation_context,
151
+ resource=self._resource,
152
+ timestamp=operation_timestamp,
153
+ summary=f"Successfully read single {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
154
+ connection_context=connection_context,
155
+ authentication=None,
156
+ authorization=authorization,
157
+ impersonation=impersonation,
158
+ response=response,
159
+ )
160
+ operation.log(self._logger, LogLevel.INFO)
161
+ operation.publish(self._logger, self._publishers)
162
+
163
+ return response # type: ignore
164
+
165
+ operation_context = deepcopy(self._operation_context)
166
+ operation_context.target.type = Target.MICROSERVICE
167
+
168
+ async with self._http_client_manager.get() as http_client:
169
+ base_headers = {
170
+ Header.CONTENT_TYPE.value: "application/json",
171
+ Header.X_OPERATION_ID.value: str(operation_id),
172
+ }
173
+ if impersonation is not None:
174
+ base_headers[Header.X_USER_ID.value] = str(impersonation.user_id)
175
+ if impersonation.organization_id is not None:
176
+ base_headers[Header.X_ORGANIZATION_ID.value] = str(
177
+ impersonation.organization_id
178
+ )
179
+
180
+ if headers is not None:
181
+ headers = merge_dicts(base_headers, headers)
182
+ else:
183
+ headers = base_headers
184
+
185
+ auth = AuthorizationFactory.httpx_auth(
186
+ scheme=authorization.scheme, authorization=authorization.credentials
187
+ )
188
+
189
+ base_url = f"{self._config.url}/v1/{self._resource.identifiers[-1].slug}"
190
+ if isinstance(parameters, ReadMultipleParameter):
191
+ url = base_url
192
+ elif isinstance(parameters, ReadSingleParameter):
193
+ if is_composite_identifier(parameters.identifier):
194
+ value = parameters.identifier.value
195
+ url = f"{self._config.url}/v1/users/{value[0]}/organizations/{value[1]}/medical-roles/{value[2]}"
196
+ elif is_id_identifier(parameters.identifier):
197
+ url = base_url + f"/{parameters.identifier.value}"
198
+ else:
199
+ url = (
200
+ base_url
201
+ + f"/{parameters.identifier.type}/{parameters.identifier.value}"
202
+ )
203
+
204
+ params = parameters.to_query_params()
205
+
206
+ response = await http_client.get(
207
+ url, params=params, headers=headers, auth=auth
208
+ )
209
+
210
+ operation_timestamp = Timestamp.completed_now(executed_at)
211
+
212
+ if response.is_error:
213
+ exc = MaleoExceptionFactory.from_httpx(
214
+ response,
215
+ operation_type=OperationType.REQUEST,
216
+ application_context=self._application_context,
217
+ operation_id=operation_id,
218
+ operation_context=operation_context,
219
+ operation_action=ReadResourceOperationAction(),
220
+ operation_timestamp=operation_timestamp,
221
+ connection_context=connection_context,
222
+ authentication=None,
223
+ authorization=authorization,
224
+ impersonation=impersonation,
225
+ logger=self._logger,
226
+ )
227
+ exc.log_and_publish_operation(self._logger, self._publishers)
228
+ raise exc
229
+
230
+ if isinstance(parameters, ReadMultipleParameter):
231
+ validated_response = MultipleDataResponse[
232
+ UserMedicalRoleSchema, StrictPagination, None
233
+ ].model_validate(response.json())
234
+ service_response = ReadMultipleDataResponse[
235
+ UserMedicalRoleSchema, StrictPagination, None
236
+ ].new(
237
+ data=validated_response.data,
238
+ pagination=validated_response.pagination,
239
+ )
240
+ operation = ReadMultipleResourceOperation[
241
+ UserMedicalRoleSchema, StrictPagination, None
242
+ ](
243
+ application_context=self._application_context,
244
+ id=operation_id,
245
+ context=operation_context,
246
+ resource=self._resource,
247
+ timestamp=operation_timestamp,
248
+ summary=f"Successfully read multiple {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
249
+ connection_context=connection_context,
250
+ authentication=None,
251
+ authorization=authorization,
252
+ impersonation=impersonation,
253
+ response=service_response,
254
+ )
255
+ operation.log(self._logger, LogLevel.INFO)
256
+ operation.publish(self._logger, self._publishers)
257
+ elif isinstance(parameters, ReadSingleParameter):
258
+ validated_response = SingleDataResponse[
259
+ UserMedicalRoleSchema, None
260
+ ].model_validate(response.json())
261
+ service_response = ReadSingleDataResponse[
262
+ UserMedicalRoleSchema, None
263
+ ].new(
264
+ data=validated_response.data,
265
+ )
266
+ operation = ReadSingleResourceOperation[UserMedicalRoleSchema, None](
267
+ application_context=self._application_context,
268
+ id=operation_id,
269
+ context=operation_context,
270
+ resource=self._resource,
271
+ timestamp=operation_timestamp,
272
+ summary=f"Successfully read single {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
273
+ connection_context=connection_context,
274
+ authentication=None,
275
+ authorization=authorization,
276
+ impersonation=impersonation,
277
+ response=service_response,
278
+ )
279
+ operation.log(self._logger, LogLevel.INFO)
280
+ operation.publish(self._logger, self._publishers)
281
+
282
+ return service_response # type: ignore