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.
- maleo/identity/client/__init__.py +0 -0
- maleo/identity/client/config.py +13 -0
- maleo/identity/client/manager.py +98 -0
- maleo/identity/client/services/__init__.py +0 -0
- maleo/identity/client/services/organization.py +272 -0
- maleo/identity/client/services/organization_registration_code.py +284 -0
- maleo/identity/client/services/organization_relation.py +288 -0
- maleo/identity/client/services/patient.py +272 -0
- maleo/identity/client/services/user.py +381 -0
- maleo/identity/client/services/user_medical_role.py +282 -0
- maleo/identity/client/services/user_organization.py +282 -0
- maleo/identity/client/services/user_organization_role.py +288 -0
- maleo/identity/client/services/user_profile.py +272 -0
- maleo/identity/client/services/user_system_role.py +280 -0
- maleo_identity_client-0.6.2.dist-info/METADATA +143 -0
- maleo_identity_client-0.6.2.dist-info/RECORD +19 -0
- maleo_identity_client-0.6.2.dist-info/WHEEL +5 -0
- maleo_identity_client-0.6.2.dist-info/licenses/LICENSE +57 -0
- maleo_identity_client-0.6.2.dist-info/top_level.txt +1 -0
|
@@ -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.user_profile import USER_PROFILE_RESOURCE
|
|
37
|
+
from maleo.identity.mixins.user_profile import is_id_identifier
|
|
38
|
+
from maleo.identity.schemas.common import UserProfileSchema
|
|
39
|
+
from maleo.identity.schemas.user_profile import (
|
|
40
|
+
ReadMultipleParameter,
|
|
41
|
+
ReadSingleParameter,
|
|
42
|
+
)
|
|
43
|
+
from ..config import ClientConfig
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class UserProfileClientService(ClientService[ClientConfig]):
|
|
47
|
+
_resource: ClassVar[Resource] = USER_PROFILE_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[UserProfileSchema, 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[UserProfileSchema, 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[UserProfileSchema, StrictPagination, None]
|
|
85
|
+
| ReadSingleDataResponse[UserProfileSchema, 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
|
+
UserProfileSchema, StrictPagination, None
|
|
120
|
+
].model_validate_json(redis_response_str)
|
|
121
|
+
operation = ReadMultipleResourceOperation[
|
|
122
|
+
UserProfileSchema, 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
|
+
UserProfileSchema, None
|
|
141
|
+
].model_validate_json(redis_response_str)
|
|
142
|
+
operation = ReadSingleResourceOperation[UserProfileSchema, 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
|
+
UserProfileSchema, StrictPagination, None
|
|
225
|
+
].model_validate(response.json())
|
|
226
|
+
service_response = ReadMultipleDataResponse[
|
|
227
|
+
UserProfileSchema, StrictPagination, None
|
|
228
|
+
].new(
|
|
229
|
+
data=validated_response.data,
|
|
230
|
+
pagination=validated_response.pagination,
|
|
231
|
+
)
|
|
232
|
+
operation = ReadMultipleResourceOperation[
|
|
233
|
+
UserProfileSchema, 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
|
+
UserProfileSchema, None
|
|
252
|
+
].model_validate(response.json())
|
|
253
|
+
service_response = ReadSingleDataResponse[UserProfileSchema, None].new(
|
|
254
|
+
data=validated_response.data,
|
|
255
|
+
)
|
|
256
|
+
operation = ReadSingleResourceOperation[UserProfileSchema, 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
|
|
@@ -0,0 +1,280 @@
|
|
|
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_system_role import USER_SYSTEM_ROLE_RESOURCE
|
|
37
|
+
from maleo.identity.mixins.user_system_role import (
|
|
38
|
+
is_id_identifier,
|
|
39
|
+
is_composite_identifier,
|
|
40
|
+
)
|
|
41
|
+
from maleo.identity.schemas.common import UserSystemRoleSchema
|
|
42
|
+
from maleo.identity.schemas.user_system_role import (
|
|
43
|
+
ReadMultipleParameter,
|
|
44
|
+
ReadSingleParameter,
|
|
45
|
+
)
|
|
46
|
+
from ..config import ClientConfig
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class UserSystemRoleClientService(ClientService[ClientConfig]):
|
|
50
|
+
_resource: ClassVar[Resource] = USER_SYSTEM_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[UserSystemRoleSchema, 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[UserSystemRoleSchema, 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[UserSystemRoleSchema, StrictPagination, None]
|
|
88
|
+
| ReadSingleDataResponse[UserSystemRoleSchema, 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
|
+
UserSystemRoleSchema, StrictPagination, None
|
|
123
|
+
].model_validate_json(redis_response_str)
|
|
124
|
+
operation = ReadMultipleResourceOperation[
|
|
125
|
+
UserSystemRoleSchema, 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
|
+
UserSystemRoleSchema, None
|
|
144
|
+
].model_validate_json(redis_response_str)
|
|
145
|
+
operation = ReadSingleResourceOperation[UserSystemRoleSchema, None](
|
|
146
|
+
application_context=self._application_context,
|
|
147
|
+
id=operation_id,
|
|
148
|
+
context=operation_context,
|
|
149
|
+
resource=self._resource,
|
|
150
|
+
timestamp=operation_timestamp,
|
|
151
|
+
summary=f"Successfully read single {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from cache",
|
|
152
|
+
connection_context=connection_context,
|
|
153
|
+
authentication=None,
|
|
154
|
+
authorization=authorization,
|
|
155
|
+
impersonation=impersonation,
|
|
156
|
+
response=response,
|
|
157
|
+
)
|
|
158
|
+
operation.log(self._logger, LogLevel.INFO)
|
|
159
|
+
operation.publish(self._logger, self._publishers)
|
|
160
|
+
|
|
161
|
+
return response # type: ignore
|
|
162
|
+
|
|
163
|
+
operation_context = deepcopy(self._operation_context)
|
|
164
|
+
operation_context.target.type = Target.MICROSERVICE
|
|
165
|
+
|
|
166
|
+
async with self._http_client_manager.get() as http_client:
|
|
167
|
+
base_headers = {
|
|
168
|
+
Header.CONTENT_TYPE.value: "application/json",
|
|
169
|
+
Header.X_OPERATION_ID.value: str(operation_id),
|
|
170
|
+
}
|
|
171
|
+
if impersonation is not None:
|
|
172
|
+
base_headers[Header.X_USER_ID.value] = str(impersonation.user_id)
|
|
173
|
+
if impersonation.organization_id is not None:
|
|
174
|
+
base_headers[Header.X_ORGANIZATION_ID.value] = str(
|
|
175
|
+
impersonation.organization_id
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if headers is not None:
|
|
179
|
+
headers = merge_dicts(base_headers, headers)
|
|
180
|
+
else:
|
|
181
|
+
headers = base_headers
|
|
182
|
+
|
|
183
|
+
auth = AuthorizationFactory.httpx_auth(
|
|
184
|
+
scheme=authorization.scheme, authorization=authorization.credentials
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
base_url = f"{self._config.url}/v1/{self._resource.identifiers[-1].slug}"
|
|
188
|
+
if isinstance(parameters, ReadMultipleParameter):
|
|
189
|
+
url = base_url
|
|
190
|
+
elif isinstance(parameters, ReadSingleParameter):
|
|
191
|
+
if is_composite_identifier(parameters.identifier):
|
|
192
|
+
value = parameters.identifier.value
|
|
193
|
+
url = f"{self._config.url}/v1/users/{value[0]}/system-roles/{value[1]}"
|
|
194
|
+
elif is_id_identifier(parameters.identifier):
|
|
195
|
+
url = base_url + f"/{parameters.identifier.value}"
|
|
196
|
+
else:
|
|
197
|
+
url = (
|
|
198
|
+
base_url
|
|
199
|
+
+ f"/{parameters.identifier.type}/{parameters.identifier.value}"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
params = parameters.to_query_params()
|
|
203
|
+
|
|
204
|
+
response = await http_client.get(
|
|
205
|
+
url, params=params, headers=headers, auth=auth
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
operation_timestamp = Timestamp.completed_now(executed_at)
|
|
209
|
+
|
|
210
|
+
if response.is_error:
|
|
211
|
+
exc = MaleoExceptionFactory.from_httpx(
|
|
212
|
+
response,
|
|
213
|
+
operation_type=OperationType.REQUEST,
|
|
214
|
+
application_context=self._application_context,
|
|
215
|
+
operation_id=operation_id,
|
|
216
|
+
operation_context=operation_context,
|
|
217
|
+
operation_action=ReadResourceOperationAction(),
|
|
218
|
+
operation_timestamp=operation_timestamp,
|
|
219
|
+
connection_context=connection_context,
|
|
220
|
+
authentication=None,
|
|
221
|
+
authorization=authorization,
|
|
222
|
+
impersonation=impersonation,
|
|
223
|
+
logger=self._logger,
|
|
224
|
+
)
|
|
225
|
+
exc.log_and_publish_operation(self._logger, self._publishers)
|
|
226
|
+
raise exc
|
|
227
|
+
|
|
228
|
+
if isinstance(parameters, ReadMultipleParameter):
|
|
229
|
+
validated_response = MultipleDataResponse[
|
|
230
|
+
UserSystemRoleSchema, StrictPagination, None
|
|
231
|
+
].model_validate(response.json())
|
|
232
|
+
service_response = ReadMultipleDataResponse[
|
|
233
|
+
UserSystemRoleSchema, StrictPagination, None
|
|
234
|
+
].new(
|
|
235
|
+
data=validated_response.data,
|
|
236
|
+
pagination=validated_response.pagination,
|
|
237
|
+
)
|
|
238
|
+
operation = ReadMultipleResourceOperation[
|
|
239
|
+
UserSystemRoleSchema, StrictPagination, None
|
|
240
|
+
](
|
|
241
|
+
application_context=self._application_context,
|
|
242
|
+
id=operation_id,
|
|
243
|
+
context=operation_context,
|
|
244
|
+
resource=self._resource,
|
|
245
|
+
timestamp=operation_timestamp,
|
|
246
|
+
summary=f"Successfully read multiple {self._resource.aggregate(AggregateField.NAME, sep=" ").lower()} from microservice",
|
|
247
|
+
connection_context=connection_context,
|
|
248
|
+
authentication=None,
|
|
249
|
+
authorization=authorization,
|
|
250
|
+
impersonation=impersonation,
|
|
251
|
+
response=service_response,
|
|
252
|
+
)
|
|
253
|
+
operation.log(self._logger, LogLevel.INFO)
|
|
254
|
+
operation.publish(self._logger, self._publishers)
|
|
255
|
+
elif isinstance(parameters, ReadSingleParameter):
|
|
256
|
+
validated_response = SingleDataResponse[
|
|
257
|
+
UserSystemRoleSchema, None
|
|
258
|
+
].model_validate(response.json())
|
|
259
|
+
service_response = ReadSingleDataResponse[
|
|
260
|
+
UserSystemRoleSchema, None
|
|
261
|
+
].new(
|
|
262
|
+
data=validated_response.data,
|
|
263
|
+
)
|
|
264
|
+
operation = ReadSingleResourceOperation[UserSystemRoleSchema, 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
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: maleo-identity-client
|
|
3
|
+
Version: 0.6.2
|
|
4
|
+
Summary: MaleoIdentity client package
|
|
5
|
+
Author-email: Agra Bima Yuda <agra@nexmedis.com>
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: aioredis>=2.0.1
|
|
11
|
+
Requires-Dist: annotated-doc>=0.0.4
|
|
12
|
+
Requires-Dist: annotated-types>=0.7.0
|
|
13
|
+
Requires-Dist: anyio>=4.12.0
|
|
14
|
+
Requires-Dist: async-timeout>=5.0.1
|
|
15
|
+
Requires-Dist: bcrypt>=5.0.0
|
|
16
|
+
Requires-Dist: black>=25.11.0
|
|
17
|
+
Requires-Dist: cachetools>=6.2.2
|
|
18
|
+
Requires-Dist: certifi>=2025.11.12
|
|
19
|
+
Requires-Dist: cffi>=2.0.0
|
|
20
|
+
Requires-Dist: cfgv>=3.5.0
|
|
21
|
+
Requires-Dist: charset-normalizer>=3.4.4
|
|
22
|
+
Requires-Dist: click>=8.3.1
|
|
23
|
+
Requires-Dist: colorama>=0.4.6
|
|
24
|
+
Requires-Dist: cryptography>=46.0.3
|
|
25
|
+
Requires-Dist: distlib>=0.4.0
|
|
26
|
+
Requires-Dist: dnspython>=2.8.0
|
|
27
|
+
Requires-Dist: elastic-transport>=9.2.0
|
|
28
|
+
Requires-Dist: elasticsearch>=9.2.0
|
|
29
|
+
Requires-Dist: fastapi>=0.123.7
|
|
30
|
+
Requires-Dist: filelock>=3.20.0
|
|
31
|
+
Requires-Dist: google-api-core>=2.28.1
|
|
32
|
+
Requires-Dist: google-auth>=2.43.0
|
|
33
|
+
Requires-Dist: google-cloud-appengine-logging>=1.7.0
|
|
34
|
+
Requires-Dist: google-cloud-audit-log>=0.4.0
|
|
35
|
+
Requires-Dist: google-cloud-core>=2.5.0
|
|
36
|
+
Requires-Dist: google-cloud-logging>=3.12.1
|
|
37
|
+
Requires-Dist: google-cloud-pubsub>=2.33.0
|
|
38
|
+
Requires-Dist: google-cloud-secret-manager>=2.25.0
|
|
39
|
+
Requires-Dist: google-cloud-storage>=3.6.0
|
|
40
|
+
Requires-Dist: google-crc32c>=1.7.1
|
|
41
|
+
Requires-Dist: google-resumable-media>=2.8.0
|
|
42
|
+
Requires-Dist: googleapis-common-protos>=1.72.0
|
|
43
|
+
Requires-Dist: greenlet>=3.2.4
|
|
44
|
+
Requires-Dist: grpc-google-iam-v1>=0.14.3
|
|
45
|
+
Requires-Dist: grpcio>=1.76.0
|
|
46
|
+
Requires-Dist: grpcio-status>=1.76.0
|
|
47
|
+
Requires-Dist: h11>=0.16.0
|
|
48
|
+
Requires-Dist: httpcore>=1.0.9
|
|
49
|
+
Requires-Dist: httpx>=0.28.1
|
|
50
|
+
Requires-Dist: identify>=2.6.15
|
|
51
|
+
Requires-Dist: idna>=3.11
|
|
52
|
+
Requires-Dist: importlib_metadata>=8.7.0
|
|
53
|
+
Requires-Dist: iniconfig>=2.3.0
|
|
54
|
+
Requires-Dist: maleo-enums>=0.0.85
|
|
55
|
+
Requires-Dist: maleo-identity>=0.3.2
|
|
56
|
+
Requires-Dist: maleo-metadata>=0.8.2
|
|
57
|
+
Requires-Dist: motor>=3.7.1
|
|
58
|
+
Requires-Dist: mypy_extensions>=1.1.0
|
|
59
|
+
Requires-Dist: nexo-client>=0.0.31
|
|
60
|
+
Requires-Dist: nexo-crypto>=0.0.5
|
|
61
|
+
Requires-Dist: nexo-database>=0.0.19
|
|
62
|
+
Requires-Dist: nexo-enums>=0.0.5
|
|
63
|
+
Requires-Dist: nexo-google>=0.0.31
|
|
64
|
+
Requires-Dist: nexo-infra>=0.0.10
|
|
65
|
+
Requires-Dist: nexo-logging>=0.0.5
|
|
66
|
+
Requires-Dist: nexo-middlewares>=0.0.31
|
|
67
|
+
Requires-Dist: nexo-schemas>=0.0.19
|
|
68
|
+
Requires-Dist: nexo-soma>=0.0.31
|
|
69
|
+
Requires-Dist: nexo-types>=0.0.5
|
|
70
|
+
Requires-Dist: nexo-utils>=0.0.5
|
|
71
|
+
Requires-Dist: nodeenv>=1.9.1
|
|
72
|
+
Requires-Dist: opentelemetry-api>=1.39.0
|
|
73
|
+
Requires-Dist: opentelemetry-sdk>=1.39.0
|
|
74
|
+
Requires-Dist: opentelemetry-semantic-conventions>=0.60b0
|
|
75
|
+
Requires-Dist: packaging>=25.0
|
|
76
|
+
Requires-Dist: pathspec>=0.12.1
|
|
77
|
+
Requires-Dist: platformdirs>=4.5.0
|
|
78
|
+
Requires-Dist: pluggy>=1.6.0
|
|
79
|
+
Requires-Dist: pre_commit>=4.5.0
|
|
80
|
+
Requires-Dist: proto-plus>=1.26.1
|
|
81
|
+
Requires-Dist: protobuf>=6.33.1
|
|
82
|
+
Requires-Dist: psutil>=7.1.3
|
|
83
|
+
Requires-Dist: pyasn1>=0.6.1
|
|
84
|
+
Requires-Dist: pyasn1_modules>=0.4.2
|
|
85
|
+
Requires-Dist: pycparser>=2.23
|
|
86
|
+
Requires-Dist: pycryptodome>=3.23.0
|
|
87
|
+
Requires-Dist: pydantic>=2.12.5
|
|
88
|
+
Requires-Dist: pydantic-settings>=2.12.0
|
|
89
|
+
Requires-Dist: pydantic_core>=2.41.5
|
|
90
|
+
Requires-Dist: Pygments>=2.19.2
|
|
91
|
+
Requires-Dist: PyJWT>=2.10.1
|
|
92
|
+
Requires-Dist: pymongo>=4.15.5
|
|
93
|
+
Requires-Dist: pytest>=9.0.1
|
|
94
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
95
|
+
Requires-Dist: python-dotenv>=1.2.1
|
|
96
|
+
Requires-Dist: pytokens>=0.3.0
|
|
97
|
+
Requires-Dist: PyYAML>=6.0.3
|
|
98
|
+
Requires-Dist: redis>=7.1.0
|
|
99
|
+
Requires-Dist: requests>=2.32.5
|
|
100
|
+
Requires-Dist: rsa>=4.9.1
|
|
101
|
+
Requires-Dist: six>=1.17.0
|
|
102
|
+
Requires-Dist: sniffio>=1.3.1
|
|
103
|
+
Requires-Dist: SQLAlchemy>=2.0.44
|
|
104
|
+
Requires-Dist: starlette>=0.50.0
|
|
105
|
+
Requires-Dist: typing-inspection>=0.4.2
|
|
106
|
+
Requires-Dist: typing_extensions>=4.15.0
|
|
107
|
+
Requires-Dist: ua-parser>=1.0.1
|
|
108
|
+
Requires-Dist: ua-parser-builtins>=0.18.0.post1
|
|
109
|
+
Requires-Dist: urllib3>=2.5.0
|
|
110
|
+
Requires-Dist: user-agents>=2.2.0
|
|
111
|
+
Requires-Dist: virtualenv>=20.35.4
|
|
112
|
+
Requires-Dist: zipp>=3.23.0
|
|
113
|
+
Dynamic: license-file
|
|
114
|
+
|
|
115
|
+
# README #
|
|
116
|
+
|
|
117
|
+
This README would normally document whatever steps are necessary to get your application up and running.
|
|
118
|
+
|
|
119
|
+
### What is this repository for? ###
|
|
120
|
+
|
|
121
|
+
* Quick summary
|
|
122
|
+
* Version
|
|
123
|
+
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
|
124
|
+
|
|
125
|
+
### How do I get set up? ###
|
|
126
|
+
|
|
127
|
+
* Summary of set up
|
|
128
|
+
* Configuration
|
|
129
|
+
* Dependencies
|
|
130
|
+
* Database configuration
|
|
131
|
+
* How to run tests
|
|
132
|
+
* Deployment instructions
|
|
133
|
+
|
|
134
|
+
### Contribution guidelines ###
|
|
135
|
+
|
|
136
|
+
* Writing tests
|
|
137
|
+
* Code review
|
|
138
|
+
* Other guidelines
|
|
139
|
+
|
|
140
|
+
### Who do I talk to? ###
|
|
141
|
+
|
|
142
|
+
* Repo owner or admin
|
|
143
|
+
* Other community or team contact
|