eclinical-requester 1.0.0__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.
Files changed (38) hide show
  1. api_requester/__init__.py +9 -0
  2. api_requester/core/admin/api/auth_api.py +34 -0
  3. api_requester/core/admin/api/user_on_board_application_api.py +31 -0
  4. api_requester/core/admin/service/auth_service.py +34 -0
  5. api_requester/core/admin/service/impl/system_env_service_impl.py +39 -0
  6. api_requester/core/admin/service/impl/user_on_board_application_service_impl.py +23 -0
  7. api_requester/core/admin/service/user_on_board_application_service.py +35 -0
  8. api_requester/core/call_api.py +64 -0
  9. api_requester/core/common/api/system_env_api.py +24 -0
  10. api_requester/core/common/service/system_env_service.py +27 -0
  11. api_requester/docs/application.yaml +46 -0
  12. api_requester/dto/admin/cross_user_user_on_board_dto.py +20 -0
  13. api_requester/dto/admin/jwt_authentication_request.py +20 -0
  14. api_requester/dto/admin/user_on_board_dto.py +29 -0
  15. api_requester/dto/base_dto.py +167 -0
  16. api_requester/dto/biz_base.py +31 -0
  17. api_requester/dto/user.py +39 -0
  18. api_requester/http/app_url.py +31 -0
  19. api_requester/http/authorize.py +156 -0
  20. api_requester/http/eclinical_requests.py +264 -0
  21. api_requester/http/exceptions.py +72 -0
  22. api_requester/http/gateway.py +105 -0
  23. api_requester/http/sample_headers.py +23 -0
  24. api_requester/http/token_manager.py +30 -0
  25. api_requester/utils/constant.py +108 -0
  26. api_requester/utils/json_utils.py +83 -0
  27. api_requester/utils/lib.py +456 -0
  28. api_requester/utils/log.py +91 -0
  29. api_requester/utils/path.py +21 -0
  30. api_requester/utils/placeholder_replacer.py +22 -0
  31. api_requester/utils/read_file.py +94 -0
  32. api_requester/utils/rsa.py +36 -0
  33. api_requester/utils/time_utils.py +27 -0
  34. eclinical_requester-1.0.0.dist-info/LICENSE +19 -0
  35. eclinical_requester-1.0.0.dist-info/METADATA +19 -0
  36. eclinical_requester-1.0.0.dist-info/RECORD +38 -0
  37. eclinical_requester-1.0.0.dist-info/WHEEL +5 -0
  38. eclinical_requester-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,39 @@
