maleo-metadata-client 0.0.4__py3-none-any.whl → 0.0.6__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.

@@ -1,379 +1,322 @@
1
1
  import json
2
2
  from copy import deepcopy
3
- from Crypto.PublicKey.RSA import RsaKey
4
3
  from datetime import datetime, timezone
5
- from redis.asyncio.client import Redis
6
- from typing import Dict, List, Literal, Optional, Union, overload
4
+ from typing import Literal, Union, overload
7
5
  from uuid import UUID
8
- from maleo.soma.authorization import BearerAuth
9
- from maleo.soma.dtos.configurations.cache.redis import RedisCacheNamespaces
10
- from maleo.soma.enums.cardinality import Cardinality
11
- from maleo.soma.enums.environment import Environment
12
- from maleo.soma.enums.expiration import Expiration
13
- from maleo.soma.enums.logging import LogLevel
14
- from maleo.soma.enums.operation import OperationTarget
15
- from maleo.soma.managers.client.maleo import MaleoClientService
16
- from maleo.soma.managers.client.http import HTTPClientManager
17
- from maleo.soma.managers.credential import CredentialManager
18
- from maleo.soma.schemas.authentication import OptionalAuthentication
19
- from maleo.soma.schemas.data import DataPair
20
- from maleo.soma.schemas.operation.context import (
21
- OperationContextSchema,
22
- OperationOriginSchema,
23
- OperationLayerSchema,
24
- OperationTargetSchema,
25
- )
26
- from maleo.soma.schemas.operation.resource import (
27
- ReadSingleResourceOperationSchema,
28
- ReadMultipleResourceOperationSchema,
6
+ from maleo.database.enums import Connection
7
+ from maleo.database.utils import build_cache_key
8
+ from maleo.enums.cardinality import Cardinality
9
+ from maleo.enums.connection import Header
10
+ from maleo.logging.enums import Level
11
+ from maleo.managers.client.maleo.config import MaleoMetadataClientConfig
12
+ from maleo.managers.client.maleo import MaleoClientService
13
+ from maleo.metadata.constants.blood_type import BLOOD_TYPE_RESOURCE
14
+ from maleo.metadata.schemas.blood_type import (
15
+ ReadMultipleParameter,
16
+ ReadSingleParameter,
17
+ StandardBloodTypeSchema,
18
+ FullBloodTypeSchema,
29
19
  )
30
- from maleo.soma.schemas.operation.resource.action import ReadResourceOperationAction
31
- from maleo.soma.schemas.operation.resource.result import (
32
- ReadSingleResourceOperationResult,
33
- ReadMultipleResourceOperationResult,
20
+ from maleo.metadata.enums.blood_type import Granularity
21
+ from maleo.metadata.utils.blood_type import get_schema_model
22
+ from maleo.schemas.connection import ConnectionContext
23
+ from maleo.schemas.exception.factory import Factory as MaleoExceptionFactory
24
+ from maleo.schemas.mixins.filter import convert as convert_filter
25
+ from maleo.schemas.mixins.sort import convert as convert_sort
26
+ from maleo.schemas.operation.action.resource import ReadResourceOperationAction
27
+ from maleo.schemas.operation.enums import OperationType, Target
28
+ from maleo.schemas.operation.mixins import Timestamp
29
+ from maleo.schemas.operation.resource import (
30
+ ReadMultipleResourceOperation,
31
+ ReadSingleResourceOperation,
34
32
  )
35
- from maleo.soma.schemas.operation.timestamp import OperationTimestamp
36
- from maleo.soma.schemas.pagination import FlexiblePagination
37
- from maleo.soma.schemas.parameter.general import ReadSingleQueryParameterSchema
38
- from maleo.soma.schemas.request import RequestContext
39
- from maleo.soma.schemas.response import (
40
- SingleDataResponseSchema,
41
- MultipleDataResponseSchema,
33
+ from maleo.schemas.pagination import StrictPagination
34
+ from maleo.schemas.response import (
35
+ MultipleDataResponse,
36
+ ReadMultipleDataResponse,
37
+ SingleDataResponse,
38
+ ReadSingleDataResponse,
42
39
  )
43
- from maleo.soma.schemas.service import ServiceContext
44
- from maleo.soma.utils.cache import build_key
45
- from maleo.soma.utils.logging import ClientLogger
46
- from maleo.soma.utils.merger import merge_dicts
47
- from maleo.soma.utils.token import reencode
48
- from maleo.metadata.constants.blood_type import RESOURCE
49
- from maleo.metadata.schemas.data.blood_type import BloodTypeDataSchema
50
- from maleo.metadata.schemas.parameter.client.blood_type import (
51
- ReadMultipleParameter,
52
- ReadMultipleQueryParameter,
40
+ from maleo.schemas.security.authorization import (
41
+ OptionalAnyAuthorization,
42
+ AnyAuthorization,
43
+ Factory as AuthorizationFactory,
53
44
  )
54
- from maleo.metadata.schemas.parameter.general.blood_type import ReadSingleParameter
45
+ from maleo.schemas.security.impersonation import OptionalImpersonation
46
+ from maleo.types.dict import OptionalStringToStringDict
47
+ from maleo.utils.merger import merge_dicts
55
48
 
56
49
 
57
- class BloodTypeClientService(MaleoClientService):
58
- def __init__(
59
- self,
60
- environment: Environment,
61
- key: str,
62
- url: str,
63
- operation_origin: OperationOriginSchema,
64
- logger: ClientLogger,
65
- credential_manager: CredentialManager,
66
- http_client_manager: HTTPClientManager,
67
- private_key: RsaKey,
68
- redis: Redis,
69
- redis_namespaces: RedisCacheNamespaces,
70
- service_context: ServiceContext,
71
- ):
72
- super().__init__(
73
- environment,
74
- key,
75
- url,
76
- operation_origin,
77
- logger,
78
- credential_manager,
79
- http_client_manager,
80
- private_key,
81
- redis,
82
- redis_namespaces,
83
- service_context,
84
- )
85
- self._namespace = self._redis_namespaces.create(
86
- self._key,
87
- RESOURCE.aggregate(),
88
- origin=self._CACHE_ORIGIN,
89
- layer=self._CACHE_LAYER,
90
- )
91
- self._default_operation_context = OperationContextSchema(
92
- origin=self._operation_origin,
93
- layer=OperationLayerSchema(type=self._OPERATION_LAYER_TYPE, details=None),
94
- target=OperationTargetSchema(
95
- type=self._OPERATION_TARGET_TYPE, details=None
96
- ),
97
- )
50
+ class BloodTypeClientService(MaleoClientService[MaleoMetadataClientConfig]):
51
+ resource = BLOOD_TYPE_RESOURCE
98
52
 
99
53
  @overload
100
54
  async def read(
101
55
  self,
102
56
  cardinality: Literal[Cardinality.MULTIPLE],
57
+ granularity: Literal[Granularity.STANDARD],
103
58
  *,
104
59
  operation_id: UUID,
105
- request_context: RequestContext,
106
- authentication: OptionalAuthentication,
60
+ resource_operation_action: ReadResourceOperationAction,
61
+ connection_context: ConnectionContext,
62
+ authorization: AnyAuthorization,
63
+ impersonation: OptionalImpersonation = None,
107
64
  parameters: ReadMultipleParameter,
108
- headers: Optional[Dict[str, str]] = None,
109
- ) -> ReadMultipleResourceOperationResult[
110
- BloodTypeDataSchema, FlexiblePagination, None
111
- ]: ...
65
+ headers: OptionalStringToStringDict = None,
66
+ ) -> ReadMultipleDataResponse[StandardBloodTypeSchema, StrictPagination, None]: ...
67
+ @overload
68
+ async def read(
69
+ self,
70
+ cardinality: Literal[Cardinality.MULTIPLE],
71
+ granularity: Literal[Granularity.FULL],
72
+ *,
73
+ operation_id: UUID,
74
+ resource_operation_action: ReadResourceOperationAction,
75
+ connection_context: ConnectionContext,
76
+ authorization: AnyAuthorization,
77
+ impersonation: OptionalImpersonation = None,
78
+ parameters: ReadMultipleParameter,
79
+ headers: OptionalStringToStringDict = None,
80
+ ) -> ReadMultipleDataResponse[FullBloodTypeSchema, StrictPagination, None]: ...
81
+ @overload
82
+ async def read(
83
+ self,
84
+ cardinality: Literal[Cardinality.SINGLE],
85
+ granularity: Literal[Granularity.STANDARD],
86
+ *,
87
+ operation_id: UUID,
88
+ resource_operation_action: ReadResourceOperationAction,
89
+ connection_context: ConnectionContext,
90
+ authorization: AnyAuthorization,
91
+ impersonation: OptionalImpersonation = None,
92
+ parameters: ReadSingleParameter,
93
+ headers: OptionalStringToStringDict = None,
94
+ ) -> ReadSingleDataResponse[StandardBloodTypeSchema, None]: ...
112
95
  @overload
113
96
  async def read(
114
97
  self,
115
98
  cardinality: Literal[Cardinality.SINGLE],
99
+ granularity: Literal[Granularity.FULL],
116
100
  *,
117
101
  operation_id: UUID,
118
- request_context: RequestContext,
119
- authentication: OptionalAuthentication,
102
+ resource_operation_action: ReadResourceOperationAction,
103
+ connection_context: ConnectionContext,
104
+ authorization: AnyAuthorization,
105
+ impersonation: OptionalImpersonation = None,
120
106
  parameters: ReadSingleParameter,
121
- headers: Optional[Dict[str, str]] = None,
122
- ) -> ReadSingleResourceOperationResult[BloodTypeDataSchema, None]: ...
107
+ headers: OptionalStringToStringDict = None,
108
+ ) -> ReadSingleDataResponse[FullBloodTypeSchema, None]: ...
123
109
  async def read(
124
110
  self,
125
111
  cardinality: Cardinality,
112
+ granularity: Granularity,
126
113
  *,
127
114
  operation_id: UUID,
128
- request_context: RequestContext,
129
- authentication: OptionalAuthentication,
115
+ resource_operation_action: ReadResourceOperationAction,
116
+ connection_context: ConnectionContext,
117
+ authorization: OptionalAnyAuthorization = None,
118
+ impersonation: OptionalImpersonation = None,
130
119
  parameters: Union[ReadMultipleParameter, ReadSingleParameter],
131
- headers: Optional[Dict[str, str]] = None,
120
+ headers: OptionalStringToStringDict = None,
132
121
  ) -> Union[
133
- ReadMultipleResourceOperationResult[
134
- BloodTypeDataSchema, FlexiblePagination, None
135
- ],
136
- ReadSingleResourceOperationResult[BloodTypeDataSchema, None],
122
+ ReadMultipleDataResponse[StandardBloodTypeSchema, StrictPagination, None],
123
+ ReadMultipleDataResponse[FullBloodTypeSchema, StrictPagination, None],
124
+ ReadSingleDataResponse[StandardBloodTypeSchema, None],
125
+ ReadSingleDataResponse[FullBloodTypeSchema, None],
137
126
  ]:
138
- operation_action = ReadResourceOperationAction()
139
- executed_at = datetime.now(tz=timezone.utc)
127
+ redis_client = self._redis.manager.client.get(Connection.ASYNC)
128
+ data_model_cls = get_schema_model(granularity)
140
129
 
141
- # Get function identifier
142
- func = self.__class__
143
- module, qualname = func.__module__, func.__qualname__
130
+ executed_at = datetime.now(tz=timezone.utc)
144
131
 
145
132
  # Define arguments being used in this function
146
- positional_arguments = [cardinality]
133
+ positional_arguments = [cardinality, granularity]
147
134
  keyword_arguments = {
148
- "authentication": authentication.model_dump(
149
- mode="json",
150
- exclude={
151
- "credentials": {
152
- "token": {
153
- "payload": {
154
- "iat_dt",
155
- "iat",
156
- "exp_dt",
157
- "exp",
158
- }
159
- }
160
- }
161
- },
135
+ "authorization": (
136
+ authorization.model_dump(mode="json")
137
+ if authorization is not None
138
+ else None
162
139
  ),
163
140
  "parameters": parameters.model_dump(mode="json"),
164
141
  }
165
142
 
166
143
  # Define full function string
167
- function = f"{qualname}({json.dumps(positional_arguments)}|{json.dumps(keyword_arguments)})"
144
+ ext = f"({json.dumps(positional_arguments)}|{json.dumps(keyword_arguments)})"
168
145
 
169
- # Define full cache key
170
- cache_key = build_key(module, function, namespace=self._namespace)
146
+ # Define full cache_key
147
+ cache_key = build_cache_key(ext, namespace=self._namespace)
171
148
 
172
149
  if parameters.use_cache:
173
- operation_context = deepcopy(self._default_operation_context)
174
- operation_context.target.type = OperationTarget.CACHE
150
+ # Initialize cache operation context
151
+ operation_context = deepcopy(self._operation_context)
152
+ operation_context.target.type = Target.CACHE
175
153
 
176
- # Check redis for data
177
- result_str = await self._redis.get(cache_key)
154
+ redis_response_str = await redis_client.get(cache_key)
178
155
 
179
- if result_str is not None:
180
- completed_at = datetime.now(tz=timezone.utc)
181
-
182
- if isinstance(parameters, ReadMultipleParameter):
183
- result = ReadMultipleResourceOperationResult[
184
- BloodTypeDataSchema, FlexiblePagination, None
185
- ].model_validate(json.loads(result_str))
186
- ReadMultipleResourceOperationSchema[
187
- OptionalAuthentication,
188
- BloodTypeDataSchema,
189
- FlexiblePagination,
190
- None,
156
+ if redis_response_str is not None:
157
+ operation_timestamp = Timestamp.completed_now(executed_at)
158
+ if cardinality is Cardinality.MULTIPLE:
159
+ response = ReadMultipleDataResponse[
160
+ data_model_cls, StrictPagination, None
161
+ ].model_validate_json(redis_response_str)
162
+ ReadMultipleResourceOperation[
163
+ data_model_cls, StrictPagination, None
191
164
  ](
192
- service_context=self._service_context,
165
+ application_context=self._application_context,
193
166
  id=operation_id,
194
167
  context=operation_context,
195
- timestamp=OperationTimestamp(
196
- executed_at=executed_at,
197
- completed_at=completed_at,
198
- duration=(completed_at - executed_at).total_seconds(),
199
- ),
200
- summary="Successfully retrieved multiple blood types from cache",
201
- request_context=request_context,
202
- authentication=authentication,
203
- action=operation_action,
204
- resource=RESOURCE,
205
- result=result,
168
+ action=resource_operation_action,
169
+ resource=self.resource,
170
+ timestamp=operation_timestamp,
171
+ summary=f"Successfully retrieved {cardinality} {granularity} blood types from cache",
172
+ connection_context=connection_context,
173
+ authentication=None,
174
+ authorization=authorization,
175
+ impersonation=impersonation,
176
+ response=response,
206
177
  ).log(
207
- self._logger, LogLevel.INFO
178
+ self._logger, Level.INFO
208
179
  )
209
- elif isinstance(parameters, ReadSingleParameter):
210
- result = ReadSingleResourceOperationResult[
211
- BloodTypeDataSchema, None
212
- ].model_validate(json.loads(result_str))
213
- ReadSingleResourceOperationSchema[
214
- OptionalAuthentication, BloodTypeDataSchema, None
215
- ](
216
- service_context=self._service_context,
180
+ elif cardinality is Cardinality.SINGLE:
181
+ response = ReadSingleDataResponse[
182
+ data_model_cls, None
183
+ ].model_validate_json(redis_response_str)
184
+ ReadSingleResourceOperation[data_model_cls, None](
185
+ application_context=self._application_context,
217
186
  id=operation_id,
218
187
  context=operation_context,
219
- timestamp=OperationTimestamp(
220
- executed_at=executed_at,
221
- completed_at=completed_at,
222
- duration=(completed_at - executed_at).total_seconds(),
223
- ),
224
- summary="Successfully retrieved single blood type from cache",
225
- request_context=request_context,
226
- authentication=authentication,
227
- action=operation_action,
228
- resource=RESOURCE,
229
- result=result,
230
- ).log(
231
- self._logger, LogLevel.INFO
232
- )
233
- return result
188
+ action=resource_operation_action,
189
+ resource=self.resource,
190
+ timestamp=operation_timestamp,
191
+ summary=f"Successfully retrieved {cardinality} {granularity} blood type from cache",
192
+ connection_context=connection_context,
193
+ authentication=None,
194
+ authorization=authorization,
195
+ impersonation=impersonation,
196
+ response=response,
197
+ ).log(self._logger, Level.INFO)
198
+
199
+ return response # type: ignore
200
+
201
+ operation_context = deepcopy(self._operation_context)
202
+ operation_context.target.type = Target.MICROSERVICE
234
203
 
235
- operation_context = deepcopy(self._default_operation_context)
236
204
  async with self._http_client_manager.get() as http_client:
237
- # Create headers
238
205
  base_headers = {
239
- "content-type": "application/json",
240
- "x-operation-id": str(operation_id),
206
+ Header.CONTENT_TYPE.value: "application/json",
207
+ Header.X_OPERATION_ID.value: str(operation_id),
241
208
  }
209
+ if impersonation is not None:
210
+ base_headers[Header.X_USER_ID.value] = str(impersonation.user_id)
211
+ if impersonation.organization_id is not None:
212
+ base_headers[Header.X_ORGANIZATION_ID.value] = str(
213
+ impersonation.organization_id
214
+ )
215
+
242
216
  if headers is not None:
243
217
  headers = merge_dicts(base_headers, headers)
244
218
  else:
245
219
  headers = base_headers
246
220
 
247
- # Create auth
248
- token = None
249
- if authentication.credentials.token is not None:
250
- try:
251
- token = reencode(
252
- payload=authentication.credentials.token.payload,
253
- key=self._private_key,
254
- )
255
- except Exception:
256
- pass
257
-
258
- auth = BearerAuth(token) if token is not None else None
221
+ if authorization is not None:
222
+ auth = AuthorizationFactory.httpx_auth(
223
+ scheme=authorization.scheme, authorization=authorization.credentials
224
+ )
225
+ else:
226
+ auth = None
259
227
 
260
228
  if isinstance(parameters, ReadMultipleParameter):
261
- # Define URL
262
- url = f"{self._url}/v1/{RESOURCE.identifiers[0].url_slug}/"
263
-
264
- # Parse parameters to query params
265
- params = ReadMultipleQueryParameter.model_validate(
266
- parameters.model_dump()
267
- ).model_dump(
268
- exclude={"sort_columns", "date_filters"}, exclude_none=True
229
+ url = f"{self._config.url}/v1/{self.resource.identifiers[0].slug}/"
230
+ parameters.date_filters
231
+ params = parameters.model_dump(
232
+ mode="json",
233
+ include={
234
+ "ids",
235
+ "uuids",
236
+ "statuses",
237
+ "keys",
238
+ "names",
239
+ "search",
240
+ "page",
241
+ "limit",
242
+ "granularity",
243
+ "use_cache",
244
+ },
269
245
  )
246
+ params["filters"] = convert_filter(parameters.date_filters)
247
+ params["sorts"] = convert_sort(parameters.sort_columns)
270
248
  elif isinstance(parameters, ReadSingleParameter):
271
- # Define URL
272
- url = f"{self._url}/v1/{RESOURCE.identifiers[0].url_slug}/{parameters.identifier}/{parameters.value}"
273
-
274
- # Parse parameters to query params
275
- params = ReadSingleQueryParameterSchema.model_validate(
276
- parameters.model_dump()
277
- ).model_dump(exclude_none=True)
249
+ url = f"{self._config.url}/v1/{self.resource.identifiers[0].slug}/{parameters.identifier}/{parameters.value}"
250
+ params = parameters.model_dump(
251
+ mode="json", include={"granularity", "use_cache"}
252
+ )
278
253
 
279
- # Send request and wait for response
280
254
  response = await http_client.get(
281
- url=url, params=params, headers=headers, auth=auth
255
+ url, params=params, headers=headers, auth=auth
282
256
  )
283
257
 
284
- if response.is_success:
285
- completed_at = datetime.now(tz=timezone.utc)
286
-
287
- if isinstance(parameters, ReadMultipleParameter):
288
- validated_response = MultipleDataResponseSchema[
289
- BloodTypeDataSchema, FlexiblePagination, None
290
- ].model_validate(response.json())
291
- data = DataPair[List[BloodTypeDataSchema], None](
292
- old=validated_response.data,
293
- new=None,
294
- )
295
- result = ReadMultipleResourceOperationResult[
296
- BloodTypeDataSchema, FlexiblePagination, None
297
- ](
298
- data=data,
299
- pagination=validated_response.pagination,
300
- metadata=None,
301
- other=None,
302
- )
303
- ReadMultipleResourceOperationSchema[
304
- OptionalAuthentication,
305
- BloodTypeDataSchema,
306
- FlexiblePagination,
307
- None,
308
- ](
309
- service_context=self._service_context,
310
- id=operation_id,
311
- context=operation_context,
312
- timestamp=OperationTimestamp(
313
- executed_at=executed_at,
314
- completed_at=completed_at,
315
- duration=(completed_at - executed_at).total_seconds(),
316
- ),
317
- summary="Successfully retrieved multiple blood types from http request",
318
- request_context=request_context,
319
- authentication=authentication,
320
- action=operation_action,
321
- resource=RESOURCE,
322
- result=result,
323
- ).log(
324
- self._logger, level=LogLevel.INFO
325
- )
326
- elif isinstance(parameters, ReadSingleParameter):
327
- validated_response = SingleDataResponseSchema[
328
- BloodTypeDataSchema, None
329
- ].model_validate(response.json())
330
- data = DataPair[BloodTypeDataSchema, None](
331
- old=validated_response.data,
332
- new=None,
333
- )
334
- result = ReadSingleResourceOperationResult[
335
- BloodTypeDataSchema, None
336
- ](
337
- data=data,
338
- pagination=validated_response.pagination,
339
- metadata=None,
340
- other=None,
341
- )
342
- ReadSingleResourceOperationSchema[
343
- OptionalAuthentication, BloodTypeDataSchema, None
344
- ](
345
- service_context=self._service_context,
346
- id=operation_id,
347
- context=operation_context,
348
- timestamp=OperationTimestamp(
349
- executed_at=executed_at,
350
- completed_at=completed_at,
351
- duration=(completed_at - executed_at).total_seconds(),
352
- ),
353
- summary="Successfully retrieved single blood type from http request",
354
- request_context=request_context,
355
- authentication=authentication,
356
- action=operation_action,
357
- resource=RESOURCE,
358
- result=result,
359
- ).log(
360
- self._logger, level=LogLevel.INFO
361
- )
258
+ operation_timestamp = Timestamp.completed_now(executed_at)
362
259
 
363
- if parameters.use_cache:
364
- await self._redis.set(
365
- cache_key, result.model_dump_json(), Expiration.EXP_1MO.value
366
- )
260
+ if response.is_error:
261
+ raise MaleoExceptionFactory.from_httpx(
262
+ response,
263
+ operation_type=OperationType.REQUEST,
264
+ application_context=self._application_context,
265
+ operation_id=operation_id,
266
+ operation_context=operation_context,
267
+ operation_action=resource_operation_action,
268
+ operation_timestamp=operation_timestamp,
269
+ connection_context=connection_context,
270
+ authentication=None,
271
+ authorization=authorization,
272
+ impersonation=impersonation,
273
+ logger=self._logger,
274
+ )
367
275
 
368
- return result
276
+ if isinstance(parameters, ReadMultipleParameter):
277
+ validated_response = MultipleDataResponse[
278
+ data_model_cls, StrictPagination, None
279
+ ].model_validate(response.json())
280
+ service_response = ReadMultipleDataResponse[
281
+ data_model_cls, StrictPagination, None
282
+ ].new(
283
+ data=validated_response.data,
284
+ pagination=validated_response.pagination,
285
+ )
286
+ ReadMultipleResourceOperation[data_model_cls, StrictPagination, None](
287
+ application_context=self._application_context,
288
+ id=operation_id,
289
+ context=operation_context,
290
+ action=resource_operation_action,
291
+ resource=BLOOD_TYPE_RESOURCE,
292
+ timestamp=operation_timestamp,
293
+ summary=f"Successfully retrieved multiple {granularity} blood types from microservice",
294
+ connection_context=connection_context,
295
+ authentication=None,
296
+ authorization=authorization,
297
+ impersonation=impersonation,
298
+ response=service_response,
299
+ ).log(self._logger, Level.INFO)
300
+ elif isinstance(parameters, ReadSingleParameter):
301
+ validated_response = SingleDataResponse[
302
+ data_model_cls, None
303
+ ].model_validate(response.json())
304
+ service_response = ReadSingleDataResponse[data_model_cls, None].new(
305
+ data=validated_response.data,
306
+ )
307
+ ReadSingleResourceOperation[data_model_cls, None](
308
+ application_context=self._application_context,
309
+ id=operation_id,
310
+ context=operation_context,
311
+ action=resource_operation_action,
312
+ resource=BLOOD_TYPE_RESOURCE,
313
+ timestamp=operation_timestamp,
314
+ summary=f"Successfully retrieved single {granularity} blood type from microservice",
315
+ connection_context=connection_context,
316
+ authentication=None,
317
+ authorization=authorization,
318
+ impersonation=impersonation,
319
+ response=service_response,
320
+ ).log(self._logger, Level.INFO)
369
321
 
370
- self._raise_resource_http_request_error(
371
- response=response,
372
- operation_id=operation_id,
373
- operation_context=operation_context,
374
- executed_at=executed_at,
375
- operation_action=operation_action,
376
- request_context=request_context,
377
- authentication=authentication,
378
- resource=RESOURCE,
379
- )
322
+ return service_response # type: ignore