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,288 @@
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.organization_relation import (
37
+ ORGANIZATION_RELATION_RESOURCE,
38
+ )
39
+ from maleo.identity.mixins.organization_relation import (
40
+ is_id_identifier,
41
+ is_composite_identifier,
42
+ )
43
+ from maleo.identity.schemas.common import OrganizationRelationSchema
44
+ from maleo.identity.schemas.organization_relation import (
45
+ ReadMultipleParameter,
46
+ ReadSingleParameter,
47
+ )
48
+ from ..config import ClientConfig
49
+
50
+
51
+ class OrganizationRelationClientService(ClientService[ClientConfig]):
52
+ _resource: ClassVar[Resource] = ORGANIZATION_RELATION_RESOURCE
53
+
54
+ @overload
55
+ async def read(
56
+ self,
57
+ cardinality: Literal[Cardinality.MULTIPLE],
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
+ OrganizationRelationSchema, StrictPagination, None
67
+ ]: ...
68
+ @overload
69
+ async def read(
70
+ self,
71
+ cardinality: Literal[Cardinality.SINGLE],
72
+ *,
73
+ operation_id: UUID,
74
+ connection_context: ConnectionContext,
75
+ authorization: AnyAuthorization,
76
+ impersonation: OptImpersonation = None,
77
+ parameters: ReadSingleParameter,
78
+ headers: OptStrToStrDict = None,
79
+ ) -> ReadSingleDataResponse[OrganizationRelationSchema, None]: ...
80
+ async def read(
81
+ self,
82
+ cardinality: Cardinality,
83
+ *,
84
+ operation_id: UUID,
85
+ connection_context: ConnectionContext,
86
+ authorization: AnyAuthorization,
87
+ impersonation: OptImpersonation = None,
88
+ parameters: ReadMultipleParameter | ReadSingleParameter,
89
+ headers: OptStrToStrDict = None,
90
+ ) -> (
91
+ ReadMultipleDataResponse[OrganizationRelationSchema, StrictPagination, None]
92
+ | ReadSingleDataResponse[OrganizationRelationSchema, None]
93
+ ):
94
+ redis_client = self._redis.manager.client.get(Connection.ASYNC)
95
+
96
+ executed_at = datetime.now(tz=timezone.utc)
97
+
98
+ # Define arguments being used in this function
99
+ positional_arguments = [cardinality]
100
+ keyword_arguments = {
101
+ "authorization": (
102
+ authorization.model_dump(mode="json")
103
+ if authorization is not None
104
+ else None
105
+ ),
106
+ "parameters": parameters.model_dump(mode="json"),
107
+ }
108
+
109
+ # Define full function string
110
+ ext = f"({json.dumps(positional_arguments)}|{json.dumps(keyword_arguments)})"
111
+
112
+ # Define full cache_key
113
+ cache_key = build_cache_key(ext, namespace=self._namespace)
114
+
115
+ if parameters.use_cache:
116
+ # Initialize cache operation context
117
+ operation_context = deepcopy(self._operation_context)
118
+ operation_context.target.type = Target.CACHE
119
+
120
+ redis_response_str = await redis_client.get(cache_key)
121
+
122
+ if redis_response_str is not None:
123
+ operation_timestamp = Timestamp.completed_now(executed_at)
124
+ if cardinality is Cardinality.MULTIPLE:
125
+ response = ReadMultipleDataResponse[
126
+ OrganizationRelationSchema, StrictPagination, None
127
+ ].model_validate_json(redis_response_str)
128
+ operation = ReadMultipleResourceOperation[
129
+ OrganizationRelationSchema, StrictPagination, None
130
+ ](
131
+ application_context=self._application_context,
132
+ id=operation_id,
133
+ context=operation_context,
134
+ resource=self._resource,
135
+ timestamp=operation_timestamp,
136
+ summary=f"Successfully read multiple {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
137
+ connection_context=connection_context,
138
+ authentication=None,
139
+ authorization=authorization,
140
+ impersonation=impersonation,
141
+ response=response,
142
+ )
143
+ operation.log(self._logger, LogLevel.INFO)
144
+ operation.publish(self._logger, self._publishers)
145
+ elif cardinality is Cardinality.SINGLE:
146
+ response = ReadSingleDataResponse[
147
+ OrganizationRelationSchema, None
148
+ ].model_validate_json(redis_response_str)
149
+ operation = ReadSingleResourceOperation[
150
+ OrganizationRelationSchema, None
151
+ ](
152
+ application_context=self._application_context,
153
+ id=operation_id,
154
+ context=operation_context,
155
+ resource=self._resource,
156
+ timestamp=operation_timestamp,
157
+ summary=f"Successfully read single {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
158
+ connection_context=connection_context,
159
+ authentication=None,
160
+ authorization=authorization,
161
+ impersonation=impersonation,
162
+ response=response,
163
+ )
164
+ operation.log(self._logger, LogLevel.INFO)
165
+ operation.publish(self._logger, self._publishers)
166
+
167
+ return response # type: ignore
168
+
169
+ operation_context = deepcopy(self._operation_context)
170
+ operation_context.target.type = Target.MICROSERVICE
171
+
172
+ async with self._http_client_manager.get() as http_client:
173
+ base_headers = {
174
+ Header.CONTENT_TYPE.value: "application/json",
175
+ Header.X_OPERATION_ID.value: str(operation_id),
176
+ }
177
+ if impersonation is not None:
178
+ base_headers[Header.X_USER_ID.value] = str(impersonation.user_id)
179
+ if impersonation.organization_id is not None:
180
+ base_headers[Header.X_ORGANIZATION_ID.value] = str(
181
+ impersonation.organization_id
182
+ )
183
+
184
+ if headers is not None:
185
+ headers = merge_dicts(base_headers, headers)
186
+ else:
187
+ headers = base_headers
188
+
189
+ auth = AuthorizationFactory.httpx_auth(
190
+ scheme=authorization.scheme, authorization=authorization.credentials
191
+ )
192
+
193
+ base_url = f"{self._config.url}/v1/{self._resource.identifiers[-1].slug}"
194
+ if isinstance(parameters, ReadMultipleParameter):
195
+ url = base_url
196
+ elif isinstance(parameters, ReadSingleParameter):
197
+ if is_composite_identifier(parameters.identifier):
198
+ value = parameters.identifier.value
199
+ url = f"{self._config.url}/v1/organizations/{value[0]}/relations/{value[1]}/relation/{value[2]}"
200
+ elif is_id_identifier(parameters.identifier):
201
+ url = base_url + f"/{parameters.identifier.value}"
202
+ else:
203
+ url = (
204
+ base_url
205
+ + f"/{parameters.identifier.type}/{parameters.identifier.value}"
206
+ )
207
+
208
+ params = parameters.to_query_params()
209
+
210
+ response = await http_client.get(
211
+ url, params=params, headers=headers, auth=auth
212
+ )
213
+
214
+ operation_timestamp = Timestamp.completed_now(executed_at)
215
+
216
+ if response.is_error:
217
+ exc = MaleoExceptionFactory.from_httpx(
218
+ response,
219
+ operation_type=OperationType.REQUEST,
220
+ application_context=self._application_context,
221
+ operation_id=operation_id,
222
+ operation_context=operation_context,
223
+ operation_action=ReadResourceOperationAction(),
224
+ operation_timestamp=operation_timestamp,
225
+ connection_context=connection_context,
226
+ authentication=None,
227
+ authorization=authorization,
228
+ impersonation=impersonation,
229
+ logger=self._logger,
230
+ )
231
+ exc.log_and_publish_operation(self._logger, self._publishers)
232
+ raise exc
233
+
234
+ if isinstance(parameters, ReadMultipleParameter):
235
+ validated_response = MultipleDataResponse[
236
+ OrganizationRelationSchema, StrictPagination, None
237
+ ].model_validate(response.json())
238
+ service_response = ReadMultipleDataResponse[
239
+ OrganizationRelationSchema, StrictPagination, None
240
+ ].new(
241
+ data=validated_response.data,
242
+ pagination=validated_response.pagination,
243
+ )
244
+ operation = ReadMultipleResourceOperation[
245
+ OrganizationRelationSchema, StrictPagination, None
246
+ ](
247
+ application_context=self._application_context,
248
+ id=operation_id,
249
+ context=operation_context,
250
+ resource=self._resource,
251
+ timestamp=operation_timestamp,
252
+ summary=f"Successfully read multiple {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
253
+ connection_context=connection_context,
254
+ authentication=None,
255
+ authorization=authorization,
256
+ impersonation=impersonation,
257
+ response=service_response,
258
+ )
259
+ operation.log(self._logger, LogLevel.INFO)
260
+ operation.publish(self._logger, self._publishers)
261
+ elif isinstance(parameters, ReadSingleParameter):
262
+ validated_response = SingleDataResponse[
263
+ OrganizationRelationSchema, None
264
+ ].model_validate(response.json())
265
+ service_response = ReadSingleDataResponse[
266
+ OrganizationRelationSchema, None
267
+ ].new(
268
+ data=validated_response.data,
269
+ )
270
+ operation = ReadSingleResourceOperation[
271
+ OrganizationRelationSchema, None
272
+ ](
273
+ application_context=self._application_context,
274
+ id=operation_id,
275
+ context=operation_context,
276
+ resource=self._resource,
277
+ timestamp=operation_timestamp,
278
+ summary=f"Successfully read single {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
279
+ connection_context=connection_context,
280
+ authentication=None,
281
+ authorization=authorization,
282
+ impersonation=impersonation,
283
+ response=service_response,
284
+ )
285
+ operation.log(self._logger, LogLevel.INFO)
286
+ operation.publish(self._logger, self._publishers)
287
+
288
+ return service_response # type: ignore
@@ -0,0 +1,272 @@
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.patient import PATIENT_RESOURCE
37
+ from maleo.identity.mixins.patient import is_id_identifier
38
+ from maleo.identity.schemas.common import PatientSchema
39
+ from maleo.identity.schemas.patient import (
40
+ ReadMultipleParameter,
41
+ ReadSingleParameter,
42
+ )
43
+ from ..config import ClientConfig
44
+
45
+
46
+ class PatientClientService(ClientService[ClientConfig]):
47
+ _resource: ClassVar[Resource] = PATIENT_RESOURCE
48
+
49
+ @overload
50
+ async def read(
51
+ self,
52
+ cardinality: Literal[Cardinality.MULTIPLE],
53
+ *,
54
+ operation_id: UUID,
55
+ connection_context: ConnectionContext,
56
+ authorization: AnyAuthorization,
57
+ impersonation: OptImpersonation = None,
58
+ parameters: ReadMultipleParameter,
59
+ headers: OptStrToStrDict = None,
60
+ ) -> ReadMultipleDataResponse[PatientSchema, StrictPagination, None]: ...
61
+ @overload
62
+ async def read(
63
+ self,
64
+ cardinality: Literal[Cardinality.SINGLE],
65
+ *,
66
+ operation_id: UUID,
67
+ connection_context: ConnectionContext,
68
+ authorization: AnyAuthorization,
69
+ impersonation: OptImpersonation = None,
70
+ parameters: ReadSingleParameter,
71
+ headers: OptStrToStrDict = None,
72
+ ) -> ReadSingleDataResponse[PatientSchema, None]: ...
73
+ async def read(
74
+ self,
75
+ cardinality: Cardinality,
76
+ *,
77
+ operation_id: UUID,
78
+ connection_context: ConnectionContext,
79
+ authorization: AnyAuthorization,
80
+ impersonation: OptImpersonation = None,
81
+ parameters: ReadMultipleParameter | ReadSingleParameter,
82
+ headers: OptStrToStrDict = None,
83
+ ) -> (
84
+ ReadMultipleDataResponse[PatientSchema, StrictPagination, None]
85
+ | ReadSingleDataResponse[PatientSchema, None]
86
+ ):
87
+ redis_client = self._redis.manager.client.get(Connection.ASYNC)
88
+
89
+ executed_at = datetime.now(tz=timezone.utc)
90
+
91
+ # Define arguments being used in this function
92
+ positional_arguments = [cardinality]
93
+ keyword_arguments = {
94
+ "authorization": (
95
+ authorization.model_dump(mode="json")
96
+ if authorization is not None
97
+ else None
98
+ ),
99
+ "parameters": parameters.model_dump(mode="json"),
100
+ }
101
+
102
+ # Define full function string
103
+ ext = f"({json.dumps(positional_arguments)}|{json.dumps(keyword_arguments)})"
104
+
105
+ # Define full cache_key
106
+ cache_key = build_cache_key(ext, namespace=self._namespace)
107
+
108
+ if parameters.use_cache:
109
+ # Initialize cache operation context
110
+ operation_context = deepcopy(self._operation_context)
111
+ operation_context.target.type = Target.CACHE
112
+
113
+ redis_response_str = await redis_client.get(cache_key)
114
+
115
+ if redis_response_str is not None:
116
+ operation_timestamp = Timestamp.completed_now(executed_at)
117
+ if cardinality is Cardinality.MULTIPLE:
118
+ response = ReadMultipleDataResponse[
119
+ PatientSchema, StrictPagination, None
120
+ ].model_validate_json(redis_response_str)
121
+ operation = ReadMultipleResourceOperation[
122
+ PatientSchema, StrictPagination, None
123
+ ](
124
+ application_context=self._application_context,
125
+ id=operation_id,
126
+ context=operation_context,
127
+ resource=self._resource,
128
+ timestamp=operation_timestamp,
129
+ summary=f"Successfully read multiple {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
130
+ connection_context=connection_context,
131
+ authentication=None,
132
+ authorization=authorization,
133
+ impersonation=impersonation,
134
+ response=response,
135
+ )
136
+ operation.log(self._logger, LogLevel.INFO)
137
+ operation.publish(self._logger, self._publishers)
138
+ elif cardinality is Cardinality.SINGLE:
139
+ response = ReadSingleDataResponse[
140
+ PatientSchema, None
141
+ ].model_validate_json(redis_response_str)
142
+ operation = ReadSingleResourceOperation[PatientSchema, None](
143
+ application_context=self._application_context,
144
+ id=operation_id,
145
+ context=operation_context,
146
+ resource=self._resource,
147
+ timestamp=operation_timestamp,
148
+ summary=f"Successfully read single {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
149
+ connection_context=connection_context,
150
+ authentication=None,
151
+ authorization=authorization,
152
+ impersonation=impersonation,
153
+ response=response,
154
+ )
155
+ operation.log(self._logger, LogLevel.INFO)
156
+ operation.publish(self._logger, self._publishers)
157
+
158
+ return response # type: ignore
159
+
160
+ operation_context = deepcopy(self._operation_context)
161
+ operation_context.target.type = Target.MICROSERVICE
162
+
163
+ async with self._http_client_manager.get() as http_client:
164
+ base_headers = {
165
+ Header.CONTENT_TYPE.value: "application/json",
166
+ Header.X_OPERATION_ID.value: str(operation_id),
167
+ }
168
+ if impersonation is not None:
169
+ base_headers[Header.X_USER_ID.value] = str(impersonation.user_id)
170
+ if impersonation.organization_id is not None:
171
+ base_headers[Header.X_ORGANIZATION_ID.value] = str(
172
+ impersonation.organization_id
173
+ )
174
+
175
+ if headers is not None:
176
+ headers = merge_dicts(base_headers, headers)
177
+ else:
178
+ headers = base_headers
179
+
180
+ auth = AuthorizationFactory.httpx_auth(
181
+ scheme=authorization.scheme, authorization=authorization.credentials
182
+ )
183
+
184
+ base_url = f"{self._config.url}/v1/{self._resource.identifiers[-1].slug}"
185
+ if isinstance(parameters, ReadMultipleParameter):
186
+ url = base_url
187
+ elif isinstance(parameters, ReadSingleParameter):
188
+ if is_id_identifier(parameters.identifier):
189
+ url = base_url + f"/{parameters.identifier.value}"
190
+ else:
191
+ url = (
192
+ base_url
193
+ + f"/{parameters.identifier.type}/{parameters.identifier.value}"
194
+ )
195
+
196
+ params = parameters.to_query_params()
197
+
198
+ response = await http_client.get(
199
+ url, params=params, headers=headers, auth=auth
200
+ )
201
+
202
+ operation_timestamp = Timestamp.completed_now(executed_at)
203
+
204
+ if response.is_error:
205
+ exc = MaleoExceptionFactory.from_httpx(
206
+ response,
207
+ operation_type=OperationType.REQUEST,
208
+ application_context=self._application_context,
209
+ operation_id=operation_id,
210
+ operation_context=operation_context,
211
+ operation_action=ReadResourceOperationAction(),
212
+ operation_timestamp=operation_timestamp,
213
+ connection_context=connection_context,
214
+ authentication=None,
215
+ authorization=authorization,
216
+ impersonation=impersonation,
217
+ logger=self._logger,
218
+ )
219
+ exc.log_and_publish_operation(self._logger, self._publishers)
220
+ raise exc
221
+
222
+ if isinstance(parameters, ReadMultipleParameter):
223
+ validated_response = MultipleDataResponse[
224
+ PatientSchema, StrictPagination, None
225
+ ].model_validate(response.json())
226
+ service_response = ReadMultipleDataResponse[
227
+ PatientSchema, StrictPagination, None
228
+ ].new(
229
+ data=validated_response.data,
230
+ pagination=validated_response.pagination,
231
+ )
232
+ operation = ReadMultipleResourceOperation[
233
+ PatientSchema, StrictPagination, None
234
+ ](
235
+ application_context=self._application_context,
236
+ id=operation_id,
237
+ context=operation_context,
238
+ resource=self._resource,
239
+ timestamp=operation_timestamp,
240
+ summary=f"Successfully read multiple {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
241
+ connection_context=connection_context,
242
+ authentication=None,
243
+ authorization=authorization,
244
+ impersonation=impersonation,
245
+ response=service_response,
246
+ )
247
+ operation.log(self._logger, LogLevel.INFO)
248
+ operation.publish(self._logger, self._publishers)
249
+ elif isinstance(parameters, ReadSingleParameter):
250
+ validated_response = SingleDataResponse[
251
+ PatientSchema, None
252
+ ].model_validate(response.json())
253
+ service_response = ReadSingleDataResponse[PatientSchema, None].new(
254
+ data=validated_response.data,
255
+ )
256
+ operation = ReadSingleResourceOperation[PatientSchema, None](
257
+ application_context=self._application_context,
258
+ id=operation_id,
259
+ context=operation_context,
260
+ resource=self._resource,
261
+ timestamp=operation_timestamp,
262
+ summary=f"Successfully read single {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
263
+ connection_context=connection_context,
264
+ authentication=None,
265
+ authorization=authorization,
266
+ impersonation=impersonation,
267
+ response=service_response,
268
+ )
269
+ operation.log(self._logger, LogLevel.INFO)
270
+ operation.publish(self._logger, self._publishers)
271
+
272
+ return service_response # type: ignore