1
+ # !/usr/bin/python3
2
+ # -*- coding:utf-8 -*-
3
+ """
4
+ @Author: xiaodong.li
5
+ @Time: 03/18/2024 3:48 PM
6
+ @Description: Description
7
+ @File: authorize.py
8
+ """
9
+
10
+
11
+ class EClinicalUser:
12
+
13
+ def __init__(self, username=None, password=None, sponsor=None, study=None, test_env=None, app_env=None, app=None,
14
+ company=None, role=None, external=False):
15
+ # username 用户名
16
+ # password 密码
17
+ # app 访问的系统
18
+ # sponsor 访问sponsor 如果不需要,则传None
19
+ # study 访问sponsor 如果不需要,则传None
20
+ # test_env 访问的服务器环境
21
+ self.username = username
22
+ self.password = password
23
+ self.app = app
24
+ self.sponsor = sponsor
25
+ self.study = study
26
+ self.app_env = app_env
27
+ self.test_env = str(test_env)
28
+ self.company = company
29
+ self.role = role
30
+ self.external = external
31
+ self.company_level_login = False
32
+
33
+ def __repr__(self):
34
+ attributes = self.__dict__
35
+ info = list()
36
+ for item in ["test_env", "username", "role", "company", "sponsor", "study"]:
37
+ if attributes.get(item) is not None:
38
+ info.append(str(attributes.get(item)))
39
+ return "/".join(info)
@@ -0,0 +1,31 @@
1
+ # !/usr/bin/python3
2
+ # -*- coding:utf-8 -*-
3
+ """
4
+ @Author: xiaodong.li
5
+ @Time: 7/24/2023 3:48 PM
6
+ @Description: Description
7
+ @File: app_url.py
8
+ """
9
+ from api_requester.http.gateway import Gateway
10
+ from api_requester.utils.constant import AppEnum
11
+
12
+
13
+ class AppUrl(object):
14
+ def __init__(self, app, test_env):
15
+ self.app = app
16
+ self.test_env = test_env
17
+
18
+ def _app_url(self, api, app, external=False, **kwargs):
19
+ return Gateway(self.test_env).url(app, api, external).format(**kwargs)
20
+
21
+ def app_url(self, api, **kwargs):
22
+ return self._app_url(api, self.app, **kwargs)
23
+
24
+ def portal_url(self, api, **kwargs):
25
+ return self._app_url(api, AppEnum.ADMIN.code, **kwargs)
26
+
27
+ def external_url(self, api, **kwargs):
28
+ return self._app_url(api, self.app, True, **kwargs)
29
+
30
+ def which_url(self, app):
31
+ return self.portal_url if app == AppEnum.ADMIN.code else self.app_url
@@ -0,0 +1,156 @@
1
+ # !/usr/bin/python3
2
+ # -*- coding:utf-8 -*-
3
+ """
4
+ @Author: xiaodong.li
5
+ @Time: 7/24/2023 3:48 PM
6
+ @Description: Description
7
+ @File: authorize.py
8
+ """
9
+ import time
10
+
11
+ from api_requester.core.admin.service.auth_service import AdminAuthService
12
+ from api_requester.core.admin.service.impl.system_env_service_impl import CommonSystemEnvServiceImpl
13
+ from api_requester.core.admin.service.impl.user_on_board_application_service_impl import \
14
+ AdminUserOnBoardApplicationServiceImpl
15
+ from api_requester.dto.admin.cross_user_user_on_board_dto import CrossUserUserOnBoardDto
16
+ from api_requester.dto.admin.jwt_authentication_request import JwtAuthenticationRequest
17
+ from api_requester.dto.admin.user_on_board_dto import UserOnBoardDto
18
+ from api_requester.dto.biz_base import BizBase
19
+ from api_requester.dto.user import EClinicalUser
20
+ from api_requester.http.sample_headers import SampleHeaders
21
+ from api_requester.utils.constant import AppEnum, UserType
22
+ from api_requester.utils.log import Logger
23
+ from api_requester.utils.rsa import encrypt_password
24
+
25
+
26
+ class Authorize(BizBase, SampleHeaders, AdminAuthService, AdminUserOnBoardApplicationServiceImpl,
27
+ CommonSystemEnvServiceImpl):
28
+
29
+ def __init__(self, user: EClinicalUser):
30
+ BizBase.__init__(self)
31
+ self.user = user
32
+ self.login_app = self.user.app
33
+ self.user.app = AppEnum.ADMIN.code
34
+ self.time_mills = time.time()
35
+ self.user_onboard_dto = UserOnBoardDto(-1)
36
+ SampleHeaders.__init__(self)
37
+ AdminAuthService.__init__(self)
38
+ AdminUserOnBoardApplicationServiceImpl.__init__(self)
39
+ CommonSystemEnvServiceImpl.__init__(self)
40
+
41
+ def login(self):
42
+ if self.login_app == AppEnum.CODING.code:
43
+ login_app_tip = "{0}({1})".format(self.login_app,
44
+ self.user.company_level_login is True and "admin" or "study")
45
+ else:
46
+ login_app_tip = self.login_app
47
+ try:
48
+ user_type = self._auth()
49
+ if user_type == UserType.account.type_name:
50
+ return
51
+ company_id = None
52
+ if self.user.company:
53
+ company_id = self.set_work_for_company_id()
54
+ if self.login_app != AppEnum.ADMIN.code:
55
+ self._user_onboard(company_id)
56
+ self.user.app = self.login_app
57
+ self._app_auth()
58
+ if self.user.role is not None:
59
+ self.switch_role(self.user.role)
60
+ else:
61
+ self._entry_portal(company_id)
62
+ Logger().info("The user({0}) logs in to {1} successfully.".format(self.user, login_app_tip))
63
+ except Exception as e:
64
+ Logger().error("The user({0}) failed to log in to {1}.".format(self.user, login_app_tip))
65
+ raise Exception(f"Authorize Failed: {e}")
66
+
67
+ def _auth(self):
68
+ self.time_mills = time.time()
69
+ encrypt_pwd = encrypt_password(self.user.password)
70
+ jwt_authentication_request_dto = JwtAuthenticationRequest(self.user.username, encrypt_pwd)
71
+ response = self.create_authentication_token(jwt_authentication_request_dto)
72
+ if response is None:
73
+ raise Exception(f"LoginParams failed: {response}")
74
+ if response.get("jwtAuthenticationResponse") is None:
75
+ token = response.get("token")
76
+ else:
77
+ token = response.get("jwtAuthenticationResponse").get("token")
78
+ self.add_authorization(token)
79
+ self.time_mills = time.time()
80
+ return response.get("type")
81
+
82
+ def _user_onboard(self, company_id):
83
+ user_onboard_dto = UserOnBoardDto(-1)
84
+ user_onboard_dto.workForCompanyId = company_id
85
+ self._build_user_onboard_dto(user_onboard_dto)
86
+ self.user_onboard_dto = user_onboard_dto
87
+ response = self.on_board(user_onboard_dto)
88
+ self.add_authorization(response.get("token"))
89
+
90
+ def _build_user_onboard_dto(self, dto: UserOnBoardDto):
91
+ onboard_system_list = self.get_user_onboard_application_list(dto.workForCompanyId)
92
+ if not onboard_system_list:
93
+ raise Exception("Failed to obtain onboardSystemList info.")
94
+ for onboard_system in onboard_system_list:
95
+ if onboard_system.get("systemName").lower() == self.login_app:
96
+ dto.applicationId = onboard_system.get("systemId")
97
+ self._handle_onboard_envs(onboard_system, dto)
98
+ if self.user.company_level_login is True:
99
+ dto.companyLevelLogin = True
100
+ continue
101
+ onboard_sponsor_list = onboard_system.get("onboardSponsorList")
102
+ if not onboard_sponsor_list:
103
+ continue
104
+ for onboard_sponsor in onboard_sponsor_list:
105
+ if onboard_sponsor.get("name") == self.user.sponsor:
106
+ dto.sponsorId = onboard_sponsor.get("sponsorId")
107
+ self._handle_onboard_envs(onboard_sponsor, dto)
108
+ onboard_study_list = onboard_sponsor.get("onboardStudyList")
109
+ if not onboard_study_list:
110
+ continue
111
+ for onboard_study in onboard_study_list:
112
+ if onboard_study.get("studyName") == self.user.study:
113
+ dto.studyId = onboard_study.get("studyId")
114
+ self._handle_onboard_envs(onboard_study, dto)
115
+ self._assert(self.login_app, dto.applicationId, "app")
116
+ if self.login_app in [AppEnum.EDC.code, AppEnum.DESIGN.code, AppEnum.IWRS.code, AppEnum.CODING.code]:
117
+ if dto.companyLevelLogin is False:
118
+ self._assert(self.user.study, dto.studyId, "study")
119
+ else:
120
+ self._assert(self.user.company, dto.workForCompanyId, "company")
121
+ elif self.login_app != AppEnum.ADMIN.code:
122
+ self._assert(self.user.sponsor, dto.sponsorId, "sponsor")
123
+ self._assert(self.user.app_env, dto.envId, "app env")
124
+
125
+ @staticmethod
126
+ def _assert(attr, att_id, k):
127
+ assert (not attr) or (False if att_id is None else True), f"Failed to obtain {k} id. {k}::{attr}."
128
+
129
+ def _handle_onboard_envs(self, onboard_infos, dto: UserOnBoardDto):
130
+ onboard_envs = onboard_infos.get("onboardEnvs") or list()
131
+ for onboardEnv in onboard_envs:
132
+ if onboardEnv.get("name") == self.user.app_env:
133
+ dto.envId = onboardEnv.get("id")
134
+
135
+ def set_work_for_company_id(self):
136
+ company_id = self.get_company_id(name=self.user.company)
137
+ if self.user.company and not company_id:
138
+ raise Exception("The company {0} was not found.".format(self.user.company))
139
+ return company_id
140
+
141
+ def _app_auth(self):
142
+ response = self.create_authentication_token()
143
+ if response.get("jwtAuthenticationResponse") is None:
144
+ token = response.get("token")
145
+ else:
146
+ token = response.get("jwtAuthenticationResponse").get("token")
147
+ self.add_authorization(token)
148
+ self.time_mills = time.time()
149
+
150
+ def _entry_portal(self, company_id):
151
+ response = self.login_portal(CrossUserUserOnBoardDto(companyId=company_id))
152
+ try:
153
+ token = response.get("token")
154
+ self.add_authorization(token)
155
+ except BaseException as e:
156
+ print(e)
@@ -0,0 +1,264 @@
1
+ """
2
+ @Author: xiaodong.li
3
+ @Date: 2023-07-25 13:33:15
4
+ LastEditors: xiaodong.li
5
+ LastEditTime: 2023-07-25 13:33:15
6
+ @Description: eclinical_requests.py
7
+ """
8
+ import os
9
+ import time
10
+ from functools import wraps
11
+
12
+ import requests
13
+ from requests import Response
14
+ from requests_toolbelt import MultipartEncoder
15
+
16
+ from api_requester.dto.base_dto import BaseDto, build_file_dto
17
+ from api_requester.dto.biz_base import BizBase
18
+ from api_requester.http.app_url import AppUrl
19
+ from api_requester.http.exceptions import ApiResponseException
20
+
21
+
22
+ def get(api):
23
+ def __wrapper__(func):
24
+ @url(api)
25
+ @build_request_data()
26
+ @init_content_type()
27
+ @refresh_token()
28
+ @get_request()
29
+ @http_ok
30
+ def __inner__(instance: BizBase, *args, **kwargs):
31
+ return func(instance, *args, **kwargs)
32
+
33
+ return __inner__
34
+
35
+ return __wrapper__
36
+
37
+
38
+ def post(api):
39
+ def __wrapper__(func):
40
+ @url(api)
41
+ @build_request_data()
42
+ @init_content_type()
43
+ @refresh_token()
44
+ @post_request()
45
+ @http_ok
46
+ def __inner__(instance: BizBase, *args, **kwargs):
47
+ return func(instance, *args, **kwargs)
48
+
49
+ return __inner__
50
+
51
+ return __wrapper__
52
+
53
+
54
+ def delete(api):
55
+ def __wrapper__(func):
56
+ @url(api)
57
+ @build_request_data()
58
+ @init_content_type()
59
+ @refresh_token()
60
+ @delete_request()
61
+ @http_ok
62
+ def __inner__(instance: BizBase, *args, **kwargs):
63
+ return func(instance, *args, **kwargs)
64
+
65
+ return __inner__
66
+
67
+ return __wrapper__
68
+
69
+
70
+ def put(api):
71
+ def __wrapper__(func):
72
+ @url(api)
73
+ @build_request_data()
74
+ @init_content_type()
75
+ @refresh_token()
76
+ @put_request()
77
+ @http_ok
78
+ def __inner__(instance: BizBase, *args, **kwargs):
79
+ return func(instance, *args, **kwargs)
80
+
81
+ return __inner__
82
+
83
+ return __wrapper__
84
+
85
+
86
+ def get_request():
87
+ def __wrapper__(func):
88
+ def __inner__(instance: BizBase, **kwargs):
89
+ app_url = kwargs.pop("app_url")
90
+ rsp = requests.get(app_url, headers=instance.headers, **kwargs)
91
+ kwargs.update(rsp=rsp)
92
+ return func(instance, **kwargs)
93
+
94
+ return __inner__
95
+
96
+ return __wrapper__
97
+
98
+
99
+ def post_request():
100
+ def __wrapper__(func):
101
+ def __inner__(instance: BizBase, **kwargs):
102
+ app_url = kwargs.pop("app_url")
103
+ rsp = requests.post(app_url, headers=instance.headers, **kwargs)
104
+ kwargs.update(rsp=rsp)
105
+ return func(instance, **kwargs)
106
+
107
+ return __inner__
108
+
109
+ return __wrapper__
110
+
111
+
112
+ def delete_request():
113
+ def __wrapper__(func):
114
+ def __inner__(instance: BizBase, **kwargs):
115
+ app_url = kwargs.pop("app_url")
116
+ rsp = requests.delete(app_url, headers=instance.headers, **kwargs)
117
+ kwargs.update(rsp=rsp)
118
+ return func(instance, **kwargs)
119
+
120
+ return __inner__
121
+
122
+ return __wrapper__
123
+
124
+
125
+ def put_request():
126
+ def __wrapper__(func):
127
+ def __inner__(instance: BizBase, **kwargs):
128
+ app_url = kwargs.pop("app_url")
129
+ rsp = requests.put(app_url, headers=instance.headers, **kwargs)
130
+ kwargs.update(rsp=rsp)
131
+ return func(instance, **kwargs)
132
+
133
+ return __inner__
134
+
135
+ return __wrapper__
136
+
137
+
138
+ def url(api):
139
+ def __wrapper__(func):
140
+ def __inner__(instance: BizBase, *args, **kwargs):
141
+ url_kwargs = kwargs.pop("url_kwargs", {})
142
+ if instance.user.external is False:
143
+ app_url = AppUrl(instance.user.app, instance.user.test_env).which_url(
144
+ instance.user.app)(api, **url_kwargs)
145
+ else:
146
+ app_url = AppUrl(instance.user.app, instance.user.test_env).external_url(api, **url_kwargs)
147
+ if app_url is None:
148
+ raise Exception("The url is null.")
149
+ kwargs.update(dict(app_url=app_url))
150
+ return func(instance, *args, **kwargs)
151
+
152
+ return __inner__
153
+
154
+ return __wrapper__
155
+
156
+
157
+ def build_request_data():
158
+ def __wrapper__(func):
159
+ def __inner__(instance: BizBase, *args, **kwargs):
160
+ for key in ["json", "params"]:
161
+ value = kwargs.get(key)
162
+ if value is not None:
163
+ if issubclass(value.__class__, BaseDto):
164
+ kwargs[key] = value.to_dict()
165
+ elif isinstance(value, list):
166
+ kwargs[key] = [i.to_dict() if issubclass(i.__class__, BaseDto) else i for i in value]
167
+ elif isinstance(value, dict):
168
+ if key == "params":
169
+ base_dto_keys = [k for k, v in value.items() if issubclass(v.__class__, BaseDto)]
170
+ if len(base_dto_keys) > 0:
171
+ base_dto_dict = dict()
172
+ for k in base_dto_keys:
173
+ dto_dict = value.pop(k).to_dict()
174
+ if dto_dict.keys() & base_dto_dict:
175
+ raise Exception("Duplicate keys found in BaseDto dictionaries")
176
+ base_dto_dict.update(dto_dict)
177
+ if base_dto_dict.keys() & value:
178
+ raise Exception("BaseDto keys overlap with non-BaseDto keys")
179
+ value.update(base_dto_dict)
180
+ else:
181
+ kwargs[key] = {k: v.to_dict() if issubclass(v.__class__, BaseDto) else v for k, v in
182
+ value.items()}
183
+ data = kwargs.get("data")
184
+ if data is None and "multipart_encoder_kwargs" in kwargs:
185
+ multipart_encoder_kwargs = kwargs.pop("multipart_encoder_kwargs")
186
+ if multipart_encoder_kwargs is not None:
187
+ multipart_encoder_data = list()
188
+ for k, v in multipart_encoder_kwargs.items():
189
+ if issubclass(v.__class__, BaseDto):
190
+ multipart_encoder_data.extend(v.build_multipart_fields(values_as_str=True, has_none=False))
191
+ elif isinstance(v, list) and all(
192
+ isinstance(file, str) and os.path.exists(file) and os.path.isfile(file) for file in v):
193
+ multipart_encoder_data.extend([(k, build_file_dto(file)) for file in v])
194
+ elif isinstance(v, str) and os.path.exists(v) and os.path.isfile(v):
195
+ multipart_encoder_data.append((k, build_file_dto(v)))
196
+ else:
197
+ multipart_encoder_data.append((k, v))
198
+ if len(multipart_encoder_data) > 0:
199
+ data = MultipartEncoder(fields=multipart_encoder_data)
200
+ kwargs.update(dict(data=data))
201
+ return func(instance, *args, **kwargs)
202
+
203
+ return __inner__
204
+
205
+ return __wrapper__
206
+
207
+
208
+ def init_content_type():
209
+ def __wrapper__(func):
210
+ def __inner__(instance: BizBase, *args, **kwargs):
211
+ if instance.refresh_content_type is True:
212
+ instance.headers.update({"Content-Type": "application/json"})
213
+ data = kwargs.get("data")
214
+ if isinstance(data, MultipartEncoder):
215
+ instance.headers.update({"Content-Type": data.content_type})
216
+ return func(instance, *args, **kwargs)
217
+
218
+ return __inner__
219
+
220
+ return __wrapper__
221
+
222
+
223
+ def refresh_token():
224
+ def __wrapper__(func):
225
+ def __inner__(instance: BizBase, *args, **kwargs):
226
+ instance.user.external is False and (
227
+ int(time.time() - instance.time_mills) < 60 * 30 - 300 or instance.login())
228
+ return func(instance, *args, **kwargs)
229
+
230
+ return __inner__
231
+
232
+ return __wrapper__
233
+
234
+
235
+ def http_ok(func):
236
+ @wraps(func)
237
+ def _http_ok(instance: BizBase, **kwargs):
238
+ try:
239
+ rsp: Response = kwargs.pop("rsp")
240
+ instance.last_kwargs = kwargs
241
+ instance.last_result = rsp
242
+ content_type = rsp.headers.get("content-type")
243
+ if rsp.status_code not in [200, 201]:
244
+ raise ApiResponseException(rsp)
245
+ if not content_type:
246
+ raise ApiResponseException(rsp, message="The content-type in the response header is empty.")
247
+ if "application/json" in content_type:
248
+ proc_code = rsp.json().get("procCode")
249
+ if proc_code not in [200, 201]:
250
+ raise ApiResponseException(rsp)
251
+ else:
252
+ return rsp.json().get("payload")
253
+ elif any(item in content_type for item in ["application/octet-stream", "application/vnd.ms-excel"]):
254
+ return rsp.content
255
+ else:
256
+ raise ApiResponseException(rsp, message="Please handle the {0} request.".format(content_type))
257
+ except Exception as e:
258
+ if instance.raises_exception is True:
259
+ raise
260
+ else:
261
+ # Handle non-exception case here if necessary
262
+ print(f"Exception caught but not re-raised: {e}")
263
+
264
+ return _http_ok
@@ -0,0 +1,72 @@
1
+ # !/usr/bin/python3
2
+ # -*- coding:utf-8 -*-
3
+ """
4
+ @Author: xiaodong.li
5
+ @Time: 8/5/2024 4:35 PM
6
+ @Description: Description
7
+ @File: exceptions.py
8
+ """
9
+ from urllib.parse import urlparse
10
+
11
+ from requests import Response
12
+
13
+
14
+ class ApiResponseException(Exception):
15
+ def __init__(self, response: Response, message=None, exception=None, request_payload=None):
16
+ self.status_code = response.status_code
17
+ self.api = urlparse(response.url).path
18
+ self.method = response.request.method
19
+ self.message = message
20
+ self.exception = exception
21
+ self.request_payload = request_payload
22
+ self.proc_code = None
23
+ content_type = response.headers.get("content-type")
24
+ if not content_type:
25
+ ...
26
+ elif "application/json" in content_type:
27
+ self.proc_code = response.json().get("procCode")
28
+ self.message = response.json().get("message") or message
29
+ self.exception = response.json().get("exception") or exception
30
+ else:
31
+ self.message = "\n".join([message, response.text]) if message else response.text
32
+
33
+ def __str__(self):
34
+ messages = [
35
+ "\nRequest response exception:",
36
+ "Status code: {0}".format(self.status_code),
37
+ "URL: {0} {1}".format(self.method, self.api),
38
+ ]
39
+ if self.request_payload:
40
+ if isinstance(self.request_payload, dict):
41
+ payload = "\n".join(["{0}: {1}".format(k, v) for k, v in self.request_payload.items()])
42
+ else:
43
+ payload = str(self.request_payload)
44
+ messages.append("Request payload: {0}".format(payload))
45
+ if self.proc_code:
46
+ messages.append("Proc code: {0}".format(self.proc_code))
47
+ if self.message:
48
+ messages.append("Message: {0}".format(self.message))
49
+ if self.exception:
50
+ messages.append("Exception: {0}".format(self.exception))
51
+ return "\n".join(messages)
52
+
53
+
54
+ class ApiValidationError(ApiResponseException):
55
+ """自定义异常类,用于验证接口失败时引发错误"""
56
+
57
+ def __init__(self, response: Response, message=None, exception=None, request_payload=None):
58
+ super().__init__(response, message, exception, request_payload)
59
+
60
+
61
+ class DatabaseValidationError(Exception):
62
+ """自定义异常类,用于数据库验证失败时引发错误"""
63
+
64
+ def __init__(self, message):
65
+ super().__init__(message)
66
+
67
+
68
+ class FileValidationError(Exception):
69
+ """自定义异常类,用于文件验证失败时引发错误"""
70
+
71
+ def __init__(self, message):
72
+ super().__init__(message)
@@ -0,0 +1,105 @@
1
+ # !/usr/bin/python3
2
+ # -*- coding:utf-8 -*-
3
+ """
4
+ @Author: xiaodong.li
5
+ @Time: 7/24/2023 3:48 PM
6
+ @Description: Description
7
+ @File: gateway.py
8
+ """
9
+ import os.path
10
+ import re
11
+ from urllib.parse import urlparse
12
+
13
+ from requests.structures import LookupDict
14
+
15
+ from api_requester.utils.constant import AppEnum
16
+ from api_requester.utils.path import docs_path
17
+ from api_requester.utils.read_file import connect_to
18
+
19
+ _api = {
20
+ AppEnum.ADMIN.code: (AppEnum.ADMIN.code,),
21
+ AppEnum.EDC.code: (AppEnum.EDC.code,),
22
+ AppEnum.CTMS.code: (AppEnum.CTMS.code,),
23
+ AppEnum.ETMF.code: (AppEnum.ETMF.code,),
24
+ AppEnum.DESIGN.code: ('designer', AppEnum.DESIGN.code),
25
+ AppEnum.IWRS.code: (AppEnum.IWRS.code,),
26
+ "external": ('external',),
27
+ AppEnum.CODING.code: (AppEnum.CODING.code,),
28
+ AppEnum.IMAGING.code: (AppEnum.IMAGING.code,),
29
+ AppEnum.PV.code: (AppEnum.PV.code,),
30
+ }
31
+ apis = LookupDict(name='api')
32
+
33
+
34
+ def _init():
35
+ for app_api, apps in _api.items():
36
+ for app in apps:
37
+ setattr(apis, app, app_api)
38
+
39
+
40
+ _init()
41
+
42
+
43
+ class Gateway(object):
44
+ def __init__(self, test_env):
45
+ self.test_env = test_env
46
+ yaml_path = os.path.join(docs_path(), "application.yaml")
47
+ self.api_yaml = connect_to(yaml_path).data
48
+
49
+ def _url(self, app_api, api, external=False):
50
+ server_url = self._server_url(app_api)
51
+ api_url = self._ex_domain(app_api, api) or self._in_domain(app_api, api, external)
52
+ return "{0}{1}".format(server_url, api_url)
53
+
54
+ def _server_url(self, app_api):
55
+ server_url_mapping = self.api_yaml["serverUrl"]
56
+ server_url = server_url_mapping.get(self.test_env)
57
+ if server_url is not None:
58
+ return server_url
59
+ else:
60
+ postfix = dict(portal_api=".admin", ctms_api=".ctms", designer_api=".designer",
61
+ etmf_api=".etmf", edc_api=".edc", iwrs_api=".iwrs").get(app_api)
62
+ return server_url_mapping.get("{0}{1}".format(self.test_env, postfix))
63
+
64
+ def _in_domain(self, app_api, api, external=False):
65
+ domain = self.api_yaml.get(app_api).get("urlPrefix")
66
+ api_url = self._disposed(app_api, api) or self._not_disposed(api)
67
+ return api_url if (external and domain in api_url) else "{0}{1}".format(domain, api_url)
68
+
69
+ def _ex_domain(self, app_api, api):
70
+ api_url = self._disposed(app_api, api) or self._not_disposed(api)
71
+ if "local" in self.test_env:
72
+ return api_url
73
+ return None
74
+
75
+ def _disposed(self, app_api, api):
76
+ return self.api_yaml.get(app_api).get(api)
77
+
78
+ @staticmethod
79
+ def _not_disposed(api):
80
+ return api
81
+
82
+ @staticmethod
83
+ def get_app_api(app):
84
+ return eval(f"apis.{app.lower()}")
85
+
86
+ def url(self, app, api, external=False):
87
+ # if external: # s3前端迁移
88
+ # self.test_env = f"{self.test_env}.external"
89
+ tmp_url = self._url(self.get_app_api(external is False and app or "external"), api, external)
90
+ return self.rewrite_path(app, tmp_url)
91
+
92
+ def netloc(self, app):
93
+ server_url = self._server_url(self.get_app_api(app))
94
+ return urlparse(server_url).netloc
95
+
96
+ def rewrite_path(self, app, api):
97
+ app_config = self.api_yaml.get(app)
98
+ filters = app_config.get("filters")
99
+ if filters is not None:
100
+ for item in filters:
101
+ if item.startswith("RewritePath="):
102
+ item = item.replace("RewritePath=", "")
103
+ rewrite_rule, target = item.split(",")
104
+ api = re.sub(rewrite_rule, target, api)
105
+ return api
@@ -0,0 +1,23 @@
1
+ # !/usr/bin/python3
2
+ # -*- coding:utf-8 -*-
3
+ """
4
+ @Author: xiaodong.li
5
+ @Time: 7/24/2023 3:48 PM
6
+ @Description: Description
7
+ @File: sample_headers.py
8
+ """
9
+
10
+
11
+ class SampleHeaders(object):
12
+ def __init__(self):
13
+ self.headers = {"Content-Type": "application/json", "Connection": "close"}
14
+
15
+ def add_header(self, **kwargs):
16
+ self.headers.update(kwargs)
17
+ return self
18
+
19
+ def to_h(self):
20
+ return self.headers
21
+
22
+ def add_authorization(self, token):
23
+ return self.add_header(Authorization=token)